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"}, } }