diff --git a/controllers/app.go b/controllers/app.go index b9caa57..d956f1a 100644 --- a/controllers/app.go +++ b/controllers/app.go @@ -7,6 +7,29 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func appinitConfigMap(cr *hyperfoilv1alpha1.Horreum) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name + "-app-init", + Namespace: cr.Namespace, + Labels: map[string]string{ + "app": cr.Name, + }, + }, + Data: map[string]string{ + // Note: 0.7.11 of Horreum used here still has grafana in the k8s-setup.sh script + "app_init.sh": ` + sed -i '/grafana/d' /deployments/k8s-setup.sh + until sh -x -c /deployments/k8s-setup.sh + do + echo 'Re-trying init ...' + sleep 10 + done + `, + }, + } +} + func appPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl, appPublicUrl string) *corev1.Pod { keycloakInternalURL := keycloakInternalURL(cr) @@ -58,6 +81,17 @@ func appPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl, appPublicUrl strin }) } volumes := []corev1.Volume{ + { + Name: "app-init", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cr.Name + "-app-init", + }, + DefaultMode: func(i int32) *int32 { return &i }(0555), + }, + }, + }, { Name: "imports", VolumeSource: corev1.VolumeSource{ @@ -132,7 +166,7 @@ func appPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl, appPublicUrl strin Image: appImage(cr), ImagePullPolicy: corev1.PullAlways, Command: []string{ - "sh", "-x", "-c", "/deployments/k8s-setup.sh", + "sh", "-x", "-c", "/deployments/app_init.sh;", }, Env: []corev1.EnvVar{ secretEnv("KEYCLOAK_USER", keycloakAdminSecret(cr), corev1.BasicAuthUsernameKey), @@ -153,6 +187,11 @@ func appPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl, appPublicUrl strin }, }, VolumeMounts: []corev1.VolumeMount{ + { + Name: "app-init", + MountPath: "/deployments/app_init.sh", + SubPath: "app_init.sh", + }, { Name: "imports", MountPath: "/etc/horreum/imports", diff --git a/controllers/horreum_controller.go b/controllers/horreum_controller.go index cf1b859..ec272e4 100644 --- a/controllers/horreum_controller.go +++ b/controllers/horreum_controller.go @@ -225,6 +225,10 @@ func (r *HorreumReconciler) Reconcile(ctx context.Context, request ctrl.Request) } cr.Status.KeycloakUrl = keycloakPublicUrl + keycloakConfigMap := keycloakConfigMap(cr) + if err := ensureSame(r, cr, logger, keycloakConfigMap, &corev1.ConfigMap{}, compareConfigMap, nocheck); err != nil { + return reconcile.Result{}, err + } keycloakPod := keycloakPod(cr, keycloakPublicUrl) if cr.Spec.Keycloak.External.PublicUri != "" { if err := ensureDeleted(r, cr, keycloakPod, &corev1.Pod{}); err != nil { @@ -238,10 +242,16 @@ func (r *HorreumReconciler) Reconcile(ctx context.Context, request ctrl.Request) return reconcile.Result{}, err } } - } else if err := ensureSame(r, cr, logger, keycloakPod, &corev1.Pod{}, comparePods, checkPod); err != nil { - return reconcile.Result{}, err + } else { + if err := ensureSame(r, cr, logger, keycloakPod, &corev1.Pod{}, comparePods, checkPod); err != nil { + return reconcile.Result{}, err + } } + appinitConfigMap := appinitConfigMap(cr) + if err := ensureSame(r, cr, logger, appinitConfigMap, &corev1.ConfigMap{}, compareConfigMap, nocheck); err != nil { + return reconcile.Result{}, err + } appService := appService(cr, r) appRoute, err := appRoute(cr, r) if err != nil { diff --git a/controllers/keycloak.go b/controllers/keycloak.go index c7b94d0..f7a542d 100644 --- a/controllers/keycloak.go +++ b/controllers/keycloak.go @@ -2,6 +2,8 @@ package horreum import ( "errors" + "io" + "net/http" "net/url" hyperfoilv1alpha1 "github.com/Hyperfoil/horreum-operator/api/v1alpha1" @@ -11,6 +13,38 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +func keycloakConfigMap(cr *hyperfoilv1alpha1.Horreum) *corev1.ConfigMap { + req, err := http.NewRequest("GET", "https://raw.githubusercontent.com/Hyperfoil/Horreum/0.7.11/infra/keycloak-horreum.json", nil) + if err != nil { + return nil + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil + } + + defer resp.Body.Close() + + horreumRealm, err := io.ReadAll(resp.Body) + if err != nil { + return nil + } + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name + "-keycloak-horreum", + Namespace: cr.Namespace, + Labels: map[string]string{ + "app": cr.Name, + }, + }, + Data: map[string]string{ + "keycloak-horreum.json": string(horreumRealm), + }, + } +} + func keycloakPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl string) *corev1.Pod { secretName := cr.Name + "-keycloak-certs" if cr.Spec.Keycloak.Route.Type == "passthrough" { @@ -30,12 +64,26 @@ func keycloakPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl string) *corev }, }, }, + { + Name: "keycloak-horreum", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cr.Name + "-keycloak-horreum", + }, + }, + }, + }, } volumeMounts := []corev1.VolumeMount{ { Name: "certs", MountPath: "/etc/x509/https", }, + { + Name: "keycloak-horreum", + MountPath: "/opt/keycloak/data/import", + }, } return &corev1.Pod{ @@ -48,10 +96,15 @@ func keycloakPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl string) *corev }, }, Spec: corev1.PodSpec{ - Containers: []corev1.Container{ + TerminationGracePeriodSeconds: &[]int64{0}[0], + InitContainers: []corev1.Container{ { - Name: "keycloak", - Image: withDefault(cr.Spec.Keycloak.Image, "quay.io/hyperfoil/horreum-keycloak:latest"), + Name: "init", + Image: withDefault(cr.Spec.Keycloak.Image, "quay.io/keycloak/keycloak:22.0"), + ImagePullPolicy: corev1.PullAlways, + Args: []string{ + "build", + }, Env: []corev1.EnvVar{ secretEnv("KEYCLOAK_ADMIN", keycloakAdminSecret(cr), corev1.BasicAuthUsernameKey), secretEnv("KEYCLOAK_ADMIN_PASSWORD", keycloakAdminSecret(cr), corev1.BasicAuthPasswordKey), @@ -94,10 +147,59 @@ func keycloakPod(cr *hyperfoilv1alpha1.Horreum, keycloakPublicUrl string) *corev }, secretEnv("KC_DB_USERNAME", keycloakDbSecret(cr), corev1.BasicAuthUsernameKey), secretEnv("KC_DB_PASSWORD", keycloakDbSecret(cr), corev1.BasicAuthPasswordKey), + }, + VolumeMounts: volumeMounts, + }, + }, + Containers: []corev1.Container{ + { + Name: "keycloak", + Image: withDefault(cr.Spec.Keycloak.Image, "quay.io/keycloak/keycloak:latest"), + Args: []string{ + "start", "--optimized", "--import-realm", + }, + Env: []corev1.EnvVar{ + secretEnv("KEYCLOAK_ADMIN", keycloakAdminSecret(cr), corev1.BasicAuthUsernameKey), + secretEnv("KEYCLOAK_ADMIN_PASSWORD", keycloakAdminSecret(cr), corev1.BasicAuthPasswordKey), + { + Name: "DB_ADDR", + Value: withDefault(cr.Spec.Keycloak.Database.Host, dbDefaultHost(cr)), + }, + { + Name: "DB_PORT", + Value: withDefaultInt(cr.Spec.Keycloak.Database.Port, 5432), + }, + { + Name: "DB_DATABASE", + Value: withDefault(cr.Spec.Keycloak.Database.Name, "keycloak"), + }, + // For simplicity of development the image has HTTP enabled, which is not suitable for production + { + Name: "KC_HTTP_ENABLED", + Value: "false", + }, { - Name: "KEYCLOAK_COMMAND", - Value: "start", + Name: "KC_HTTPS_PORT", + Value: "8443", + }, + { + Name: "KC_HTTPS_CERTIFICATE_FILE", + Value: "/etc/x509/https/tls.crt", + }, + { + Name: "KC_HTTPS_CERTIFICATE_KEY_FILE", + Value: "/etc/x509/https/tls.key", }, + { + Name: "KC_HOSTNAME", + Value: publicUrl.Host, + }, + { + Name: "KC_PROXY", + Value: "passthrough", // TODO at least for NodePort? + }, + secretEnv("KC_DB_USERNAME", keycloakDbSecret(cr), corev1.BasicAuthUsernameKey), + secretEnv("KC_DB_PASSWORD", keycloakDbSecret(cr), corev1.BasicAuthPasswordKey), }, Ports: []corev1.ContainerPort{ { diff --git a/go.mod b/go.mod index d87cbb3..fd7e7cb 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/go-openapi/swag v0.19.14 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -45,7 +44,6 @@ require ( github.com/imdario/mergo v0.3.12 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/llparse/controller-gen v0.0.0-20180131011002-7a38c4658cb4 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -62,14 +60,12 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.21.0 // indirect golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.6.0 // indirect golang.org/x/term v0.6.0 // indirect golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.7.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect @@ -78,9 +74,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/code-generator v0.25.0 // indirect k8s.io/component-base v0.25.0 // indirect - k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect k8s.io/klog/v2 v2.70.1 // indirect k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect diff --git a/go.sum b/go.sum index 1bae09d..62a4a86 100644 --- a/go.sum +++ b/go.sum @@ -163,7 +163,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -282,8 +281,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/llparse/controller-gen v0.0.0-20180131011002-7a38c4658cb4 h1:FrJSY29TdNaLpfbneg37XwwmbPfJJsqkqmlBNVrsZ/0= -github.com/llparse/controller-gen v0.0.0-20180131011002-7a38c4658cb4/go.mod h1:MNU/rtrt2+D702OLCXO7CdoiLROp61B1U7JFmmgzIkM= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= @@ -444,8 +441,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -660,7 +655,6 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -845,14 +839,10 @@ k8s.io/apimachinery v0.25.1/go.mod h1:hqqA1X0bsgsxI6dXsJ4HnNTBOmJNxyPp8dw3u2fSHw k8s.io/client-go v0.25.1 h1:uFj4AJKtE1/ckcSKz8IhgAuZTdRXZDKev8g387ndD58= k8s.io/client-go v0.25.1/go.mod h1:rdFWTLV/uj2C74zGbQzOsmXPUtMAjSf7ajil4iJUNKo= k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= -k8s.io/code-generator v0.25.0 h1:QP8fJuXu882ztf6dsqJsso/Btm94pMd68TAZC1rE6KI= -k8s.io/code-generator v0.25.0/go.mod h1:B6jZgI3DvDFAualltPitbYMQ74NjaCFxum3YeKZZ+3w= k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI= -k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=