diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml new file mode 100644 index 000000000..0419b5108 --- /dev/null +++ b/.github/workflows/go-test.yaml @@ -0,0 +1,56 @@ +name: Go Tests +on: + push: + branches: + - main + paths: + - "go/**" + - .github/workflows/go-test.yaml + pull_request: + paths: + - "go/**" + - .github/workflows/go-test.yaml + workflow_dispatch: + merge_group: + types: [checks_requested] + paths: + - "go/**" + - .github/workflows/go-test.yaml + +permissions: + contents: read + +jobs: + setup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + + - name: List Applications + id: list_dirs + run: | + apps=$(ls -d go/* | xargs -n1 basename | jq -R -s -c 'split("\n")[:-1]') + echo "apps=${apps}" + # TODO: dexop does not pass tests + #echo "apps=%s" "${app_array}" >> "$GITHUB_OUTPUT" + echo "apps=[\"understackctl\"]" >> "$GITHUB_OUTPUT" + shell: bash + outputs: + apps: ${{ steps.list_dirs.outputs.apps }} + + test: + runs-on: ubuntu-latest + needs: setup + strategy: + matrix: + app: ${{ fromJson(needs.setup.outputs.apps) }} + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 + with: + go-version-file: go/${{ matrix.app }}/go.mod + + - name: Test ${{ matrix.app }} + working-directory: go/${{ matrix.app }} + run: go test -v ./... diff --git a/charts/argocd-understack/templates/_helpers.tpl b/charts/argocd-understack/templates/_helpers.tpl index cd385dd28..c6a6213e8 100644 --- a/charts/argocd-understack/templates/_helpers.tpl +++ b/charts/argocd-understack/templates/_helpers.tpl @@ -108,7 +108,7 @@ Examples: deploy_path_prefix: "us/east" -> "us/east/uc-iad3-prod" Usage in valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/dex.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/manifests/dex/values.yaml Usage in source path: path: {{ include "understack.deploy_path" $ }}/manifests/dex diff --git a/charts/argocd-understack/templates/application-argo-events-workflows.yaml.tpl b/charts/argocd-understack/templates/application-argo-events-workflows.yaml.tpl index 6cf1a6b6f..01c1263ee 100644 --- a/charts/argocd-understack/templates/application-argo-events-workflows.yaml.tpl +++ b/charts/argocd-understack/templates/application-argo-events-workflows.yaml.tpl @@ -16,7 +16,7 @@ spec: ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/argo-events-workflows + - path: {{ include "understack.deploy_path" $ }}/argo-events-workflows ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-argo-events.yaml.tpl b/charts/argocd-understack/templates/application-argo-events.yaml.tpl index 9ffeed096..88e453cdb 100644 --- a/charts/argocd-understack/templates/application-argo-events.yaml.tpl +++ b/charts/argocd-understack/templates/application-argo-events.yaml.tpl @@ -15,7 +15,7 @@ spec: ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/argo-events + - path: {{ include "understack.deploy_path" $ }}/argo-events ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-argo-workflows.yaml.tpl b/charts/argocd-understack/templates/application-argo-workflows.yaml.tpl index 34eb99457..1165acd5a 100644 --- a/charts/argocd-understack/templates/application-argo-workflows.yaml.tpl +++ b/charts/argocd-understack/templates/application-argo-workflows.yaml.tpl @@ -12,7 +12,7 @@ spec: server: {{ $.Values.cluster_server }} project: understack sources: - - path: {{ include "understack.deploy_path" $ }}/manifests/argo-workflows + - path: {{ include "understack.deploy_path" $ }}/argo-workflows ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-cilium.yaml.tpl b/charts/argocd-understack/templates/application-cilium.yaml.tpl index a5181cf4d..b00a1cc6b 100644 --- a/charts/argocd-understack/templates/application-cilium.yaml.tpl +++ b/charts/argocd-understack/templates/application-cilium.yaml.tpl @@ -12,7 +12,7 @@ spec: server: {{ $.Values.cluster_server }} project: understack-infra sources: - - path: {{ include "understack.deploy_path" $ }}/manifests/cilium + - path: {{ include "understack.deploy_path" $ }}/cilium ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-dex.yaml.tpl b/charts/argocd-understack/templates/application-dex.yaml.tpl index 65fb96cc4..581422aa5 100644 --- a/charts/argocd-understack/templates/application-dex.yaml.tpl +++ b/charts/argocd-understack/templates/application-dex.yaml.tpl @@ -18,14 +18,14 @@ spec: releaseName: dex valueFiles: - $understack/components/dex/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/dex.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/dex/values.yaml repoURL: https://charts.dexidp.io targetRevision: 0.16.0 - path: components/dex ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/dex + - path: {{ include "understack.deploy_path" $ }}/dex ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-envoy-configs.yaml.tpl b/charts/argocd-understack/templates/application-envoy-configs.yaml.tpl index afdbf4cb5..6cc9f9e23 100644 --- a/charts/argocd-understack/templates/application-envoy-configs.yaml.tpl +++ b/charts/argocd-understack/templates/application-envoy-configs.yaml.tpl @@ -17,11 +17,11 @@ spec: ignoreMissingValueFiles: true valueFiles: - $understack/components/envoy-configs/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/envoy-configs.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/envoy-configs/values.yaml ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/envoy-configs + - path: {{ include "understack.deploy_path" $ }}/envoy-configs ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-envoy-gateway.yaml.tpl b/charts/argocd-understack/templates/application-envoy-gateway.yaml.tpl index 8a0e60906..b50c0b351 100644 --- a/charts/argocd-understack/templates/application-envoy-gateway.yaml.tpl +++ b/charts/argocd-understack/templates/application-envoy-gateway.yaml.tpl @@ -18,14 +18,14 @@ spec: releaseName: gateway-helm valueFiles: - $understack/components/envoy-gateway/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/envoy-gateway.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/envoy-gateway/values.yaml repoURL: docker.io/envoyproxy targetRevision: v1.6.0 - path: components/envoy-gateway ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/envoy-gateway + - path: {{ include "understack.deploy_path" $ }}/envoy-gateway ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-etcdbackup.yaml.tpl b/charts/argocd-understack/templates/application-etcdbackup.yaml.tpl index 497e51b87..4f5eb625c 100644 --- a/charts/argocd-understack/templates/application-etcdbackup.yaml.tpl +++ b/charts/argocd-understack/templates/application-etcdbackup.yaml.tpl @@ -16,7 +16,7 @@ spec: ignoreMissingValueFiles: true valueFiles: - $understack/components/etcdbackup/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/etcdbackup.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/etcdbackup/values.yaml path: components/etcdbackup ref: understack repoURL: {{ include "understack.understack_url" $ }} diff --git a/charts/argocd-understack/templates/application-external-dns.yaml.tpl b/charts/argocd-understack/templates/application-external-dns.yaml.tpl index f11260ec3..cc8a0a918 100644 --- a/charts/argocd-understack/templates/application-external-dns.yaml.tpl +++ b/charts/argocd-understack/templates/application-external-dns.yaml.tpl @@ -17,10 +17,10 @@ spec: ignoreMissingValueFiles: true releaseName: external-dns-rackspace valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/external-dns.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/external-dns/values.yaml repoURL: ghcr.io/rackerlabs/charts targetRevision: 0.2.0 - - path: {{ include "understack.deploy_path" $ }}/manifests/external-dns + - path: {{ include "understack.deploy_path" $ }}/external-dns ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-global-workflows.yaml.tpl b/charts/argocd-understack/templates/application-global-workflows.yaml.tpl index 733eabb54..506173c00 100644 --- a/charts/argocd-understack/templates/application-global-workflows.yaml.tpl +++ b/charts/argocd-understack/templates/application-global-workflows.yaml.tpl @@ -16,7 +16,7 @@ spec: ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/global-workflows + - path: {{ include "understack.deploy_path" $ }}/global-workflows ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-ingress-nginx.yaml.tpl b/charts/argocd-understack/templates/application-ingress-nginx.yaml.tpl index aabdb7372..e029b2385 100644 --- a/charts/argocd-understack/templates/application-ingress-nginx.yaml.tpl +++ b/charts/argocd-understack/templates/application-ingress-nginx.yaml.tpl @@ -17,7 +17,7 @@ spec: ignoreMissingValueFiles: true releaseName: ingress-nginx valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/ingress-nginx.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/ingress-nginx/values.yaml repoURL: https://kubernetes.github.io/ingress-nginx targetRevision: 4.12.1 - ref: deploy diff --git a/charts/argocd-understack/templates/application-monitoring.tpl b/charts/argocd-understack/templates/application-monitoring.tpl index 82691d5ba..689d1cd14 100644 --- a/charts/argocd-understack/templates/application-monitoring.tpl +++ b/charts/argocd-understack/templates/application-monitoring.tpl @@ -23,7 +23,7 @@ spec: releaseName: kube-prometheus-stack valueFiles: - $understack/operators/monitoring/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/monitoring.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/manifests/monitoring/values.yaml repoURL: https://prometheus-community.github.io/helm-charts targetRevision: 79.5.0 - path: operators/monitoring diff --git a/charts/argocd-understack/templates/application-nautobot-site.yaml.tpl b/charts/argocd-understack/templates/application-nautobot-site.yaml.tpl index ec031c81c..9bccf101b 100644 --- a/charts/argocd-understack/templates/application-nautobot-site.yaml.tpl +++ b/charts/argocd-understack/templates/application-nautobot-site.yaml.tpl @@ -12,7 +12,7 @@ spec: server: {{ $.Values.cluster_server }} project: understack sources: - - path: {{ include "understack.deploy_path" $ }}/manifests/nautobot-site + - path: {{ include "understack.deploy_path" $ }}/nautobot-site ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-nautobot.yaml.tpl b/charts/argocd-understack/templates/application-nautobot.yaml.tpl index 540d67615..d5f9bcb75 100644 --- a/charts/argocd-understack/templates/application-nautobot.yaml.tpl +++ b/charts/argocd-understack/templates/application-nautobot.yaml.tpl @@ -21,14 +21,14 @@ spec: releaseName: nautobot valueFiles: - $understack/components/nautobot/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/nautobot.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/nautobot/values.yaml repoURL: https://nautobot.github.io/helm-charts/ targetRevision: 2.5.6 - path: components/nautobot ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/nautobot + - path: {{ include "understack.deploy_path" $ }}/nautobot ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-nautobotop.yaml.tpl b/charts/argocd-understack/templates/application-nautobotop.yaml.tpl index 0c26c00cd..2bc0ccb9a 100644 --- a/charts/argocd-understack/templates/application-nautobotop.yaml.tpl +++ b/charts/argocd-understack/templates/application-nautobotop.yaml.tpl @@ -17,10 +17,10 @@ spec: ignoreMissingValueFiles: true releaseName: nautobotop valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/nautobotop.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/nautobotop/values.yaml repoURL: ghcr.io/rackerlabs/charts targetRevision: 0.0.1 - - path: {{ include "understack.deploy_path" $ }}/manifests/nautobotop + - path: {{ include "understack.deploy_path" $ }}/nautobotop ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-openebs.yaml.tpl b/charts/argocd-understack/templates/application-openebs.yaml.tpl index 08e48242f..0258acc29 100644 --- a/charts/argocd-understack/templates/application-openebs.yaml.tpl +++ b/charts/argocd-understack/templates/application-openebs.yaml.tpl @@ -18,7 +18,7 @@ spec: releaseName: openebs valueFiles: - $understack/operators/openebs/values.yaml - - $deploy/{{ $.Release.Name }}/helm-configs/openebs.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/openebs/values.yaml repoURL: https://openebs.github.io/openebs targetRevision: 4.4.0 - path: operators/openebs @@ -28,7 +28,7 @@ spec: - ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} - path: {{ include "understack.deploy_path" $ }}/manifests/openebs + path: {{ include "understack.deploy_path" $ }}/openebs syncPolicy: automated: prune: true diff --git a/charts/argocd-understack/templates/application-openstack-exporter.yaml.tpl b/charts/argocd-understack/templates/application-openstack-exporter.yaml.tpl index a11114563..f0761f743 100644 --- a/charts/argocd-understack/templates/application-openstack-exporter.yaml.tpl +++ b/charts/argocd-understack/templates/application-openstack-exporter.yaml.tpl @@ -18,7 +18,7 @@ spec: releaseName: prometheus-openstack-exporter valueFiles: - $understack/components/openstack-exporter/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/openstack-exporter.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/openstack-exporter/values.yaml repoURL: registry.scs.community/openstack-exporter targetRevision: 0.4.5 - ref: understack diff --git a/charts/argocd-understack/templates/application-openstack-helm.yaml.tpl b/charts/argocd-understack/templates/application-openstack-helm.yaml.tpl index f74d539ad..f9d5f441f 100644 --- a/charts/argocd-understack/templates/application-openstack-helm.yaml.tpl +++ b/charts/argocd-understack/templates/application-openstack-helm.yaml.tpl @@ -38,14 +38,14 @@ spec: valueFiles: - $understack/components/images-openstack.yaml - $understack/components/{{ $appName }}/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/secret-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/images-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/{{ $appName }}.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/secret-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/images-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/{{ $appName }}/values.yaml - path: components/{{ $appName }}/ ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/{{ $appName }} + - path: {{ include "understack.deploy_path" $ }}/{{ $appName }} ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-openstack-memcached.yaml.tpl b/charts/argocd-understack/templates/application-openstack-memcached.yaml.tpl index e9fb8dbac..bd52c432a 100644 --- a/charts/argocd-understack/templates/application-openstack-memcached.yaml.tpl +++ b/charts/argocd-understack/templates/application-openstack-memcached.yaml.tpl @@ -18,7 +18,7 @@ spec: releaseName: memcached valueFiles: - $understack/components/openstack/memcached-values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/openstack-memcached.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/openstack-memcached/values.yaml repoURL: https://charts.bitnami.com/bitnami targetRevision: 7.8.6 - ref: understack diff --git a/charts/argocd-understack/templates/application-openstack.yaml.tpl b/charts/argocd-understack/templates/application-openstack.yaml.tpl index cc26c63d8..2139ad35d 100644 --- a/charts/argocd-understack/templates/application-openstack.yaml.tpl +++ b/charts/argocd-understack/templates/application-openstack.yaml.tpl @@ -24,8 +24,8 @@ spec: helm: ignoreMissingValueFiles: true valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/openstack.yaml - - path: {{ include "understack.deploy_path" $ }}/manifests/openstack + - $deploy/{{ include "understack.deploy_path" $ }}/openstack/values.yaml + - path: {{ include "understack.deploy_path" $ }}/openstack ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-opentelemetry-operator.yaml.tpl b/charts/argocd-understack/templates/application-opentelemetry-operator.yaml.tpl index 563642f2c..ab3aef472 100644 --- a/charts/argocd-understack/templates/application-opentelemetry-operator.yaml.tpl +++ b/charts/argocd-understack/templates/application-opentelemetry-operator.yaml.tpl @@ -18,7 +18,7 @@ spec: releaseName: opentelemetry-operator valueFiles: - $understack/operators/opentelemetry-operator/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/opentelemetry-operator.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/opentelemetry-operator/values.yaml repoURL: https://open-telemetry.github.io/opentelemetry-helm-charts targetRevision: 0.95.1 - path: operators/opentelemetry-operator diff --git a/charts/argocd-understack/templates/application-openvswitch.yaml.tpl b/charts/argocd-understack/templates/application-openvswitch.yaml.tpl index b05db8d54..9b1a894a6 100644 --- a/charts/argocd-understack/templates/application-openvswitch.yaml.tpl +++ b/charts/argocd-understack/templates/application-openvswitch.yaml.tpl @@ -20,14 +20,14 @@ spec: valueFiles: - $understack/components/images-openstack.yaml - $understack/components/openvswitch/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/secret-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/images-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/openvswitch.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/secret-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/images-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/openvswitch/values.yaml - path: components/openvswitch/ ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/openvswitch + - path: {{ include "understack.deploy_path" $ }}/openvswitch ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-otel-collector.yaml.tpl b/charts/argocd-understack/templates/application-otel-collector.yaml.tpl index a87146fae..2571cb2da 100644 --- a/charts/argocd-understack/templates/application-otel-collector.yaml.tpl +++ b/charts/argocd-understack/templates/application-otel-collector.yaml.tpl @@ -12,7 +12,7 @@ spec: server: {{ $.Values.cluster_server }} project: understack sources: - - path: {{ include "understack.deploy_path" $ }}/manifests/otel-collector + - path: {{ include "understack.deploy_path" $ }}/otel-collector ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-ovn.yaml.tpl b/charts/argocd-understack/templates/application-ovn.yaml.tpl index d7ba699f3..62d182553 100644 --- a/charts/argocd-understack/templates/application-ovn.yaml.tpl +++ b/charts/argocd-understack/templates/application-ovn.yaml.tpl @@ -20,14 +20,14 @@ spec: valueFiles: - $understack/components/images-openstack.yaml - $understack/components/ovn/values.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/secret-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/manifests/images-openstack.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/ovn.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/secret-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/images-openstack.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/ovn/values.yaml - path: components/ovn/ ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/ovn + - path: {{ include "understack.deploy_path" $ }}/ovn ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-rook.yaml.tpl b/charts/argocd-understack/templates/application-rook.yaml.tpl index 064c9dff7..77818f249 100644 --- a/charts/argocd-understack/templates/application-rook.yaml.tpl +++ b/charts/argocd-understack/templates/application-rook.yaml.tpl @@ -18,7 +18,7 @@ spec: releaseName: rook-ceph valueFiles: - $understack/operators/rook/values-operator.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/rook-operator.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/rook-operator/values.yaml repoURL: https://charts.rook.io/release targetRevision: v1.16.4 - chart: rook-ceph-cluster @@ -27,7 +27,7 @@ spec: releaseName: rook-ceph-cluster valueFiles: - $understack/operators/rook/values-cluster.yaml - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/rook-cluster.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/rook-cluster/values.yaml repoURL: https://charts.rook.io/release targetRevision: v1.16.4 - path: operators/rook diff --git a/charts/argocd-understack/templates/application-site-workflows.yaml.tpl b/charts/argocd-understack/templates/application-site-workflows.yaml.tpl index 667368f25..54cc888be 100644 --- a/charts/argocd-understack/templates/application-site-workflows.yaml.tpl +++ b/charts/argocd-understack/templates/application-site-workflows.yaml.tpl @@ -16,7 +16,7 @@ spec: ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/site-workflows + - path: {{ include "understack.deploy_path" $ }}/site-workflows ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-snmp-exporter.yaml.tpl b/charts/argocd-understack/templates/application-snmp-exporter.yaml.tpl index 36ee9263a..4d526034d 100644 --- a/charts/argocd-understack/templates/application-snmp-exporter.yaml.tpl +++ b/charts/argocd-understack/templates/application-snmp-exporter.yaml.tpl @@ -17,7 +17,7 @@ spec: ignoreMissingValueFiles: true releaseName: prometheus-snmp-exporter valueFiles: - - $deploy/{{ include "understack.deploy_path" $ }}/helm-configs/prometheus-snmp-exporter.yaml + - $deploy/{{ include "understack.deploy_path" $ }}/prometheus-snmp-exporter/values.yaml repoURL: https://prometheus-community.github.io/helm-charts targetRevision: 5.6.0 - ref: understack diff --git a/charts/argocd-understack/templates/application-understack-cluster-issuer.yaml.tpl b/charts/argocd-understack/templates/application-understack-cluster-issuer.yaml.tpl index 010da8ea3..f6c6bb304 100644 --- a/charts/argocd-understack/templates/application-understack-cluster-issuer.yaml.tpl +++ b/charts/argocd-understack/templates/application-understack-cluster-issuer.yaml.tpl @@ -12,7 +12,7 @@ spec: server: {{ $.Values.cluster_server }} project: understack sources: - - path: {{ include "understack.deploy_path" $ }}/manifests/cert-manager + - path: {{ include "understack.deploy_path" $ }}/cert-manager ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/templates/application-undersync.yaml.tpl b/charts/argocd-understack/templates/application-undersync.yaml.tpl index 267550d5f..a1d3e2d07 100644 --- a/charts/argocd-understack/templates/application-undersync.yaml.tpl +++ b/charts/argocd-understack/templates/application-undersync.yaml.tpl @@ -16,7 +16,7 @@ spec: ref: understack repoURL: {{ include "understack.understack_url" $ }} targetRevision: {{ include "understack.understack_ref" $ }} - - path: {{ include "understack.deploy_path" $ }}/manifests/undersync + - path: {{ include "understack.deploy_path" $ }}/undersync ref: deploy repoURL: {{ include "understack.deploy_url" $ }} targetRevision: {{ include "understack.deploy_ref" $ }} diff --git a/charts/argocd-understack/values.yaml b/charts/argocd-understack/values.yaml index 0ce584b44..f08954d5b 100644 --- a/charts/argocd-understack/values.yaml +++ b/charts/argocd-understack/values.yaml @@ -23,7 +23,7 @@ deploy_ref: HEAD # -- Optional prefix for the deployment repository path. When set, the path # becomes "/" instead of just "". -# Example: "sites" would result in "sites/uc-iad3-prod/helm-configs/..." +# Example: "sites" would result in "sites/uc-iad3-prod/manifests/..." # @default -- "" (no prefix, just Release.Name) deploy_path_prefix: diff --git a/docs/deploy-guide/auth.md b/docs/deploy-guide/auth.md index 816b948a5..7255a9605 100644 --- a/docs/deploy-guide/auth.md +++ b/docs/deploy-guide/auth.md @@ -8,7 +8,7 @@ services and a connector must be configured to provide authentication. Unfortunately we need to use the domain ID (UUID) and not the domain name in the Dex configuration. This is not known before hand. So at this time you must copy the `config.connectors` section from `values-generic.yaml` or `values-azure.yaml` - into `${DEPLOY_NAME}/helm-configs/dexidp.yaml` and change the value of the + into `${DEPLOY_NAME}/dex/values.yaml` and change the value of the `domain` key in the `keystone_internal` section to the UUID of the `operator` domain. @@ -41,11 +41,11 @@ You will then make a note of the following pieces of information for your applic #### Azure Dex Configuration -In `${DEPLOY_NAME}/helm-configs/dexidp.yaml` under the `valuesFiles` key +In `${DEPLOY_NAME}/dex/values.yaml` under the `valuesFiles` key add `$values/components/dexidp/values-azure.yaml` beneath `values-generic.yaml` like: -```yaml title="${DEPLOY_NAME}/helm-configs/dexidp.yaml" +```yaml title="${DEPLOY_NAME}/dex/values.yaml" spec: sources: - chart: dex @@ -55,7 +55,7 @@ spec: valuesFiles: - $understack/components/dexidp/values-generic.yaml - $understack/components/dexidp/values-azure.yaml - - $deploy/YOUR_CLUSTER/helm-configs/dexidp.yaml + - $deploy/YOUR_CLUSTER/dex/values.yaml # rest omitted ``` @@ -68,7 +68,7 @@ kubectl --namespace dex \ --from-literal=client-id={client_id} \ --from-literal=client-secret={client_secret} \ --from-literal=redirect-uri=https://dex.${DNS_ZONE}/callback \ - -o yaml > ${UC_DEPLOY}/${DEPLOY_NAME}/manifests/secret-oidc-sso-dex.yaml + -o yaml > ${UC_DEPLOY}/${DEPLOY_NAME}/secret-oidc-sso-dex.yaml ``` You must remember to add this secret to your secret storage (e.g. for @@ -92,9 +92,9 @@ to different parts of the system. The default groups through the system are: ### Nautobot To customize the administrator group set the following in your -`${DEPLOY_NAME}/helm-configs/nautobot.yaml` +`${DEPLOY_NAME}/nautobot/values.yaml` -```yaml title="${DEPLOY_NAME}/helm-configs/nautobot.yaml" +```yaml title="${DEPLOY_NAME}/nautobot/values.yaml" nautobot: extraEnvVars: # ignoring existing values here, don't remove diff --git a/docs/deploy-guide/component-config.md b/docs/deploy-guide/component-config.md index 3a4948c21..bea93d3c6 100644 --- a/docs/deploy-guide/component-config.md +++ b/docs/deploy-guide/component-config.md @@ -51,7 +51,7 @@ the value `understack` or `deploy` respectively. - component: argo sources: - ref: deploy - path: deploy_name/manifests/argo-workflows + path: deploy_name/argo-workflows ``` The above would replace the default behavior to only source from your @@ -66,7 +66,7 @@ Another example would be: path: component/openstack helm: valueFiles: - - $deploy/deploy_name/helm-configs/openstack.yaml + - $deploy/deploy_name/openstack/values.yaml - ref: deploy ``` @@ -85,12 +85,12 @@ first determine if it's being deployed with Helm or Kustomize. ### Helm Most of the applications can have their Helm values overridden by adding -or modifying `$DEPLOY_NAME/helm-configs/$COMPONENT.yaml` in your deployment +or modifying `$DEPLOY_NAME/$COMPONENT/values.yaml` in your deployment repo. ### Kustomize -To make changes you will need to add or modify files in `$DEPLOY_NAME/manifests/$COMPONENT/` +To make changes you will need to add or modify files in `$DEPLOY_NAME/$COMPONENT/` in your deployment repo. ## Modifying Environment State diff --git a/docs/deploy-guide/config-argo-workflows.md b/docs/deploy-guide/config-argo-workflows.md index 37d8237da..29a0b1c81 100644 --- a/docs/deploy-guide/config-argo-workflows.md +++ b/docs/deploy-guide/config-argo-workflows.md @@ -3,7 +3,7 @@ ## UI and API access To allow UI and API access to Argo Workflows you must configure SSO and -the Ingress. To start, in your deployment repo under `$ENV_NAME/manifests/` +the Ingress. To start, in your deployment repo under `$ENV_NAME/` you must have a `argo-workflows` directory. You can download the SSO configuration template and Ingress template from: diff --git a/docs/deploy-guide/config-dex.md b/docs/deploy-guide/config-dex.md index 1d58a0fe8..5edd2ade8 100644 --- a/docs/deploy-guide/config-dex.md +++ b/docs/deploy-guide/config-dex.md @@ -4,7 +4,7 @@ As of Dex v2.44.0 the HTTP 302 redirects contain a `Location` header which is relative and not all clients like this in authentication flows. To workaround this easiest approach is have the ingress rewrite the URL for us. -In `$DEPLOY_NAME/helm-configs/dex.yaml` in the `ingress.annotations` key, add the +In `$DEPLOY_NAME/dex/values.yaml` in the `ingress.annotations` key, add the following: ```yaml diff --git a/docs/deploy-guide/config-openstack.md b/docs/deploy-guide/config-openstack.md index eb01ff5e0..97f97c367 100644 --- a/docs/deploy-guide/config-openstack.md +++ b/docs/deploy-guide/config-openstack.md @@ -14,7 +14,7 @@ The OpenStack component is a Helm chart that creates: ## Configuration -Configure the OpenStack component by editing `$DEPLOY_NAME/helm-configs/openstack.yaml` in your deployment repository. +Configure the OpenStack component by editing `$DEPLOY_NAME/openstack/values.yaml` in your deployment repository. ### Basic Configuration diff --git a/docs/deploy-guide/deploy-repo.md b/docs/deploy-guide/deploy-repo.md index b0f969136..f678fb0a1 100644 --- a/docs/deploy-guide/deploy-repo.md +++ b/docs/deploy-guide/deploy-repo.md @@ -12,60 +12,96 @@ a [Site cluster](./welcome.md#system-division). ## Initial Structure -To begin we will create our directory structure inside our Deployment Repository. +To begin we will create our directory structure inside our Deployment Repository +using the `understackctl` CLI tool. ```bash title="From the Deployment Repo" -# where 'my-global' is the environment name you've used for your global cluster -mkdir -p my-global/{manifests,helm-configs,inventory} -# where 'my-site' is the environment name you've used for your site cluster -mkdir -p my-site/{manifests,helm-configs,inventory} +# For a global cluster +understackctl deploy init my-global --type global -cat <<- EOF > my-global/deploy.yaml ---- -understack_url: https://github.com/rackerlabs/understack.git -understack_ref: v0.0.5 # replace with the tag or git reference you want to use -deploy_url: git@github.com:my-org/my-deploy.git -deploy_ref: HEAD +# For a site cluster +understackctl deploy init my-site --type site -global: - enabled: false +# For an all-in-one (AIO) cluster +understackctl deploy init my-aio --type aio +``` -site: - enabled: false -EOF +This creates a `deploy.yaml` configuration file with the component list for your +cluster type. The `deploy_url` will be auto-detected from your git remote if available. + +Example `deploy.yaml` for a site cluster: -cat <<- EOF > my-site/deploy.yaml ---- +```yaml understack_url: https://github.com/rackerlabs/understack.git -understack_ref: v0.0.5 deploy_url: git@github.com:my-org/my-deploy.git -deploy_ref: HEAD +site: + enabled: true + keystone: + enabled: true + nova: + enabled: true + neutron: + enabled: true + ironic: + enabled: true + glance: + enabled: true + # ... additional site components +``` -global: - enabled: false +After initialization, create the manifest directories: -site: - enabled: false -EOF +```bash +understackctl deploy update my-site +``` + +This creates `/` directories with `kustomization.yaml` and +`values.yaml` files for each enabled component. + +### component directories + +Inside of the `manifests` directory you'll have child directories named after +each component (using hyphens, e.g., `cert-manager`, `argo-workflows`). These +directories contain: + +- `kustomization.yaml` - Kustomize configuration for the component +- `values.yaml` - Helm value overrides for the component + +You can verify all required files exist with: + +```bash +understackctl deploy check my-site ``` -For `dev` focused deployments, you do not need to specify the refs directly -as they can be set on the ArgoCD cluster secret to allow more flexibility -during testing. +### Managing Components -### manifests directory +To enable or disable components, edit the component sections in `deploy.yaml`: -Inside of the `manifests` directory you'll create child directories that will -be named after each application that we will deploy. These directories are -expected to hold a `kustomization.yaml` as `kustomize` will be used to apply -these manifests to your cluster. +```yaml +site: + enabled: true + keystone: + enabled: true + nova: + enabled: true + neutron: + enabled: false # Disable by setting to false +``` + +Then sync the filesystem: + +```bash +understackctl deploy update my-site +``` -### helm-configs directory +This will create directories for enabled components and remove directories for +disabled components. -The `helm-configs` directory holds YAML files which are Helm `values.yaml` -files that are used as additional values files that will be merged together -by Helm. These yaml files will be named after each application with the `.yaml` -file extension. +!!! note "Component Naming" + Component names in `deploy.yaml` use underscores (e.g., `cert_manager`, + `argo_workflows`) but the corresponding directories use hyphens + (e.g., `cert-manager`, `argo-workflows`). The `understackctl` tool + handles this conversion automatically. ### inventory directory diff --git a/docs/deploy-guide/gitops-install.md b/docs/deploy-guide/gitops-install.md index b4c8da0ac..3dce880c0 100644 --- a/docs/deploy-guide/gitops-install.md +++ b/docs/deploy-guide/gitops-install.md @@ -143,28 +143,41 @@ git -C "${UC_DEPLOY}" commit -m "my-k3s: secrets generation" ### Defining the app deployment -In this section we will use the [App of Apps][app-of-apps] pattern to define -the deployment of all the components of UnderStack. +In this section we will initialize the deployment configuration and create +the component manifests. ```bash -./scripts/gitops-deploy.sh ${UC_DEPLOY}/my-k3s.env -git -C "${UC_DEPLOY}" add helm-configs/my-k3s -git -C "${UC_DEPLOY}" commit -m "my-k3s: initial cluster config" +cd "${UC_DEPLOY}" +understackctl deploy init my-k3s --type aio +understackctl deploy update my-k3s +git add my-k3s +git commit -m "my-k3s: initial cluster config" ``` +The `deploy init` command creates a `deploy.yaml` with all components for an +AIO (all-in-one) deployment. The `deploy update` command creates the manifest +directories with `kustomization.yaml` and `values.yaml` files for each component. + ## Final modifications of your deployment -This is point you can make changes to the [ArgoCD][argocd] configs before -you do the deployment in your `$UC_DEPLOY` repo. You'll want to consider -any changes to each of components to your cluster by modifying or adding -values files or kustomize patches. This should be considered a rough template -that is yours to modify. Once you've made all the changes you want to make, -ensure that you `git push` your `$UC_DEPLOY` repo so that ArgoCD can access it. +This is the point you can make changes to the [ArgoCD][argocd] configs before +you do the deployment in your `$UC_DEPLOY` repo. You can: -For authentication, please review the [authentication](auth.md) documentation. +- Edit component-specific Helm values in `my-k3s//values.yaml` +- Add Kustomize patches in `my-k3s//kustomization.yaml` +- Enable/disable components by editing the `components` list in `my-k3s/deploy.yaml` + and running `understackctl deploy update my-k3s` + +Verify your configuration: + +```bash +understackctl deploy check my-k3s +``` -For OpenStack Helm components, an empty file in `$UC_DEPLOY/my-k3s/helm-configs` -has been created for each component for you to use for customization. +Once you've made all the changes you want to make, ensure that you `git push` +your `$UC_DEPLOY` repo so that ArgoCD can access it. + +For authentication, please review the [authentication](auth.md) documentation. See the Preparing Your Deployment section for component specific details. @@ -177,13 +190,13 @@ to your git server so that ArgoCD can access it. Configure your ArgoCD to be aware of your cluster: ```bash -kubectl -n argocd apply -f "${UC_DEPLOY}/${DEPLOY_NAME}/manifests/argocd/secret-*-cluster.yaml" +kubectl -n argocd apply -f "${UC_DEPLOY}/${DEPLOY_NAME}/argocd/secret-*-cluster.yaml" ``` Now configure your ArgoCD to have the credential access to your deploy repo: ```bash -kubectl -n argocd apply -f "${UC_DEPLOY}/${DEPLOY_NAME}/manifests/argocd/secret-deploy-repo.yaml" +kubectl -n argocd apply -f "${UC_DEPLOY}/${DEPLOY_NAME}/argocd/secret-deploy-repo.yaml" ``` Label the node(s) to allow OpenStack control plane installation: diff --git a/docs/deploy-guide/global-cluster.md b/docs/deploy-guide/global-cluster.md index 0406542cf..e4db3c701 100644 --- a/docs/deploy-guide/global-cluster.md +++ b/docs/deploy-guide/global-cluster.md @@ -19,37 +19,45 @@ Before starting, ensure: - Your target Kubernetes cluster has a **storage provisioner** configured and operational -## Create the Cluster Directory +## Create the Initial Configuration -In your deployment repository, create a directory named after your cluster. -This name must match the cluster name registered in ArgoCD. +Initialize your global cluster configuration using `understackctl`: -```bash title="From the deploy repo root" -mkdir -p my-global/{manifests,helm-configs} +```bash +cd /path/to/deploy-repo +understackctl deploy init my-global --type global ``` -## Create the Initial deploy.yaml - -Create a `deploy.yaml` in your cluster directory. This file combines the -repository metadata and the Helm values for the -[argocd-understack][argocd-helm-chart] chart into a single -file. - -Start with **everything disabled** so you can verify ArgoCD connectivity -before deploying any workloads. +This creates `my-global/deploy.yaml` with all global components enabled: ```yaml title="my-global/deploy.yaml" ---- understack_url: https://github.com/rackerlabs/understack.git -understack_ref: v0.1.0 # replace with the tag or git reference you want to use deploy_url: https://github.com/my-org/my-deploy.git -deploy_ref: HEAD +global: + enabled: true + cert_manager: + enabled: true + cilium: + enabled: true + dex: + enabled: true + external_secrets: + enabled: true + # ... additional global components +``` +To start with everything disabled for initial ArgoCD connectivity testing, +edit the file to set `enabled: false`: + +```yaml global: enabled: false +``` -site: - enabled: false +Create the manifest directories: + +```bash +understackctl deploy update my-global ``` ## Register the Cluster with ArgoCD @@ -103,14 +111,10 @@ global: enabled: true ``` -Create a directory for your cluster issuer manifests: - -```bash -mkdir -p my-global/manifests/cert-manager -``` - -Place your `ClusterIssuer` resource(s) in this directory. These define how -certificates will be issued for services in your cluster. +After enabling, run `understackctl deploy update my-global` to create the +directory. Place your `ClusterIssuer` resource(s) in +`my-global/cert-manager/`. These define how certificates will be +issued for services in your cluster. ### External DNS @@ -135,15 +139,13 @@ global: enabled: true ``` -Create the manifests directory and Helm values file: +Create the component directories and Helm values file: ```bash -mkdir -p my-global/manifests/dex -``` - -Configure your authentication in the Helm values file: +After enabling, run `understackctl deploy update my-global` to create the +directory. Configure your authentication in the Helm values file: -```yaml title="my-global/helm-configs/dex.yaml" +```yaml title="my-global/dex/values.yaml" # See the Dex configuration guide for details on what goes here ``` @@ -187,8 +189,8 @@ following: For each component that requires environment-specific configuration, you can provide: -- **Helm value overrides** in `my-global/helm-configs/.yaml` -- **Kustomize manifests** in `my-global/manifests//` +- **Helm value overrides** in `my-global//values.yaml` +- **Kustomize manifests** in `my-global//` See [Configuring Components](./component-config.md) for details on these customization methods. @@ -197,5 +199,4 @@ customization methods. [dex]: [cert-manager]: [external-dns]: -[argocd-helm-chart]: <../operator-guide/argocd-helm-chart.md> [values]: diff --git a/docs/deploy-guide/management-cluster.md b/docs/deploy-guide/management-cluster.md index b52d6f6ac..1b2d72546 100644 --- a/docs/deploy-guide/management-cluster.md +++ b/docs/deploy-guide/management-cluster.md @@ -15,11 +15,11 @@ can skip this section. ### Configuring ArgoCD -To configure ArgoCD you'll need to create a `$DEPLOY_NAME/helm-configs/argocd.yaml` +To configure ArgoCD you'll need to create a `$DEPLOY_NAME/argocd/values.yaml` in your deploy repo. In there you can configure the DNS name that will be used for the Ingress as well as the authentication. An example of which can be: -```yaml title="$DEPLOY_NAME/helm-configs/argocd.yaml" +```yaml title="$DEPLOY_NAME/argocd/values.yaml" global: domain: argocd.your.dns.zone @@ -47,7 +47,7 @@ for authentication. You can also include the following: -```yaml ttile="$DEPLOY_NAME/helm-configs/argocd.yaml" +```yaml title="$DEPLOY_NAME/argocd/values.yaml" extraObjects: - # k8s object like a secret definition ``` @@ -76,9 +76,9 @@ DEPLOY_NAME=my-site # this should match one of your top-level directories in you DNS_ZONE="zone.where.all.dns.entries.will.live" # all services will have DNS entries under here # assuming you are in the top-level of your deploy repo checkout -mkdir -p management/manifests/argocd/ +mkdir -p management/argocd/ -cat << EOF > management/manifests/argocd/${DEPLOY_NAME}-cluster.yaml +cat << EOF > management/argocd/${DEPLOY_NAME}-cluster.yaml apiVersion: v1 kind: Secret metadata: @@ -142,7 +142,7 @@ Once you have the Kubernetes secret which defines your cluster config ready you must commit it to your deploy repo and then deploy it to your ArgoCD. In your deploy repo you should commit your cluster config at -`management/manifests/argocd/secret-${DEPLOY_NAME}-cluster.yaml`. It is +`management/argocd/secret-${DEPLOY_NAME}-cluster.yaml`. It is highly advised to use a secure method to store your secrets. There are many ways in ArgoCD to achieve this. For more details see their [Secrets Management][argocd-secrets-mgmt] guide. @@ -151,7 +151,7 @@ Lastly ensure that you've added this secret to your `kustomization.yaml` with the following: ```bash -cd management/manifests/argocd/ +cd management/argocd/ [ ! -f kustomization.yaml ] && kustomize create kustomize edit add resource secret-${DEPLOY_NAME}-cluster.yaml ``` diff --git a/docs/deploy-guide/nautobot-secrets.md b/docs/deploy-guide/nautobot-secrets.md index 9f8f4b4fb..f98846a56 100644 --- a/docs/deploy-guide/nautobot-secrets.md +++ b/docs/deploy-guide/nautobot-secrets.md @@ -134,7 +134,7 @@ The Nautobot service account automation is deployed and managed through **Argo C - This token is stored securely as a Kubernetes Secret in the `nautobot` namespace of the global cluster. - Another responsibility of global cluster is to create superuser token of site clusters which is used by site cluster to bootstrap other tokens. - example: global cluster (staging) creates site cluster super-user (rxdb-lab) secret and creates user and token in nautobot. - - In deploy repo create sites cluster superuser secrets in `"{{.name}}/manifests/nautobot-site` directory as defined in [`apps/site/nautobot-site.yaml`](https://github.com/rackerlabs/understack/blob/main/apps/site/nautobot-site.yaml). + - In deploy repo create sites cluster superuser secrets in `"{{.name}}/nautobot-site` directory as defined in [`apps/site/nautobot-site.yaml`](https://github.com/rackerlabs/understack/blob/main/apps/site/nautobot-site.yaml). 2. **Site Nautobot Deployment** - Each site’s Argo CD application (`nautobot-site.yaml`) only creates secrets. @@ -142,7 +142,7 @@ The Nautobot service account automation is deployed and managed through **Argo C - The site retrieves the **superuser token** and uses it to authenticate against Nautobot. - Site-specific **service accounts and tokens** are then created through Argo Events and Ansible workflows. - Global cluster's superuser token is not used anywhere in site cluster. - - In deploy repo define superuser bootstrap secret in `"{{.name}}/manifests/nautobot-site` directory as defined in [`apps/site/nautobot-site.yaml`](https://github.com/rackerlabs/understack/blob/main/apps/site/nautobot-site.yaml). + - In deploy repo define superuser bootstrap secret in `"{{.name}}/nautobot-site` directory as defined in [`apps/site/nautobot-site.yaml`](https://github.com/rackerlabs/understack/blob/main/apps/site/nautobot-site.yaml). 3. **Automation Integration** - When new site credentials are created in Secret Management Backend, the change triggers the site-level automation flow. diff --git a/docs/deploy-guide/override-openstack-svc-config.md b/docs/deploy-guide/override-openstack-svc-config.md index cb9b330af..80505a6d2 100644 --- a/docs/deploy-guide/override-openstack-svc-config.md +++ b/docs/deploy-guide/override-openstack-svc-config.md @@ -14,7 +14,7 @@ somevalue = 1 Firstly you would create either a `Secret` or a `ConfigMap` and ensure that it is being loaded by `${DEPLOY_NAME}/manifest/neutron/kustomize.yaml` -Then you would edit `${DEPLOY_NAME}/helm-configs/neutron.yaml` and add something +Then you would edit `${DEPLOY_NAME}/neutron/values.yaml` and add something like: ```yaml diff --git a/docs/deploy-guide/requirements.md b/docs/deploy-guide/requirements.md index 241fd0858..779fa3990 100644 --- a/docs/deploy-guide/requirements.md +++ b/docs/deploy-guide/requirements.md @@ -34,32 +34,31 @@ The layout of this repo will be something like: ```shell . ├── management # (1) -│   ├── helm-configs # (2) -│   └── manifests # (3) -├── iad3-prod # (4) -│   ├── flavors -> ../flavors/prod # (5) -│   ├── helm-configs -│   └── manifests -├── iad3-staging # (6) -│   ├── flavors -> ../flavors/nonprod # (7) -│   ├── helm-configs -│   └── manifests -├── global-prod # (8) -│   ├── helm-configs -│   └── manifests +│ ├── argocd # (2) +│ └── dex +├── iad3-prod # (3) +│ ├── flavors -> ../flavors/prod # (4) +│ ├── keystone +│ └── ironic +├── iad3-staging # (5) +│ ├── flavors -> ../flavors/nonprod # (6) +│ ├── keystone +│ └── neutron +├── global-prod # (7) +│ ├── nautobot +│ └── dex └── flavors -    ├── nonprod -    └── prod + ├── nonprod + └── prod ``` 1. This contains data which the cluster labeled as `management` will consume. -2. helm `values.yaml` files per application/component will be here for `management`. -3. Any Kubernetes manifests per application/component will be here for `management`. -4. This contains data which the cluster labeled as `iad3-prod` will consume. -5. The definitions of the hardware flavors that this cluster, which later you will see maps to a site. -6. This contains data which the cluster labeled as `iad3-staging` will consume. -7. The definitions of the hardware flavors that this cluster, which later you will see maps to a site. Notice it is different than staging. -8. The cluster labeled as `global-prod` will have resources consumed here. +2. Per-component directories with Kubernetes manifests and Helm `values.yaml` overrides. +3. This contains data which the cluster labeled as `iad3-prod` will consume. +4. The definitions of the hardware flavors that this cluster, which later you will see maps to a site. +5. This contains data which the cluster labeled as `iad3-staging` will consume. +6. The definitions of the hardware flavors that this cluster, which later you will see maps to a site. Notice it is different than staging. +7. The cluster labeled as `global-prod` will have resources consumed here. ### Creation and Access diff --git a/docs/deploy-guide/secrets-eso-setup.md b/docs/deploy-guide/secrets-eso-setup.md index aa9c8fdf1..16701c3df 100644 --- a/docs/deploy-guide/secrets-eso-setup.md +++ b/docs/deploy-guide/secrets-eso-setup.md @@ -33,9 +33,9 @@ For each OpenStack service, the ESO integration creates: ### 1. Enable ESO Integration -In your `$DEPLOY_NAME/helm-configs/openstack.yaml`: +In your `$DEPLOY_NAME/openstack/values.yaml`: -```yaml title="$DEPLOY_NAME/helm-configs/openstack.yaml" +```yaml title="$DEPLOY_NAME/openstack/values.yaml" keystoneServiceUsers: enabled: true secretStore: @@ -51,7 +51,7 @@ keystoneServiceUsers: Define the service accounts for each OpenStack component: -```yaml title="$DEPLOY_NAME/helm-configs/openstack.yaml" +```yaml title="$DEPLOY_NAME/openstack/values.yaml" keystoneServiceUsers: services: nova: @@ -89,7 +89,7 @@ Each service requires specific usage types: UnderStack's ['openstack' component][openstack-component] a `ClusterSecretStore` will be created that uses Kubernetes authentication: -```yaml title="$DEPLOY_NAME/helm-configs/openstack.yaml" +```yaml title="$DEPLOY_NAME/openstack/values.yaml" # Generated automatically by the openstack component apiVersion: external-secrets.io/v1 kind: ClusterSecretStore @@ -170,7 +170,7 @@ kubectl get secrets -n openstack | grep keystone-password ``` ```bash title="Check if ESO is enabled in your configuration" -grep -A 10 "keystoneServiceUsers:" $DEPLOY_NAME/helm-configs/openstack.yaml +grep -A 10 "keystoneServiceUsers:" $DEPLOY_NAME/openstack/values.yaml ``` ### Migration Steps diff --git a/docs/deploy-guide/site-cluster.md b/docs/deploy-guide/site-cluster.md index c01b058ed..2c22de8ce 100644 --- a/docs/deploy-guide/site-cluster.md +++ b/docs/deploy-guide/site-cluster.md @@ -22,39 +22,47 @@ Before starting, ensure: - Your target Kubernetes cluster has a **storage provisioner** configured and operational -## Create the Cluster Directory +## Create the Initial Configuration -In your deployment repository, create a directory named after your cluster. -This name must match the cluster name registered in ArgoCD. +Initialize your site cluster configuration using `understackctl`: -```bash title="From the deploy repo root" -mkdir -p my-site/{manifests,helm-configs} +```bash +cd /path/to/deploy-repo +understackctl deploy init my-site --type site ``` -## Create the Initial deploy.yaml - -Create a `deploy.yaml` in your cluster directory. This file combines the -repository metadata and the Helm values for the -[argocd-understack][argocd-helm-chart] chart into a single -file. - -Start with **everything disabled** so you can verify ArgoCD connectivity -before deploying any workloads. +This creates `my-site/deploy.yaml` with all site components enabled: ```yaml title="my-site/deploy.yaml" ---- understack_url: https://github.com/rackerlabs/understack.git -understack_ref: v0.1.0 # replace with the tag or git reference you want to use deploy_url: https://github.com/my-org/my-deploy.git -deploy_ref: HEAD +site: + enabled: true + keystone: + enabled: true + nova: + enabled: true + neutron: + enabled: true + ironic: + enabled: true + # ... additional site components +``` -global: - enabled: false +To start with everything disabled for initial ArgoCD connectivity testing, +edit the file to set `enabled: false`: +```yaml site: enabled: false ``` +Create the manifest directories: + +```bash +understackctl deploy update my-site +``` + ## Register the Cluster with ArgoCD Your Site cluster must be registered as a cluster in ArgoCD on the @@ -112,13 +120,9 @@ site: enabled: true ``` -Create a directory for your cluster issuer manifests: - -```bash -mkdir -p my-site/manifests/cert-manager -``` - -Place your `ClusterIssuer` resource(s) in this directory. +After enabling, run `understackctl deploy update my-site` to create the +component directory. Then place your `ClusterIssuer` resource(s) in +`my-site/cert-manager/`. #### External DNS @@ -194,11 +198,9 @@ site: enabled: true ``` -Configure the shared OpenStack infrastructure in your Helm values: - -```bash title="Create the values file" -touch my-site/helm-configs/openstack.yaml -``` +Configure the shared OpenStack infrastructure in your Helm values. +After enabling the component, run `understackctl deploy update my-site` to +create the directory, then edit `my-site/openstack/values.yaml`. See [Configuring OpenStack (Shared)](./config-openstack.md) for details on MariaDB, RabbitMQ, and service account configuration. @@ -226,11 +228,9 @@ site: enabled: true ``` -Provide any Helm value overrides: - -```bash -touch my-site/helm-configs/keystone.yaml -``` +After enabling, run `understackctl deploy update my-site` to create the +directory. Provide any Helm value overrides in +`my-site/keystone/values.yaml`. ### OpenStack Services - Wave 2 (Core Services) @@ -399,8 +399,8 @@ Enable these as needed for your deployment. For each component that requires environment-specific configuration, you can provide: -- **Helm value overrides** in `my-site/helm-configs/.yaml` -- **Kustomize manifests** in `my-site/manifests//` +- **Helm value overrides** in `my-site//values.yaml` +- **Kustomize manifests** in `my-site//` See [Configuring Components](./component-config.md) for details on these customization methods. @@ -418,6 +418,5 @@ customization methods. - [Override OpenStack Service Config](./override-openstack-svc-config.md) - Per-service configuration overrides -[argocd-helm-chart]: <../operator-guide/argocd-helm-chart.md> [nautobot]: [dex]: diff --git a/docs/design-guide/add-remove-app.md b/docs/design-guide/add-remove-app.md index 31349ffeb..9def50086 100644 --- a/docs/design-guide/add-remove-app.md +++ b/docs/design-guide/add-remove-app.md @@ -38,14 +38,14 @@ sources: # pulls defaults from the understack repo - $understack/components/dex/values.yaml # pulls overrides from your deploy repo - - $deploy/{{.name}}/helm-configs/dex.yaml + - $deploy/{{.name}}/dex/values.yaml ignoreMissingValueFiles: true - ref: understack # path should only be here if you have manifests to load path: 'components/dex' - ref: deploy # only needed if manifests should be here - path: '{{.name}}/manifests/dex' + path: '{{.name}}/dex' ``` {% endraw %} diff --git a/docs/operator-guide/argocd-helm-chart.md b/docs/operator-guide/argocd-helm-chart.md index 5b4a4dda0..86c320faf 100644 --- a/docs/operator-guide/argocd-helm-chart.md +++ b/docs/operator-guide/argocd-helm-chart.md @@ -69,11 +69,11 @@ site: ### Deploy Repository Path Prefix -By default, the chart looks for cluster configs at `/helm-configs/` -and `/manifests/`. Use `deploy_path_prefix` to add a prefix: +By default, the chart looks for cluster configs at `/`. +Use `deploy_path_prefix` to add a prefix: ```yaml -deploy_path_prefix: sites # Results in "sites/my-cluster/helm-configs/..." +deploy_path_prefix: sites # Results in "sites/my-cluster/..." ``` **Default structure:** @@ -81,11 +81,13 @@ deploy_path_prefix: sites # Results in "sites/my-cluster/helm-configs/..." ```text deploy-repo/ ├── uc-iad3-prod/ -│ ├── helm-configs/ -│ │ ├── keystone.yaml -│ │ └── ... -│ └── manifests/ -│ └── ... +│ ├── keystone/ +│ │ ├── values.yaml +│ │ └── kustomization.yaml +│ ├── dex/ +│ │ ├── values.yaml +│ │ └── kustomization.yaml +│ └── ... └── uc-ord1-staging/ └── ... ``` @@ -96,8 +98,8 @@ deploy-repo/ deploy-repo/ ├── sites/ │ ├── uc-iad3-prod/ -│ │ ├── helm-configs/ -│ │ └── manifests/ +│ │ ├── keystone/ +│ │ └── dex/ │ └── uc-ord1-staging/ │ └── ... └── other-stuff/ diff --git a/docs/operator-guide/gateway-api.md b/docs/operator-guide/gateway-api.md index 5a95d6249..a7dfa66d3 100644 --- a/docs/operator-guide/gateway-api.md +++ b/docs/operator-guide/gateway-api.md @@ -190,7 +190,7 @@ In your `apps.yaml`, add ingress-nginx to the skip list: #### 2. Create GatewayClass Resource -Create `manifests/envoy-gateway/gateway-class.yaml`: +Create `envoy-gateway/gateway-class.yaml`: ```yaml --- @@ -202,7 +202,7 @@ spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller ``` -And `manifests/envoy-gateway/kustomization.yaml`: +And `envoy-gateway/kustomization.yaml`: ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 @@ -213,7 +213,7 @@ resources: #### 3. Configure Gateway and Routes -Create `helm-configs/envoy-configs.yaml` with your Gateway and route definitions: +Create `envoy-configs/values.yaml` with your Gateway and route definitions: ```yaml # yaml-language-server: $schema=https://rackerlabs.github.io/understack/schema/component-envoy-configs.schema.json @@ -268,7 +268,7 @@ routes: backendType: tls ``` -Create empty `manifests/envoy-configs/kustomization.yaml`: +Create empty `envoy-configs/kustomization.yaml`: ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 @@ -282,13 +282,13 @@ Delete any standalone Ingress manifests: ```bash # Example -rm manifests/argo-workflows/ingress.yaml +rm argo-workflows/ingress.yaml ``` Update kustomization files to remove ingress references: ```yaml -# manifests/argo-workflows/kustomization.yaml +# argo-workflows/kustomization.yaml resources: - https://github.com/rackerlabs/understack.git//components/argo-workflows/?ref=v0.0.43 # Remove: - ingress.yaml @@ -296,10 +296,10 @@ resources: #### 5. Disable Ingress in Helm Charts -For components that generate their own Ingress resources (like monitoring stack), disable them in helm-configs: +For components that generate their own Ingress resources (like monitoring stack), disable them in their values: ```yaml -# helm-configs/monitoring.yaml +# monitoring/values.yaml grafana: ingress: enabled: false @@ -318,7 +318,7 @@ alertmanager: If using Cilium L2 announcements, update the service selector to match Envoy Gateway: ```yaml -# manifests/cilium/c-l2policies.yaml +# cilium/c-l2policies.yaml spec: serviceSelector: matchLabels: @@ -330,7 +330,7 @@ spec: Update any services that share IPs with the ingress controller: ```yaml -# manifests/ironic/services-ironic.yaml +# ironic/services-ironic.yaml metadata: annotations: lbipam.cilium.io/sharing-cross-namespace: envoy-gateway # Changed from: ingress-nginx @@ -338,7 +338,7 @@ metadata: #### 8. Update cert-manager Configuration -If not already done, ensure cert-manager has Gateway API support enabled in `helm-configs/cert-manager.yaml`: +If not already done, ensure cert-manager has Gateway API support enabled in `cert-manager/values.yaml`: ```yaml config: @@ -362,7 +362,7 @@ Commit and push your changes. ArgoCD will automatically: When adding a new application that needs external access: -1. **Add route configuration** to your site's `helm-configs/envoy-configs.yaml`: +1. **Add route configuration** to your site's `envoy-configs/values.yaml`: ```yaml routes: @@ -487,7 +487,7 @@ The `envoy-configs` chart is deployed via ArgoCD and generates Gateway and Route **Deployment**: `apps/site/envoy-configs.yaml` -**Site-specific values**: `deploy//helm-configs/envoy-configs.yaml` +**Site-specific values**: `deploy//envoy-configs/values.yaml` ### Route Schema diff --git a/docs/operator-guide/nautobotop.md b/docs/operator-guide/nautobotop.md index a643b8a46..0e70a86c0 100644 --- a/docs/operator-guide/nautobotop.md +++ b/docs/operator-guide/nautobotop.md @@ -189,8 +189,8 @@ The nautobotop component is defined in the UnderStack repository at `apps/global UnderStack allows environment-specific customization through: -- **Helm Values Override**: Create `$DEPLOY_NAME/helm-configs/nautobotop.yaml` in deployment repository -- **Manifest Overlays**: Add Kustomize overlays in `$DEPLOY_NAME/manifests/nautobotop/` +- **Helm Values Override**: Create `$DEPLOY_NAME/nautobotop/values.yaml` in deployment repository +- **Manifest Overlays**: Add Kustomize overlays in `$DEPLOY_NAME/nautobotop/` - **Component Control**: Disable or modify sources in your `$DEPLOY_NAME/apps.yaml` ## Local Development diff --git a/docs/operator-guide/troubleshooting-osh.md b/docs/operator-guide/troubleshooting-osh.md index c46d0e8f9..40f446fdf 100644 --- a/docs/operator-guide/troubleshooting-osh.md +++ b/docs/operator-guide/troubleshooting-osh.md @@ -26,8 +26,8 @@ helm template \ --namespace openstack \ -f understack/components/images-openstack.yaml \ # the version we are deploying -f understack/components/ironic/values.yaml \ # common configs - -f your-deploy/$YOUR_ENV/manifests/secret-openstack.yaml \ # credentials - -f your-deploy/$YOUR_ENV/helm-configs/ironic.yaml # your specific overrides + -f your-deploy/$YOUR_ENV/secret-openstack.yaml \ # credentials + -f your-deploy/$YOUR_ENV/ironic/values.yaml # your specific overrides ``` For another component change all instances of `ironic` to the one you want to target. @@ -41,7 +41,7 @@ helm template \ --namespace openstack \ -f understack/components/images-openstack.yaml \ # the version we are deploying -f understack/components/ironic/values.yaml \ # common configs - -f your-deploy/$YOUR_ENV/manifests/secret-openstack.yaml \ # credentials - -f your-deploy/$YOUR_ENV/helm-configs/ironic.yaml \ # your specific overrides + -f your-deploy/$YOUR_ENV/secret-openstack.yaml \ # credentials + -f your-deploy/$YOUR_ENV/ironic/values.yaml \ # your specific overrides | kubectl -n openstack diff -f - ``` diff --git a/go/understackctl/README.md b/go/understackctl/README.md index fcfe96861..697f5b672 100644 --- a/go/understackctl/README.md +++ b/go/understackctl/README.md @@ -1,36 +1,34 @@ # understackctl -understackctl is a CLI that helps quickly generate a deployment repository. +understackctl is a CLI tool for managing UnderStack deployments. -## How to use +## Commands -> Please make sure that you have kubeseal binary installed your system https://github.com/bitnami-labs/sealed-secrets +### deploy -* Export all these env +Manage deployment repository structure and configuration. -```sh -export UC_DEPLOY="" -export DEPLOY_NAME="" -export UC_DEPLOY_GIT_URL=git@github.com:RSS-Engineering/undercloud-deploy.git -export UC_DEPLOY_SSH_FILE= -export DNS_ZONE=.dev.undercloud.rackspace.net -export UC_DEPLOY_EMAIL="" -export UC_AIO=yes -``` +```bash +# Initialize a new cluster configuration +understackctl deploy init --type -* Quick Run +# Sync manifest directories with deploy.yaml +understackctl deploy update +# Validate configuration +understackctl deploy check ``` -go run *.go quickstart -go run *.go help -``` -* Commit all changes to the undercloud-deploy repo in your branch +See the [Deploy Guide](https://rackerlabs.github.io/understack/deploy-guide/deploy-repo/) for details. + +### Other Commands + +Run `understackctl --help` to see all available commands. ## Build -* Local build `make build` ( this will only build binary for you local os ). -* Cross-Platform build `make build-all` ( build for linux-mac-windows ) +* Local build: `make build` (builds for your local OS) +* Cross-platform build: `make build-all` (builds for Linux, macOS, Windows) ## Development diff --git a/go/understackctl/cmd/deploy/check.go b/go/understackctl/cmd/deploy/check.go new file mode 100644 index 000000000..0a085a2b7 --- /dev/null +++ b/go/understackctl/cmd/deploy/check.go @@ -0,0 +1,65 @@ +package deploy + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/charmbracelet/log" + "github.com/spf13/cobra" +) + +func newCmdDeployCheck() *cobra.Command { + cmd := &cobra.Command{ + Use: "check ", + Short: "Verify component manifests exist", + Long: `Check that kustomization.yaml and values.yaml exist for each enabled component.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clusterName := args[0] + return runDeployCheck(clusterName) + }, + } + + return cmd +} + +func runDeployCheck(clusterName string) error { + config, err := loadDeployConfig(clusterName) + if err != nil { + return err + } + + components := enabledComponents(config) + if len(components) == 0 { + log.Info("No components enabled") + return nil + } + + missing := []string{} + + for _, comp := range components { + compDir := filepath.Join(clusterName, comp) + kustomPath := filepath.Join(compDir, "kustomization.yaml") + valuesPath := filepath.Join(compDir, "values.yaml") + + if _, err := os.Stat(kustomPath); os.IsNotExist(err) { + missing = append(missing, kustomPath) + } + + if _, err := os.Stat(valuesPath); os.IsNotExist(err) { + missing = append(missing, valuesPath) + } + } + + if len(missing) > 0 { + log.Error("Missing required files:") + for _, path := range missing { + log.Errorf(" - %s", path) + } + return fmt.Errorf("validation failed: %d missing files", len(missing)) + } + + log.Infof("All %d components validated successfully", len(components)) + return nil +} diff --git a/go/understackctl/cmd/deploy/config.go b/go/understackctl/cmd/deploy/config.go new file mode 100644 index 000000000..4c17c9192 --- /dev/null +++ b/go/understackctl/cmd/deploy/config.go @@ -0,0 +1,50 @@ +package deploy + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" +) + +func loadDeployConfig(clusterName string) (map[string]any, error) { + deployYamlPath := filepath.Join(clusterName, "deploy.yaml") + data, err := os.ReadFile(deployYamlPath) + if err != nil { + return nil, fmt.Errorf("failed to read deploy.yaml: %w", err) + } + + var config map[string]any + if err := yaml.Unmarshal(data, &config); err != nil { + return nil, fmt.Errorf("failed to parse deploy.yaml: %w", err) + } + + return config, nil +} + +func enabledComponents(config map[string]any) []string { + var components []string + + for _, section := range []string{"global", "site"} { + if sectionRaw, ok := config[section]; ok { + if sectionMap, ok := sectionRaw.(map[string]any); ok { + if enabled, ok := sectionMap["enabled"].(bool); ok && enabled { + for key, val := range sectionMap { + if key == "enabled" { + continue + } + if compMap, ok := val.(map[string]any); ok { + if compEnabled, ok := compMap["enabled"].(bool); ok && compEnabled { + components = append(components, strings.ReplaceAll(key, "_", "-")) + } + } + } + } + } + } + } + + return components +} diff --git a/go/understackctl/cmd/deploy/deploy.go b/go/understackctl/cmd/deploy/deploy.go index d53dc7e10..0132636fa 100644 --- a/go/understackctl/cmd/deploy/deploy.go +++ b/go/understackctl/cmd/deploy/deploy.go @@ -29,6 +29,12 @@ func NewCmdDeploy() *cobra.Command { } addDeployRepoFlag(cmd) + + cmd.AddCommand(newCmdDeployInit()) + cmd.AddCommand(newCmdDeployCheck()) + cmd.AddCommand(newCmdDeployUpdate()) + cmd.AddCommand(newCmdDeployRender()) + return cmd } diff --git a/go/understackctl/cmd/deploy/deploy_test.go b/go/understackctl/cmd/deploy/deploy_test.go new file mode 100644 index 000000000..2eb5174c6 --- /dev/null +++ b/go/understackctl/cmd/deploy/deploy_test.go @@ -0,0 +1,322 @@ +package deploy + +import ( + "os" + "path/filepath" + "testing" + + "gopkg.in/yaml.v3" +) + +func TestDeployConfigStructure(t *testing.T) { + config := map[string]any{ + "understack_url": understackRepoURL, + "deploy_url": "https://github.com/example/deploy.git", + "global": map[string]any{ + "enabled": true, + "cert_manager": map[string]any{ + "enabled": true, + }, + "external_secrets": map[string]any{ + "enabled": false, + }, + }, + "site": map[string]any{ + "enabled": true, + "keystone": map[string]any{ + "enabled": true, + }, + }, + } + + data, err := yaml.Marshal(&config) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + + var parsed map[string]any + if err := yaml.Unmarshal(data, &parsed); err != nil { + t.Fatalf("failed to unmarshal config: %v", err) + } + + globalMap := parsed["global"].(map[string]any) + certMgr := globalMap["cert_manager"].(map[string]any) + if !certMgr["enabled"].(bool) { + t.Error("cert_manager should be enabled") + } +} + +func TestEnabledComponentsConvertsToHyphens(t *testing.T) { + config := map[string]any{ + "global": map[string]any{ + "enabled": true, + "cert_manager": map[string]any{ + "enabled": true, + }, + "external_secrets": map[string]any{ + "enabled": true, + }, + }, + "site": map[string]any{ + "enabled": true, + "argo_workflows": map[string]any{ + "enabled": true, + }, + "mariadb_operator": map[string]any{ + "enabled": false, + }, + }, + } + + components := enabledComponents(config) + + expected := map[string]bool{ + "cert-manager": true, + "external-secrets": true, + "argo-workflows": true, + } + + if len(components) != len(expected) { + t.Fatalf("expected %d components, got %d", len(expected), len(components)) + } + + for _, comp := range components { + if !expected[comp] { + t.Errorf("unexpected component: %s", comp) + } + } +} + +func TestDeployInit(t *testing.T) { + tmpDir := t.TempDir() + clusterName := filepath.Join(tmpDir, "test-cluster") + + if err := runDeployInit(clusterName, "site", "origin"); err != nil { + t.Fatalf("runDeployInit failed: %v", err) + } + + deployYaml := filepath.Join(clusterName, "deploy.yaml") + if _, err := os.Stat(deployYaml); os.IsNotExist(err) { + t.Fatal("deploy.yaml was not created") + } + + data, err := os.ReadFile(deployYaml) + if err != nil { + t.Fatalf("failed to read deploy.yaml: %v", err) + } + + var config map[string]any + if err := yaml.Unmarshal(data, &config); err != nil { + t.Fatalf("failed to parse deploy.yaml: %v", err) + } + + if config["understack_url"] != understackRepoURL { + t.Errorf("unexpected understack_url: %s", config["understack_url"]) + } + + if _, ok := config["global"]; ok { + t.Error("global should not be set for site type") + } + + siteMap, ok := config["site"].(map[string]any) + if !ok || !siteMap["enabled"].(bool) { + t.Error("site should be enabled") + } + + // Check that components are present as individual keys + componentCount := 0 + for key := range siteMap { + if key != "enabled" { + componentCount++ + } + } + + if componentCount == 0 { + t.Error("site should have component keys") + } +} + +func TestDeployUpdate(t *testing.T) { + tmpDir := t.TempDir() + clusterName := filepath.Join(tmpDir, "test-cluster") + + config := map[string]any{ + "understack_url": understackRepoURL, + "site": map[string]any{ + "enabled": true, + "keystone": map[string]any{ + "enabled": true, + }, + "nova_compute": map[string]any{ + "enabled": true, + }, + }, + } + + if err := os.MkdirAll(clusterName, 0755); err != nil { + t.Fatalf("failed to create cluster dir: %v", err) + } + + data, err := yaml.Marshal(&config) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + + deployYaml := filepath.Join(clusterName, "deploy.yaml") + if err := os.WriteFile(deployYaml, data, 0644); err != nil { + t.Fatalf("failed to write deploy.yaml: %v", err) + } + + if err := runDeployUpdate(clusterName); err != nil { + t.Fatalf("runDeployUpdate failed: %v", err) + } + + keystoneDir := filepath.Join(clusterName, "keystone") + novaDir := filepath.Join(clusterName, "nova-compute") + + for _, dir := range []string{keystoneDir, novaDir} { + kustomPath := filepath.Join(dir, "kustomization.yaml") + valuesPath := filepath.Join(dir, "values.yaml") + + if _, err := os.Stat(kustomPath); os.IsNotExist(err) { + t.Errorf("kustomization.yaml not created in %s", dir) + } + + if _, err := os.Stat(valuesPath); os.IsNotExist(err) { + t.Errorf("values.yaml not created in %s", dir) + } + } +} + +func TestDeployCheck(t *testing.T) { + tmpDir := t.TempDir() + clusterName := filepath.Join(tmpDir, "test-cluster") + + config := map[string]any{ + "site": map[string]any{ + "enabled": true, + "keystone": map[string]any{ + "enabled": true, + }, + }, + } + + if err := os.MkdirAll(clusterName, 0755); err != nil { + t.Fatalf("failed to create cluster dir: %v", err) + } + + data, err := yaml.Marshal(&config) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + + deployYaml := filepath.Join(clusterName, "deploy.yaml") + if err := os.WriteFile(deployYaml, data, 0644); err != nil { + t.Fatalf("failed to write deploy.yaml: %v", err) + } + + if err := runDeployCheck(clusterName); err == nil { + t.Error("check should fail when components don't exist") + } + + keystoneDir := filepath.Join(clusterName, "keystone") + if err := os.MkdirAll(keystoneDir, 0755); err != nil { + t.Fatalf("failed to create keystone dir: %v", err) + } + + kustomPath := filepath.Join(keystoneDir, "kustomization.yaml") + valuesPath := filepath.Join(keystoneDir, "values.yaml") + + if err := os.WriteFile(kustomPath, []byte("test"), 0644); err != nil { + t.Fatalf("failed to write kustomization.yaml: %v", err) + } + + if err := os.WriteFile(valuesPath, []byte("test"), 0644); err != nil { + t.Fatalf("failed to write values.yaml: %v", err) + } + + if err := runDeployCheck(clusterName); err != nil { + t.Errorf("check should pass: %v", err) + } +} + +func TestDeployWorkflowIntegration(t *testing.T) { + tmpDir := t.TempDir() + clusterName := filepath.Join(tmpDir, "integration-test") + + if err := runDeployInit(clusterName, "site", "origin"); err != nil { + t.Fatalf("init failed: %v", err) + } + + config, err := loadDeployConfig(clusterName) + if err != nil { + t.Fatalf("failed to load config: %v", err) + } + + siteMap, ok := config["site"].(map[string]any) + if !ok || !siteMap["enabled"].(bool) { + t.Fatal("site should be enabled") + } + + if err := runDeployUpdate(clusterName); err != nil { + t.Fatalf("update failed: %v", err) + } + + components := enabledComponents(config) + for _, comp := range components { + compDir := filepath.Join(clusterName, comp) + if _, err := os.Stat(compDir); os.IsNotExist(err) { + t.Errorf("directory not created for component: %s", comp) + } + } + + if err := runDeployCheck(clusterName); err != nil { + t.Fatalf("check failed: %v", err) + } + + // Modify config to only have keystone and nova + config["site"] = map[string]any{ + "enabled": true, + "keystone": map[string]any{ + "enabled": true, + }, + "nova": map[string]any{ + "enabled": true, + }, + } + + data, err := yaml.Marshal(&config) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + + deployYaml := filepath.Join(clusterName, "deploy.yaml") + if err := os.WriteFile(deployYaml, data, 0644); err != nil { + t.Fatalf("failed to write deploy.yaml: %v", err) + } + + if err := runDeployUpdate(clusterName); err != nil { + t.Fatalf("update after config change failed: %v", err) + } + + entries, err := os.ReadDir(clusterName) + if err != nil { + t.Fatalf("failed to read cluster dir: %v", err) + } + + // Count only component directories (not deploy.yaml) + compCount := 0 + for _, entry := range entries { + if entry.IsDir() { + compCount++ + } + } + + if compCount != 2 { + t.Errorf("expected 2 component directories, got %d", compCount) + } + + if err := runDeployCheck(clusterName); err != nil { + t.Fatalf("check after cleanup failed: %v", err) + } +} diff --git a/go/understackctl/cmd/deploy/init.go b/go/understackctl/cmd/deploy/init.go new file mode 100644 index 000000000..c7cb1f815 --- /dev/null +++ b/go/understackctl/cmd/deploy/init.go @@ -0,0 +1,114 @@ +package deploy + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/charmbracelet/log" + "github.com/rackerlabs/understack/go/understackctl/internal/chartvalues" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +const understackRepoURL = "https://github.com/rackerlabs/understack.git" + +func newCmdDeployInit() *cobra.Command { + var clusterType string + var gitRemote string + + cmd := &cobra.Command{ + Use: "init ", + Short: "Create cluster directory with deploy.yaml", + Long: `Initialize a cluster directory with deploy.yaml configuration. +Populates global and site component sections based on cluster type.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clusterName := args[0] + return runDeployInit(clusterName, clusterType, gitRemote) + }, + } + + cmd.Flags().StringVar(&clusterType, "type", "aio", "Cluster type: global, site, or aio") + cmd.Flags().StringVar(&gitRemote, "git-remote", "origin", "Git remote name to use for deploy URL") + + return cmd +} + +func runDeployInit(clusterName, clusterType, gitRemote string) error { + if clusterType != "global" && clusterType != "site" && clusterType != "aio" { + return fmt.Errorf("invalid cluster type %q: must be global, site, or aio", clusterType) + } + + clusterDir := clusterName + if err := os.MkdirAll(clusterDir, 0755); err != nil { + return fmt.Errorf("failed to create cluster directory: %w", err) + } + + deployYamlPath := filepath.Join(clusterDir, "deploy.yaml") + if _, err := os.Stat(deployYamlPath); err == nil { + return fmt.Errorf("deploy.yaml already exists at %s", deployYamlPath) + } + + deployURL, err := getGitRemoteURL(gitRemote) + if err != nil { + log.Warnf("Could not detect git remote: %v", err) + deployURL = "" + } + + config := make(map[string]any) + config["understack_url"] = understackRepoURL + config["deploy_url"] = deployURL + + log.Infof("Fetching component list from UnderStack") + valuesData, err := chartvalues.FetchValues("main") + if err != nil { + return fmt.Errorf("failed to fetch values.yaml: %w", err) + } + + globalComponents, siteComponents, err := chartvalues.ParseComponents(valuesData) + if err != nil { + return fmt.Errorf("failed to parse components: %w", err) + } + + if clusterType == "global" || clusterType == "aio" { + globalMap := make(map[string]any) + globalMap["enabled"] = true + for _, c := range globalComponents { + globalMap[c.Key] = map[string]any{"enabled": true} + } + config["global"] = globalMap + } + + if clusterType == "site" || clusterType == "aio" { + siteMap := make(map[string]any) + siteMap["enabled"] = true + for _, c := range siteComponents { + siteMap[c.Key] = map[string]any{"enabled": true} + } + config["site"] = siteMap + } + + data, err := yaml.Marshal(&config) + if err != nil { + return fmt.Errorf("failed to marshal config: %w", err) + } + + if err := os.WriteFile(deployYamlPath, data, 0644); err != nil { + return fmt.Errorf("failed to write deploy.yaml: %w", err) + } + + log.Infof("Created %s", deployYamlPath) + return nil +} + +func getGitRemoteURL(remoteName string) (string, error) { + cmd := exec.Command("git", "remote", "get-url", remoteName) + output, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(output)), nil +} diff --git a/go/understackctl/cmd/deploy/render.go b/go/understackctl/cmd/deploy/render.go new file mode 100644 index 000000000..f5d6ac578 --- /dev/null +++ b/go/understackctl/cmd/deploy/render.go @@ -0,0 +1,75 @@ +package deploy + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/spf13/cobra" +) + +const ( + defaultChartURL = "https://github.com/rackerlabs/understack//charts/argocd-understack" +) + +func newCmdDeployRender() *cobra.Command { + var chartPath string + var valuesFile string + var version string + + cmd := &cobra.Command{ + Use: "render ", + Short: "Preview rendered ArgoCD Applications via helm template", + Long: `Render the ArgoCD Applications Helm chart for a cluster, printing +the rendered YAML to stdout. This is a convenience wrapper around +helm template.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clusterName := args[0] + return runDeployRender(clusterName, chartPath, valuesFile, version) + }, + } + + cmd.Flags().StringVar(&chartPath, "chart-path", "", "Path or URL to the ArgoCD Helm chart (default: UnderStack GitHub repo)") + cmd.Flags().StringVarP(&valuesFile, "values", "f", "", "Path to the per-cluster values file (default: /values.yaml)") + cmd.Flags().StringVar(&version, "version", "main", "Chart version (branch/tag) when using the default git chart URL") + + return cmd +} + +func runDeployRender(clusterName, chartPath, valuesFile, version string) error { + if chartPath == "" { + chartPath = defaultChartURL + "?ref=" + version + } + + if valuesFile == "" { + valuesFile = filepath.Join(clusterName, "values.yaml") + } + + if _, err := os.Stat(valuesFile); err != nil { + return fmt.Errorf("values file not found: %s", valuesFile) + } + + helmPath, err := exec.LookPath("helm") + if err != nil { + return fmt.Errorf("helm not found in PATH: %w", err) + } + + args := []string{ + "template", + clusterName, + chartPath, + "-f", valuesFile, + } + + helmCmd := exec.Command(helmPath, args...) + helmCmd.Stdout = os.Stdout + helmCmd.Stderr = os.Stderr + + if err := helmCmd.Run(); err != nil { + return fmt.Errorf("helm template failed: %w", err) + } + + return nil +} diff --git a/go/understackctl/cmd/deploy/update.go b/go/understackctl/cmd/deploy/update.go new file mode 100644 index 000000000..5e16b3af1 --- /dev/null +++ b/go/understackctl/cmd/deploy/update.go @@ -0,0 +1,104 @@ +package deploy + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/charmbracelet/log" + "github.com/spf13/cobra" +) + +const ( + kustomizationContent = `apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: [] +` + valuesContent = `# Component-specific Helm values +` +) + +func newCmdDeployUpdate() *cobra.Command { + cmd := &cobra.Command{ + Use: "update ", + Short: "Sync components with deploy.yaml", + Long: `Add or remove component directories based on deploy.yaml configuration.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clusterName := args[0] + return runDeployUpdate(clusterName) + }, + } + + return cmd +} + +func runDeployUpdate(clusterName string) error { + config, err := loadDeployConfig(clusterName) + if err != nil { + return err + } + + enabledComps := enabledComponents(config) + enabledSet := make(map[string]bool) + for _, comp := range enabledComps { + enabledSet[comp] = true + } + + // Remove disabled components + entries, err := os.ReadDir(clusterName) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to read cluster directory: %w", err) + } + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + if !enabledSet[entry.Name()] { + compDir := filepath.Join(clusterName, entry.Name()) + if err := os.RemoveAll(compDir); err != nil { + return fmt.Errorf("failed to remove %s: %w", compDir, err) + } + log.Infof("Removed %s", compDir) + } + } + + // Add enabled components and ensure files exist + created := 0 + for _, comp := range enabledComps { + compDir := filepath.Join(clusterName, comp) + kustomPath := filepath.Join(compDir, "kustomization.yaml") + valuesPath := filepath.Join(compDir, "values.yaml") + + dirExists := false + if _, err := os.Stat(compDir); err == nil { + dirExists = true + } + + if err := os.MkdirAll(compDir, 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", compDir, err) + } + + if _, err := os.Stat(kustomPath); os.IsNotExist(err) { + if err := os.WriteFile(kustomPath, []byte(kustomizationContent), 0644); err != nil { + return fmt.Errorf("failed to write %s: %w", kustomPath, err) + } + } + + if _, err := os.Stat(valuesPath); os.IsNotExist(err) { + if err := os.WriteFile(valuesPath, []byte(valuesContent), 0644); err != nil { + return fmt.Errorf("failed to write %s: %w", valuesPath, err) + } + } + + if !dirExists { + log.Infof("Created %s", compDir) + created++ + } + } + + log.Infof("Updated components: %d created", created) + return nil +} diff --git a/go/understackctl/cmd/dex/dex.go b/go/understackctl/cmd/dex/dex.go index 7c2ee147a..17caeebe0 100644 --- a/go/understackctl/cmd/dex/dex.go +++ b/go/understackctl/cmd/dex/dex.go @@ -52,7 +52,7 @@ func generateDexServiceSecrets() error { } } - helpers.UpdateKustomizeFile(filepath.Join(envutil.Getenv("DEPLOY_NAME"), "manifests", "dex")) + helpers.UpdateKustomizeFile(filepath.Join(envutil.Getenv("DEPLOY_NAME"), "dex")) return nil } diff --git a/go/understackctl/cmd/helmConfig/helmConfig.go b/go/understackctl/cmd/helmConfig/helmConfig.go index 6fdc05702..d95ec6437 100644 --- a/go/understackctl/cmd/helmConfig/helmConfig.go +++ b/go/understackctl/cmd/helmConfig/helmConfig.go @@ -65,8 +65,8 @@ func dex() error { filePath := filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "helm", - "dex.yaml", + "dex", + "values.yaml", ) if err := fsutil.WriteFile(filePath, data, os.ModePerm); err != nil { @@ -82,8 +82,8 @@ func glance() error { filePath := filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "helm", - "glance.yaml", + "glance", + "values.yaml", ) if err := fsutil.WriteFile(filePath, template, os.ModePerm); err != nil { @@ -114,8 +114,8 @@ func ironic() error { filePath := filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "helm", - "ironic.yaml", + "ironic", + "values.yaml", ) if err := fsutil.WriteFile(filePath, template, os.ModePerm); err != nil { @@ -210,8 +210,8 @@ func rook() error { filePath := filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "helm", - "rook-cluster.yaml", + "rook-cluster", + "values.yaml", ) if err := fsutil.WriteFile(filePath, template, os.ModePerm); err != nil { diff --git a/go/understackctl/cmd/other/other.go b/go/understackctl/cmd/other/other.go index 0663b6a03..6dc882957 100644 --- a/go/understackctl/cmd/other/other.go +++ b/go/understackctl/cmd/other/other.go @@ -99,7 +99,6 @@ func updateOpenStackSecretsFile() error { secretFilePath := filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "manifests", "secret-openstack.yaml", ) log.Info("updating secret-openstack.yaml", "path", secretFilePath) diff --git a/go/understackctl/helpers/helpers.go b/go/understackctl/helpers/helpers.go index 447df4cf9..69b0c4335 100644 --- a/go/understackctl/helpers/helpers.go +++ b/go/understackctl/helpers/helpers.go @@ -20,7 +20,6 @@ type SecretConfig struct { func GetManifestPathToService(service string) string { return filepath.Join( envutil.Getenv("DEPLOY_NAME"), - "manifests", service, ) } diff --git a/go/understackctl/internal/chartvalues/parse.go b/go/understackctl/internal/chartvalues/parse.go new file mode 100644 index 000000000..313c2cbb4 --- /dev/null +++ b/go/understackctl/internal/chartvalues/parse.go @@ -0,0 +1,112 @@ +package chartvalues + +import ( + "fmt" + "io" + "net/http" + "strings" + + "gopkg.in/yaml.v3" +) + +const ( + rawGitHubURL = "https://raw.githubusercontent.com/rackerlabs/understack/refs" +) + +// ComponentKey represents a component found in the values.yaml. +type ComponentKey struct { + Key string // original key, e.g. "cert_manager" + Name string // hyphenated name, e.g. "cert-manager" +} + +// FetchValues fetches the ArgoCD chart values.yaml from GitHub for the given version. +// Version can be a branch name (e.g. "main") or a tag (e.g. "v0.1.6"). +func FetchValues(version string) ([]byte, error) { + refType := "heads" + if strings.HasPrefix(version, "v") { + refType = "tags" + } + + url := fmt.Sprintf("%s/%s/%s/charts/argocd-understack/values.yaml", rawGitHubURL, refType, version) + + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to fetch values.yaml: %w", err) + } + defer func() { + _ = resp.Body.Close() + }() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch values.yaml: HTTP %d from %s", resp.StatusCode, url) + } + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + return data, nil +} + +// knownNonComponents are keys under global/site that are not component names. +var knownNonComponents = map[string]bool{ + "enabled": true, +} + +// ParseComponents extracts component keys from the global and site blocks of values.yaml. +// It returns separate lists for global and site components. +func ParseComponents(valuesYAML []byte) (global []ComponentKey, site []ComponentKey, err error) { + var values map[string]any + if err := yaml.Unmarshal(valuesYAML, &values); err != nil { + return nil, nil, fmt.Errorf("failed to parse values.yaml: %w", err) + } + + global, err = extractComponents(values, "global") + if err != nil { + return nil, nil, fmt.Errorf("failed to extract global components: %w", err) + } + + site, err = extractComponents(values, "site") + if err != nil { + return nil, nil, fmt.Errorf("failed to extract site components: %w", err) + } + + return global, site, nil +} + +// extractComponents walks a scope block (global or site) and returns component keys. +func extractComponents(values map[string]any, scope string) ([]ComponentKey, error) { + scopeRaw, ok := values[scope] + if !ok { + return nil, nil + } + + scopeMap, ok := scopeRaw.(map[string]any) + if !ok { + return nil, fmt.Errorf("%s is not a map", scope) + } + + var components []ComponentKey + for key := range scopeMap { + if knownNonComponents[key] { + continue + } + + // Verify this key maps to a map (component config block) + child, ok := scopeMap[key] + if !ok { + continue + } + if _, isMap := child.(map[string]any); !isMap { + continue + } + + components = append(components, ComponentKey{ + Key: key, + Name: strings.ReplaceAll(key, "_", "-"), + }) + } + + return components, nil +} diff --git a/go/understackctl/internal/chartvalues/parse_test.go b/go/understackctl/internal/chartvalues/parse_test.go new file mode 100644 index 000000000..eef6248b0 --- /dev/null +++ b/go/understackctl/internal/chartvalues/parse_test.go @@ -0,0 +1,182 @@ +package chartvalues + +import ( + "sort" + "testing" +) + +const sampleValuesYAML = ` +cluster_server: https://kubernetes.default.svc +understack_url: https://github.com/rackerlabs/understack.git +understack_ref: HEAD +deploy_url: +deploy_ref: HEAD +deploy_path_prefix: + +global: + enabled: true + cert_manager: + enabled: true + cilium: + enabled: true + dex: + enabled: true + external_secrets: + enabled: true + +site: + enabled: true + cilium: + enabled: true + argo_events: + enabled: true + openstack: + enabled: true + repoUrl: https://tarballs.opendev.org/openstack/openstack-helm + namespace: openstack + keystone: + enabled: true + wave: 1 + chartVersion: "2025.2.7" + snmp_exporter: + enabled: true + undersync: + enabled: true +` + +func sortComponentKeys(keys []ComponentKey) { + sort.Slice(keys, func(i, j int) bool { + return keys[i].Key < keys[j].Key + }) +} + +func TestParseComponents(t *testing.T) { + global, site, err := ParseComponents([]byte(sampleValuesYAML)) + if err != nil { + t.Fatalf("ParseComponents failed: %v", err) + } + + sortComponentKeys(global) + sortComponentKeys(site) + + // Verify global components + expectedGlobal := []ComponentKey{ + {Key: "cert_manager", Name: "cert-manager"}, + {Key: "cilium", Name: "cilium"}, + {Key: "dex", Name: "dex"}, + {Key: "external_secrets", Name: "external-secrets"}, + } + + if len(global) != len(expectedGlobal) { + t.Fatalf("expected %d global components, got %d: %v", len(expectedGlobal), len(global), global) + } + + for i, expected := range expectedGlobal { + if global[i].Key != expected.Key { + t.Errorf("global[%d].Key = %q, want %q", i, global[i].Key, expected.Key) + } + if global[i].Name != expected.Name { + t.Errorf("global[%d].Name = %q, want %q", i, global[i].Name, expected.Name) + } + } + + // Verify site components + expectedSite := []ComponentKey{ + {Key: "argo_events", Name: "argo-events"}, + {Key: "cilium", Name: "cilium"}, + {Key: "keystone", Name: "keystone"}, + {Key: "openstack", Name: "openstack"}, + {Key: "snmp_exporter", Name: "snmp-exporter"}, + {Key: "undersync", Name: "undersync"}, + } + + if len(site) != len(expectedSite) { + t.Fatalf("expected %d site components, got %d: %v", len(expectedSite), len(site), site) + } + + for i, expected := range expectedSite { + if site[i].Key != expected.Key { + t.Errorf("site[%d].Key = %q, want %q", i, site[i].Key, expected.Key) + } + if site[i].Name != expected.Name { + t.Errorf("site[%d].Name = %q, want %q", i, site[i].Name, expected.Name) + } + } +} + +func TestParseComponentsExcludesEnabled(t *testing.T) { + global, site, err := ParseComponents([]byte(sampleValuesYAML)) + if err != nil { + t.Fatalf("ParseComponents failed: %v", err) + } + + for _, c := range global { + if c.Key == "enabled" { + t.Error("global components should not include 'enabled'") + } + } + + for _, c := range site { + if c.Key == "enabled" { + t.Error("site components should not include 'enabled'") + } + } +} + +func TestParseComponentsUnderscoreToHyphen(t *testing.T) { + global, _, err := ParseComponents([]byte(sampleValuesYAML)) + if err != nil { + t.Fatalf("ParseComponents failed: %v", err) + } + + for _, c := range global { + if c.Key == "cert_manager" { + if c.Name != "cert-manager" { + t.Errorf("expected cert_manager -> cert-manager, got %q", c.Name) + } + return + } + } + t.Error("cert_manager not found in global components") +} + +func TestParseComponentsOpenStackIsComponent(t *testing.T) { + _, site, err := ParseComponents([]byte(sampleValuesYAML)) + if err != nil { + t.Fatalf("ParseComponents failed: %v", err) + } + + found := false + for _, c := range site { + if c.Key == "openstack" { + found = true + if c.Name != "openstack" { + t.Errorf("expected openstack -> openstack, got %q", c.Name) + } + break + } + } + if !found { + t.Error("openstack should be in site components") + } +} + +func TestParseComponentsEmptyInput(t *testing.T) { + global, site, err := ParseComponents([]byte("{}")) + if err != nil { + t.Fatalf("ParseComponents failed: %v", err) + } + if len(global) != 0 { + t.Errorf("expected 0 global components, got %d", len(global)) + } + if len(site) != 0 { + t.Errorf("expected 0 site components, got %d", len(site)) + } +} + +func TestParseComponentsInvalidYAML(t *testing.T) { + _, _, err := ParseComponents([]byte("not: valid: yaml: {{")) + if err == nil { + t.Error("expected error for invalid YAML") + } +} diff --git a/scripts/gitops-deploy.sh b/scripts/gitops-deploy.sh index 407bdc12f..826172c09 100755 --- a/scripts/gitops-deploy.sh +++ b/scripts/gitops-deploy.sh @@ -1,8 +1,16 @@ #!/bin/bash +# DEPRECATED: This script is deprecated in favor of understackctl deploy commands. +# Use instead: +# understackctl deploy init --type +# understackctl deploy update +# +# This script remains for backward compatibility but may be removed in a future release. + usage() { echo "$(basename "$0") " >&2 echo "" >&2 + echo "DEPRECATED: Use 'understackctl deploy init' and 'understackctl deploy update' instead" >&2 echo "Generates an initial layout of configs for deploying" >&2 exit 1 } @@ -50,18 +58,20 @@ if [ -z "${DEPLOY_NAME}" ]; then fi UC_REPO_COMPONENTS="${UC_REPO}/components" -UC_DEPLOY_HELM_CFG="${UC_DEPLOY}/${DEPLOY_NAME}/helm-configs" +UC_DEPLOY_MANIFESTS="${UC_DEPLOY}/${DEPLOY_NAME}/manifests" export DNS_ZONE export UC_DEPLOY_GIT_URL export DEPLOY_NAME -# create helm-configs directory for values.yaml overrides -mkdir -p "${UC_DEPLOY_HELM_CFG}" +# create manifests directory for component configs +mkdir -p "${UC_DEPLOY_MANIFESTS}" # shellcheck disable=SC2043 for component in "dex"; do - helmvals="${UC_DEPLOY_HELM_CFG}/${component}.yaml" + compdir="${UC_DEPLOY_MANIFESTS}/${component}" + helmvals="${compdir}/values.yaml" + mkdir -p "${compdir}" if [ -f "${helmvals}" ]; then echo "You have ${helmvals} already, not overwriting" continue