Skip to content
Snippets Groups Projects
deploy.go 7.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package main
    
    import (
    	"code.fbi.h-da.de/cocsn/gosdn/nucleus"
    	"context"
    	appv1 "k8s.io/api/apps/v1"
    	corev1 "k8s.io/api/core/v1"
    	"k8s.io/apimachinery/pkg/api/errors"
    	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    	"k8s.io/apimachinery/pkg/util/intstr"
    	"k8s.io/client-go/kubernetes"
    	"k8s.io/client-go/tools/clientcmd"
    	"k8s.io/client-go/util/homedir"
    	"log"
    	"os"
    	"path/filepath"
    )
    
    func main() {
    	kubeconfig, err := clientcmd.BuildConfigFromFlags("", filepath.Join(homedir.HomeDir(), ".kube", "config"))
    	if err != nil {
    		log.Fatal(err)
    	}
    	clientset, err := kubernetes.NewForConfig(kubeconfig)
    	if err != nil {
    		log.Fatal(err)
    	}
    	var tag string
    	switch os.Getenv("CI_COMMIT_BRANCH") {
    	case "master":
    		tag = "latest"
    	case "develop":
    		tag = "develop"
    	default:
    		tag = os.Getenv("CI_COMMIT_SHA")
    	}
    	env := "gosdn-" + tag
    	service := createService(env)
    	config := createConfigMap(env)
    	deployment := createDeployment(env, tag)
    	switch os.Getenv("K8S_OP") {
    	case "create":
    		if err := create(clientset, service, config, deployment, env); err != nil {
    			log.Println(err)
    		}
    	case "delete":
    		if err := remove(clientset, env); err != nil {
    			log.Println(err)
    		}
    	case "update":
    		if err := update(clientset, service, env); err != nil {
    			log.Println(err)
    		}
    		if err := update(clientset, config, env); err != nil {
    			log.Println(err)
    		}
    		if err := update(clientset, deployment, env); err != nil {
    			log.Println(err)
    		}
    	default:
    		log.Fatal("invalid option")
    	}
    }
    
    func create(clientset *kubernetes.Clientset, service *corev1.Service, config *corev1.ConfigMap, deployment *appv1.Deployment, env string) error {
    	opts := metav1.CreateOptions{}
    	ctx := context.Background()
    	_, err := clientset.CoreV1().Services("cocsn").Create(ctx, service, opts)
    	if err != nil {
    		switch err.(type) {
    		case *errors.StatusError:
    			if err.(*errors.StatusError).ErrStatus.Code == 409 {
    				err = update(clientset, service, env)
    			}
    		default:
    			return err
    		}
    	}
    	_, err = clientset.CoreV1().ConfigMaps("cocsn").Create(ctx, config, opts)
    	if err != nil {
    		switch err.(type) {
    		case *errors.StatusError:
    			if err.(*errors.StatusError).ErrStatus.Code == 409 {
    				err = update(clientset, config, env)
    			}
    		default:
    			return err
    		}
    	}
    	_, err = clientset.AppsV1().Deployments("cocsn").Create(ctx, deployment, opts)
    	if err != nil {
    		switch err.(type) {
    		case *errors.StatusError:
    			if err.(*errors.StatusError).ErrStatus.Code == 409 {
    				err = update(clientset, deployment, env)
    			}
    		default:
    			return err
    		}
    	}
    	return err
    }
    
    func update(clientset *kubernetes.Clientset, resource metav1.Common, env string) error {
    	opts := metav1.UpdateOptions{}
    	getOpts := metav1.GetOptions{}
    	ctx := context.Background()
    	switch resource.(type) {
    	case *corev1.Service:
    		service := resource.(*corev1.Service)
    		s, err := clientset.CoreV1().Services("cocsn").Get(ctx, env, getOpts)
    		if err != nil {
    			return err
    		}
    		s.DeepCopyInto(service)
    		_, err = clientset.CoreV1().Services("cocsn").Update(ctx, service, opts)
    		if err != nil {
    			return err
    		}
    	case *corev1.ConfigMap:
    		config := resource.(*corev1.ConfigMap)
    		c, err := clientset.CoreV1().ConfigMaps("cocsn").Get(ctx, env+"-config", getOpts)
    		if err != nil {
    			return err
    		}
    		c.DeepCopyInto(config)
    		_, err = clientset.CoreV1().ConfigMaps("cocsn").Update(ctx, config, opts)
    		if err != nil {
    			return err
    		}
    	case *appv1.Deployment:
    		deployment := resource.(*appv1.Deployment)
    		d, err := clientset.AppsV1().Deployments("cocsn").Get(ctx, env, getOpts)
    		if err != nil {
    			return err
    		}
    		d.DeepCopyInto(deployment)
    		_, err = clientset.AppsV1().Deployments("cocsn").Update(ctx, deployment, opts)
    		if err != nil {
    			return err
    		}
    	default:
    		return &nucleus.ErrInvalidParameters{}
    	}
    	return nil
    }
    
    func remove(clientset *kubernetes.Clientset, env string) error {
    	opts := metav1.DeleteOptions{}
    	ctx := context.Background()
    	err := clientset.CoreV1().Services("cocsn").Delete(ctx, env, opts)
    	if err != nil {
    		log.Println(err)
    	}
    	err = clientset.CoreV1().ConfigMaps("cocsn").Delete(ctx, env+"-config", opts)
    	if err != nil {
    		log.Println(err)
    	}
    	err = clientset.AppsV1().Deployments("cocsn").Delete(ctx, env, opts)
    	if err != nil {
    		log.Println(err)
    	}
    	return err
    }
    
    func createService(environment string) *corev1.Service {
    	return &corev1.Service{
    		TypeMeta: metav1.TypeMeta{
    			Kind:       "Service",
    			APIVersion: "v1",
    		},
    		ObjectMeta: metav1.ObjectMeta{
    			Name:      environment,
    			Namespace: "cocsn",
    			Labels:    map[string]string{"run": environment},
    		},
    		Spec: corev1.ServiceSpec{
    			Ports: []corev1.ServicePort{
    				{
    					Name:       "http",
    					Port:       8080,
    					TargetPort: intstr.IntOrString{IntVal: 8080},
    				},
    				{
    					Name:       "grpc",
    					Port:       55055,
    					TargetPort: intstr.IntOrString{IntVal: 55055},
    				},
    			},
    			Selector:     map[string]string{"run": environment},
    			Type:         "NodePort",
    			ExternalName: environment + ".apps.ocp.fbi.h-da.de",
    		},
    	}
    }
    
    func createDeployment(environment, hash string) *appv1.Deployment {
    	return &appv1.Deployment{
    		TypeMeta: metav1.TypeMeta{
    			Kind:       "Deployment",
    			APIVersion: "apps/v1",
    		},
    		ObjectMeta: metav1.ObjectMeta{
    			Name: environment,
    		},
    		Spec: appv1.DeploymentSpec{
    			Selector: &metav1.LabelSelector{
    				MatchLabels: map[string]string{"run": environment},
    			},
    			Template: corev1.PodTemplateSpec{
    				ObjectMeta: metav1.ObjectMeta{
    					Labels: map[string]string{"run": environment},
    				},
    				Spec: corev1.PodSpec{
    					Volumes: []corev1.Volume{
    						{
    							Name: "gosdn-config-volume",
    							VolumeSource: corev1.VolumeSource{
    								ConfigMap: &corev1.ConfigMapVolumeSource{
    									LocalObjectReference: corev1.LocalObjectReference{
    										Name: "gosdn-develop-config",
    									},
    								},
    							},
    						},
    					},
    					Containers: []corev1.Container{
    						{
    							Name:       "gosdn",
    							Image:      "registry.code.fbi.h-da.de/cocsn/gosdn:" + hash,
    							Command:    nil,
    							Args:       nil,
    							WorkingDir: "",
    							Ports: []corev1.ContainerPort{
    								{
    									Name:          "grpc",
    									ContainerPort: 55055,
    								},
    								{
    									Name:          "http",
    									ContainerPort: 8080,
    								},
    							},
    							VolumeMounts: []corev1.VolumeMount{
    								{
    									Name:      "gosdn-config-volume",
    									MountPath: "/usr/local/etc/gosdn/gosdn.toml",
    								},
    							},
    							LivenessProbe: &corev1.Probe{
    								Handler: corev1.Handler{
    									HTTPGet: &corev1.HTTPGetAction{
    										Path: "/livez",
    										Port: intstr.IntOrString{IntVal: 8080},
    									},
    								},
    								InitialDelaySeconds: 20,
    								PeriodSeconds:       2,
    							},
    							ReadinessProbe: &corev1.Probe{
    								Handler: corev1.Handler{
    									HTTPGet: &corev1.HTTPGetAction{
    										Path: "/readyz",
    										Port: intstr.IntOrString{IntVal: 8080},
    									},
    								},
    								InitialDelaySeconds: 120,
    								PeriodSeconds:       2,
    							},
    							ImagePullPolicy: "Always",
    						},
    					},
    					ImagePullSecrets: []corev1.LocalObjectReference{
    						{Name: "k8s-gosdn-test"},
    					},
    				},
    			},
    			Strategy: appv1.DeploymentStrategy{
    				Type:          "RollingUpdate",
    				RollingUpdate: &appv1.RollingUpdateDeployment{},
    			},
    		},
    	}
    }
    
    func createConfigMap(env string) *corev1.ConfigMap {
    	return &corev1.ConfigMap{
    		TypeMeta: metav1.TypeMeta{
    			Kind:       "ConfigMap",
    			APIVersion: "v1",
    		},
    		ObjectMeta: metav1.ObjectMeta{
    			Name: env + "-config",
    		},
    		Data: map[string]string{"gosdn.toml": "#empty"},
    	}
    }