diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index 65edaf8..a2c4359 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -68,6 +68,10 @@ spec: - name: HELM_REGISTRY_CREDENTIALS_PATH value: {{ .Values.helm.credentials.mountPath | quote }} {{- end }} + {{- with .Values.helm.user }} + - name: HELM_USER + value: {{ . | quote }} + {{- end }} {{- with .Values.controller.concurrency }} - name: CONCURRENCY value: {{ . | quote }} diff --git a/chart/tests/helm/expected.yaml b/chart/tests/helm/expected.yaml new file mode 100644 index 0000000..0284774 --- /dev/null +++ b/chart/tests/helm/expected.yaml @@ -0,0 +1,577 @@ +--- +# Source: joy-operator-chart/templates/crds.yaml +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + helm.sh/resource-policy: keep + name: releases.joy.nesto.ca +spec: + group: joy.nesto.ca + names: + kind: Release + listKind: ReleaseList + plural: releases + shortNames: + - rel + singular: release + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + chart: + properties: + mappings: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + ref: + type: string + repoUrl: + type: string + version: + type: string + type: object + links: + additionalProperties: + type: string + type: object + namespace: + type: string + project: + type: string + values: + type: object + x-kubernetes-preserve-unknown-fields: true + version: + type: string + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + helm.sh/resource-policy: keep + name: environments.joy.nesto.ca +spec: + group: joy.nesto.ca + names: + kind: Environment + listKind: EnvironmentList + plural: environments + shortNames: + - env + singular: environment + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + chartVersions: + additionalProperties: + type: string + type: object + cluster: + type: string + namespace: + type: string + order: + type: integer + organization: + type: string + owners: + items: + type: string + type: array + promotion: + properties: + allowAutoMerge: + type: boolean + fromEnvironments: + items: + type: string + type: array + fromPullRequests: + type: boolean + type: object + sealedSecretsCert: + type: string + values: + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + helm.sh/resource-policy: keep + name: projects.joy.nesto.ca +spec: + group: joy.nesto.ca + names: + kind: Project + listKind: ProjectList + plural: projects + shortNames: + - proj + singular: project + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + gitTagTemplate: + type: string + links: + additionalProperties: + type: string + type: object + owners: + items: + type: string + type: array + releaseLinks: + additionalProperties: + type: string + type: object + repository: + type: string + repositorySubpaths: + items: + type: string + type: array + reviewers: + items: + type: string + type: array + skipPreReleaseCheck: + type: boolean + type: object + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + helm.sh/resource-policy: keep + name: catalogs.joy.nesto.ca +spec: + group: joy.nesto.ca + names: + kind: Catalog + listKind: CatalogList + plural: catalogs + shortNames: + - cat + singular: catalog + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + spec: + properties: + charts: + properties: + default: + type: string + refs: + additionalProperties: + properties: + mappings: + type: object + x-kubernetes-preserve-unknown-fields: true + name: + type: string + repoUrl: + type: string + version: + type: string + required: + - repoUrl + - name + - version + type: object + type: object + required: + - default + - refs + type: object + repoUrl: + type: string + revision: + type: string + required: + - repoUrl + - revision + type: object + required: + - spec + type: object + served: true + storage: true +--- +# Source: joy-operator-chart/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: null + name: my-release +spec: + replicas: 1 + selector: + matchLabels: + release: my-release + strategy: + type: Recreate + template: + metadata: + labels: + release: my-release + spec: + containers: + - env: + - name: SERVICE_NAME + value: my-release + - name: CHART_CACHE_DIR + value: /helm/cache + - name: ENV_DESTINATIONS + value: | + {} + - name: ENV_SOURCE_PATTERN + value: environments/*/env.yaml + - name: HELM_REGISTRY + value: ghcr.io/testy-test + - name: HELM_REGISTRY_CREDENTIALS_PATH + value: /secrets/helm.creds + - name: HELM_USER + value: ouath2accesstoken + image: ghcr.io/nestoca/joy-operator:latest + imagePullPolicy: IfNotPresent + name: main + ports: + - containerPort: 3000 + name: health + protocol: TCP + readinessProbe: + failureThreshold: 6 + httpGet: + path: /ready + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + resources: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsGroup: 65532 + runAsNonRoot: true + runAsUser: 65532 + volumeMounts: + - mountPath: /helm/cache + name: helm-cache + - mountPath: /.config/helm + name: helm-config + - mountPath: /tmp + name: tmp + - mountPath: /secrets/helm.creds + name: helm-credentials + readOnly: true + subPath: creds + securityContext: + fsGroup: 65532 + serviceAccountName: my-release + volumes: + - emptyDir: {} + name: helm-cache + - emptyDir: {} + name: helm-config + - emptyDir: {} + name: tmp + - name: helm-credentials + secret: + items: + - key: creds + path: creds + secretName: helm-creds +--- +# Source: joy-operator-chart/templates/rbac.yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: null + name: my-release-environments +rules: + - apiGroups: + - joy.nesto.ca + resources: + - environments + verbs: + - get + - list + - watch + - update + - patch + - delete + - apiGroups: + - joy.nesto.ca + resources: + - environments/status + - environments/finalizers + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: null + name: my-release-projects +rules: + - apiGroups: + - joy.nesto.ca + resources: + - projects + verbs: + - get + - list + - watch + - update + - patch + - delete + - apiGroups: + - joy.nesto.ca + resources: + - projects/status + - projects/finalizers + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: null + name: my-release-catalogs +rules: + - apiGroups: + - joy.nesto.ca + resources: + - catalogs + verbs: + - get + - list + - watch + - update + - patch + - delete + - apiGroups: + - joy.nesto.ca + resources: + - catalogs/status + - catalogs/finalizers + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: null + name: my-release-releases +rules: + - apiGroups: + - joy.nesto.ca + resources: + - releases + verbs: + - get + - list + - watch + - update + - patch + - delete + - apiGroups: + - joy.nesto.ca + resources: + - releases/status + - releases/finalizers + verbs: + - get + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: null + name: my-release-namespaces +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - create + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: null + name: my-release-environments +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-release-environments +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: null + name: my-release-projects +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-release-projects +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: null + name: my-release-catalogs +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-release-catalogs +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: null + name: my-release-releases +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-release-releases +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: null + name: my-release-namespaces +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: my-release-namespaces +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: null + name: my-release-applications + namespace: argocd +rules: + - apiGroups: + - argoproj.io + resources: + - applications + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: null + name: my-release-applications + namespace: argocd +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: my-release-applications +subjects: + - kind: ServiceAccount + name: my-release + namespace: my-namespace +--- +# Source: joy-operator-chart/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: null + name: my-release +spec: + ports: + - name: health + port: 3000 + protocol: TCP + targetPort: health + selector: + release: my-release + type: ClusterIP +--- +# Source: joy-operator-chart/templates/serviceaccount.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: null + name: my-release \ No newline at end of file diff --git a/chart/tests/helm/values.yaml b/chart/tests/helm/values.yaml new file mode 100644 index 0000000..bc6a651 --- /dev/null +++ b/chart/tests/helm/values.yaml @@ -0,0 +1,7 @@ +helm: + registry: ghcr.io/testy-test + user: ouath2accesstoken + credentials: + key: creds + mountPath: /secrets/helm.creds + secret: helm-creds diff --git a/chart/values.yaml b/chart/values.yaml index d5d475e..94a9a7c 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -2,6 +2,7 @@ image: ghcr.io/nestoca/joy-operator version: latest helm: + user: '' registry: '' credentials: secret: '' diff --git a/cmd/operator/conf.go b/cmd/operator/conf.go index 0e010f4..67bf5ea 100644 --- a/cmd/operator/conf.go +++ b/cmd/operator/conf.go @@ -22,6 +22,7 @@ type Config struct { } type HelmLogin struct { + User string Registry string CredentialsPath string Credentials []byte @@ -35,6 +36,7 @@ func GetConfig() (Config, error) { conf.Var(conf.Environ, &cfg.EnvDestinations, "ENV_DESTINATIONS", conf.JSON[map[string]argocd.ApplicationDestination]) conf.Var(conf.Environ, &cfg.EnvSourcePattern, "ENV_SOURCE_PATTERN", conf.Default("environments/*/env.yaml")) conf.Var(conf.Environ, &cfg.HelmLogin.Registry, "HELM_REGISTRY") + conf.Var(conf.Environ, &cfg.HelmLogin.User, "HELM_USER", conf.Default("_json_key")) conf.Var(conf.Environ, &cfg.HelmLogin.CredentialsPath, "HELM_REGISTRY_CREDENTIALS_PATH") conf.Var(conf.Environ, &cfg.ChartCacheDir, "CHART_CACHE_DIR", conf.RequiredNonEmpty[string]()) conf.Var(conf.Environ, &cfg.Concurrency, "CONCURRENCY", conf.Default(defaultConcurrency)) diff --git a/cmd/operator/main.go b/cmd/operator/main.go index 4184b86..579cd69 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -53,9 +53,14 @@ func run() (err error) { } if cfg.HelmLogin.Registry != "" { - if err := AuthenticateHelm(ctx, cfg.HelmLogin.Registry, cfg.HelmLogin.Credentials); err != nil { + if err := AuthenticateHelm(ctx, cfg.HelmLogin); err != nil { return fmt.Errorf("failed to authenticate helm: %w", err) } + logger.Info( + "successfully authenticated to helm registry", + "registry", cfg.HelmLogin.Registry, + "user", cfg.HelmLogin.User, + ) } restCfg, err := func() (*rest.Config, error) { @@ -190,13 +195,13 @@ func run() (err error) { return <-e } -func AuthenticateHelm(ctx context.Context, registry string, credentials []byte) error { - login := exec.CommandContext(ctx, "helm", "registry", "login", "-u", "_json_key", "--password-stdin", registry) +func AuthenticateHelm(ctx context.Context, params HelmLogin) error { + login := exec.CommandContext(ctx, "helm", "registry", "login", "-u", params.User, "--password-stdin", params.Registry) var buffer bytes.Buffer login.Stdout = &buffer login.Stderr = &buffer - login.Stdin = bytes.NewReader(credentials) + login.Stdin = bytes.NewReader(params.Credentials) if err := login.Run(); err != nil { return fmt.Errorf("%w: %q", err, &buffer) diff --git a/cmd/operator/main_test.go b/cmd/operator/main_test.go index 78514ef..36734fc 100644 --- a/cmd/operator/main_test.go +++ b/cmd/operator/main_test.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "testing" "time" @@ -220,6 +221,10 @@ func TestMain(m *testing.M) { ), ) + if ok, _ := strconv.ParseBool(os.Getenv("SETUP_ONLY")); ok { + os.Exit(0) + } + os.Exit(m.Run()) }