Skip to content
Snippets Groups Projects
deploy.go 9.72 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package main
    
    import (
    	"context"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"os"
    
    
    	nucleus "code.fbi.h-da.de/cocsn/gosdn/nucleus/errors"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	log "github.com/sirupsen/logrus"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	appv1 "k8s.io/api/apps/v1"
    	corev1 "k8s.io/api/core/v1"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	netv1 "k8s.io/api/networking/v1beta1"
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	"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"
    )
    
    func main() {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	log.SetFormatter(&log.JSONFormatter{})
    
    	kubeconfig, err := clientcmd.BuildConfigFromFlags("https://api.ocp.fbi.h-da.de:6443", "")
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	kubeconfig.BearerToken = os.Getenv("K8S_DEPLOY_TOKEN")
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	clientset, err := kubernetes.NewForConfig(kubeconfig)
    	if err != nil {
    		log.Fatal(err)
    	}
    	var tag string
    	switch os.Getenv("CI_COMMIT_BRANCH") {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	case os.Getenv("CI_DEFAULT_BRANCH"):
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		tag = "latest"
    	case "develop":
    		tag = "develop"
    	default:
    		tag = os.Getenv("CI_COMMIT_SHA")
    	}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	switch os.Getenv("CI_NIGHTLY") {
    	case "mainline":
    		tag = "nightly"
    	case "develop":
    		tag = "nightly-develop"
    	default:
    		tag = os.Getenv("CI_COMMIT_SHA")
    	}
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	switch os.Getenv("K8S_OP") {
    	case "create":
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		if err := create(clientset, tag); err != nil {
    			log.Fatal(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    	case "delete":
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		if err := remove(clientset, tag); err != nil {
    			log.Fatal(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    	default:
    		log.Fatal("invalid option")
    	}
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    // nolint
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func create(clientset *kubernetes.Clientset, tag string) error {
    	env := "gosdn-" + tag
    	service := createService(env)
    	ingress := createIngress(env)
    	config := createConfigMap(env)
    	deployment := createDeployment(env, tag)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	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 {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				if err := update(clientset, service, env); err != nil {
    					return err
    				}
    			} else {
    				log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			}
    		default:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	} else {
    		log.Printf("service %v created", service.Name)
    	}
    	_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Create(ctx, ingress, opts)
    	if err != nil {
    		switch err.(type) {
    		case *errors.StatusError:
    			if err.(*errors.StatusError).ErrStatus.Code == 409 {
    				if err := update(clientset, ingress, env); err != nil {
    					log.Error(err)
    				}
    			} else {
    				log.Error(err)
    			}
    		default:
    			log.Error(err)
    		}
    	} else {
    		log.Printf("ingress %v created", ingress.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    	_, 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 {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				if err := update(clientset, config, env); err != nil {
    					log.Error(err)
    				}
    			} else {
    				log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			}
    		default:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	} else {
    		log.Printf("configMap %v created", config.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    	_, 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 {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    				if err := update(clientset, deployment, env); err != nil {
    					log.Error(err)
    				}
    			} else {
    				log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			}
    		default:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    			log.Error(err)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	} else {
    		log.Printf("deployment %v created", deployment.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	return nil
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    }
    
    func update(clientset *kubernetes.Clientset, resource metav1.Common, env string) error {
    	opts := metav1.UpdateOptions{}
    	getOpts := metav1.GetOptions{}
    	ctx := context.Background()
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	switch resource := resource.(type) {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	case *corev1.Service:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		service := resource
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		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
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Printf("service %v updated", service.Name)
    	case *netv1.Ingress:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		ingress := resource
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		i, err := clientset.NetworkingV1beta1().Ingresses("cocsn").Get(ctx, env, getOpts)
    		if err != nil {
    			return err
    		}
    		i.DeepCopyInto(ingress)
    		_, err = clientset.NetworkingV1beta1().Ingresses("cocsn").Update(ctx, ingress, opts)
    		if err != nil {
    			return err
    		}
    		log.Printf("ingress %v updated", ingress.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	case *corev1.ConfigMap:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		config := resource
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		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
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Printf("configMap %v updated", config.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	case *appv1.Deployment:
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		deployment := resource
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		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
    		}
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Printf("deployment %v updated", deployment.Name)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	default:
    		return &nucleus.ErrInvalidParameters{}
    	}
    	return nil
    }
    
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    func remove(clientset *kubernetes.Clientset, tag string) error {
    	env := "gosdn-" + tag
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	opts := metav1.DeleteOptions{}
    	ctx := context.Background()
    	err := clientset.CoreV1().Services("cocsn").Delete(ctx, env, opts)
    	if err != nil {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Error(err)
    	} else {
    		log.Printf("service %v deleted", env)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    	err = clientset.CoreV1().ConfigMaps("cocsn").Delete(ctx, env+"-config", opts)
    	if err != nil {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Error(err)
    	} else {
    		log.Printf("config %v deleted", env+"-config")
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    	err = clientset.AppsV1().Deployments("cocsn").Delete(ctx, env, opts)
    	if err != nil {
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    		log.Error(err)
    	} else {
    		log.Printf("deployment %v deleted", env)
    	}
    	err = clientset.NetworkingV1beta1().Ingresses("cocsn").Delete(ctx, env, opts)
    	if err != nil {
    		log.Error(err)
    	} else {
    		log.Printf("ingress %v deleted", env)
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    	}
    	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{
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    										Name: environment + "-config",
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    									},
    								},
    							},
    						},
    					},
    					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: 5,
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    								PeriodSeconds:       2,
    							},
    							ReadinessProbe: &corev1.Probe{
    								Handler: corev1.Handler{
    									HTTPGet: &corev1.HTTPGetAction{
    										Path: "/readyz",
    										Port: intstr.IntOrString{IntVal: 8080},
    									},
    								},
    
    								InitialDelaySeconds: 10,
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    								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"},
    	}
    }
    
    Manuel Kieweg's avatar
    Manuel Kieweg committed
    
    func createIngress(env string) *netv1.Ingress {
    	return &netv1.Ingress{
    		TypeMeta: metav1.TypeMeta{
    			Kind:       "Ingress",
    			APIVersion: "v1",
    		},
    		ObjectMeta: metav1.ObjectMeta{
    			Name:      env,
    			Namespace: "cocsn",
    		},
    		Spec: netv1.IngressSpec{
    			Rules: []netv1.IngressRule{
    				{
    					Host: env + ".apps.ocp.fbi.h-da.de",
    					IngressRuleValue: netv1.IngressRuleValue{
    						HTTP: &netv1.HTTPIngressRuleValue{
    							Paths: []netv1.HTTPIngressPath{
    								{
    									Path: "/api",
    									Backend: netv1.IngressBackend{
    										ServiceName: env,
    										ServicePort: intstr.IntOrString{IntVal: 8080},
    									},
    								},
    							},
    						},
    					},
    				},
    			},
    		},
    		Status: netv1.IngressStatus{},
    	}
    }