diff --git a/.gitignore b/.gitignore index ec50cd286e7b47974349d1a56c5007a19d99c58f..b7ffa54af779ca9f6b49a0a4cfd906973d6aa48e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ *.tgz *.xpkg -local +*local* argopw -argowftoken \ No newline at end of file +argowftoken + +dependencies/charts +dependencies/Chart.lock diff --git a/README.md b/README.md index f3b2bfe14be500df86d195ea674951905791ac54..54cf76ffea76297f97415d76edd73cc778248876 100644 --- a/README.md +++ b/README.md @@ -15,37 +15,115 @@ The two charts which need to be installed in the cluster: * `dependencies` installs and configures the required k8s controllers and CRDs * `resources` installs the required resources that implement provisioner logic -In the dependencies chart, ensure the appropriate `storageClassName` is set for your cluster. -In the resources chart, ensure the correct `kafkaEndpoint` is set. - <b>Note: Currently, the charts have to be installed as-is with all their listed dependencies. Do not try to replace a dependency with one that you already have running, such as using an already existing gitea instance. Decoupling of dependencies is still a work in progress!</b> -Before running helm install ensure that the following `secrets` are present: - * `ec-pull-secret` allows the installer or the argo workflows to pull the necessary images from code.europa.eu (EC_USERNAME: gitlab username, EC_PASSWORD: gitlab password or token) - * `ionos-provider` access token for the Ionos cloud API - * `gitea-secret` internal to the provisioner, used in the gitops process (will be removed in the future) +### Dependencies Install + +To install the dependencies module, you need to update the file `charts/dependencies/values.env.yaml` with the parameters to your environment: + + - GITEA_STORAGE_CLASS: the k8s storage class to create the gitea persistence volume; + - ARGO_CD_CRDS_INSTALL: install ArgoCD without CRDs, use this to avoid resource conflicts if installing in a cluster that already has an ArgoCD installation; + - GITEA_USERNAME: gitea admin user name; + - GITEA_PASSWORD: gitea admin user password; + +Execute the installation with the following commnands: ```cmd -NS=infrastructure -kubectl create -n $NS secret docker-registry "ec-pull-secret" --docker-server="code.europa.eu:4567" --docker-username="$EC_USERNAME" --docker-password="$EC_PASSWORD" -kubectl create -n $NS secret generic ionos-provider --from-literal=credentials="{\"token\":\"${IONOS_TOKEN}\"}" -kubectl create -n $NS secret generic gitea-secret --from-literal=username=gitops_test --from-literal=password=test1234 +helm dep up charts/dependencies +helm upgrade --install provisioner-dependencies --create-namespace -n $NS charts/dependencies -f charts/dependencies/values.yaml -f charts/dependencies/values.env.yaml ``` -## Local setup +### Resources Install + +To install the resources module, you need to update the file `charts/resources/values.env.yaml` with the parameters to you environment: + + - KAFKA_ENDPOINT: kafka broker endpoint url and port; + - KAFKA_USERNAME: kafka user name to connect on kafka broker; + - KAFKA_PASSWORD: kafka user password to connect on kafka broker; + - IONOS_TOKEN: IONOS API Token to be use by crossplane IONOS provider to create the cloud environments. + - GITEA_USERNAME: gitea admin user name; + - GITEA_PASSWORD: gitea admin user password; + +Execute the installation with the following commnands: +```cmd +helm upgrade --install provisioner-resources --create-namespace -n $NS charts/resources -f charts/resources/values.yaml -f charts/resources/values.env.yaml +``` + +### Accessing the Services Locally + +To access the ArgoCD UI for the provisioner, retrieve the initial admin password. Username is "admin" by default. -#### To setup locally, install [KinD](https://kind.sigs.k8s.io/) to setup a local K8s cluster. Then run the `local-setup.sh` script to setup your environment. -#### Accessing the UIs of the components: -Retrieve initial admin password for ArgoCD and Auth Token for ArgoWorkflows ```cmd -NS=infrastructure -echo "Bearer $(kubectl get -n $NS secret cli.service-account-token -o=jsonpath='{.data.token}' | base64 --decode)" > argowftoken kubectl get -n $NS secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d > argopw ``` -Port forward for each service +To access the ArgoWorkflows UI, ensure that the <b>cli</b> service account is enabled by setting ```cliEnabled: true``` in the resources helm chart values.yaml and retrieve the auth token. + +```cmd +echo "Bearer $(kubectl get -n $NS secret cli.service-account-token -o=jsonpath='{.data.token}' | base64 --decode)" > argowftoken +``` + +Port forward for each service: ```cmd -NS=infrastructure kubectl port-forward -n $NS svc/argocd-server 8888:443 kubectl port-forward -n $NS svc/argowf-argo-workflows-server 8777:2746 kubectl port-forward -n $NS svc/gitea-http 8333:3000 ``` + +### Setting up an environment for local development and testing +Local development and testing can be done using the [kind](https://kind.sigs.k8s.io/) tool. The following commands will create a local k8s cluster. The configuration will mount a local directory into the cluster for creating persistent volumes. + +```cmd +cat <<EOF >> $HOME/.config/.kind +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +name: default +nodes: + - role: control-plane + image: kindest/node:v1.31.1 + extraMounts: + - hostPath: $HOME/devtools/kind/default_volume + containerPath: /default +EOF +kind create cluster --config=$HOME/.config/.kind +``` + +Once the cluster has finalized creating, you can install the components using the `setup.sh` script. A successful install would look like this: +``` +Creating cluster "default" ... + â Ensuring node image (kindest/node:v1.31.1) đŧ + â Preparing nodes đĻ + â Writing configuration đ + â Starting control-plane đšī¸ + â Installing CNI đ + â Installing StorageClass đž +Set kubectl context to "kind-default" +You can now use your cluster with: + +kubectl cluster-info --context kind-default + +$ bash setup.sh + +namespace/infrastructure created +Release "dependencies" does not exist. Installing it now. +NAME: dependencies +LAST DEPLOYED: Mon Jan 27 15:22:21 2025 +NAMESPACE: infrastructure +STATUS: deployed +REVISION: 1 +pod/ionos-cloud-crossplane-provider-ionoscloud-6920f51664b9-7fg77ll condition met +Release "resources" does not exist. Installing it now. +W0127 15:24:23.858997 310697 warnings.go:70] metadata.finalizers: "resources-finalizer.argocd.argoproj.io": prefer a domain-qualified finalizer name to avoid accidental conflicts with other finalizer writers +NAME: resources +LAST DEPLOYED: Mon Jan 27 15:24:23 2025 +NAMESPACE: infrastructure +STATUS: deployed +REVISION: 1 +TEST SUITE: None +pod/dependencies-argocd-server-5f549f7d8b-g2khh condition met +pod/dependencies-gitea-6d99cc7b84-j4kc5 condition met +pod/dependencies-argo-workflows-server-5cfc85fc45-jj5hw condition met +Forwarding from 127.0.0.1:8333 -> 3000 +Forwarding from 127.0.0.1:8777 -> 2746 +Forwarding from 127.0.0.1:8888 -> 8080 +Handling connection for 8333 +``` \ No newline at end of file diff --git a/build_package.sh b/build_package.sh index 08c2d26523097f24594eb33765b87be12ea36460..8487c26c1ce22690f3c3f5091cf053461cdab108 100644 --- a/build_package.sh +++ b/build_package.sh @@ -1,4 +1,4 @@ # Builds the crossplane configuration package image and pushes it to EC gitlab -# VERSION=v0.3.9 +# VERSION=v0.3.10 crossplane xpkg build -f package/ -e package/examples/ -o configuration-${VERSION}.xpkg --verbose crossplane xpkg push code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/configuration:${VERSION} -f configuration-${VERSION}.xpkg --domain https://code.europa.eu \ No newline at end of file diff --git a/charts/.helmignore b/charts/.helmignore new file mode 100644 index 0000000000000000000000000000000000000000..1535abfe9f8a7c764b1c9bcfc7e9fdd5a326287f --- /dev/null +++ b/charts/.helmignore @@ -0,0 +1,2 @@ +values.env.yaml +dependencies \ No newline at end of file diff --git a/charts/Chart.yaml b/charts/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3cb5472ef7f020d23fc5c6e6c95781475046ea34 --- /dev/null +++ b/charts/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: infrastructure-crossplane +version: ${PROJECT_RELEASE_VERSION} +appVersion: "${PROJECT_RELEASE_VERSION}" diff --git a/charts/dependencies/Chart.lock b/charts/dependencies/Chart.lock deleted file mode 100644 index 6a940d5f7df074ac417fdfebc331480f0ace7eba..0000000000000000000000000000000000000000 --- a/charts/dependencies/Chart.lock +++ /dev/null @@ -1,18 +0,0 @@ -dependencies: -- name: crossplane - repository: https://charts.crossplane.io/stable - version: 1.16.2 -- name: argo-cd - repository: https://argoproj.github.io/argo-helm - version: 6.7.13 -- name: argo-events - repository: https://argoproj.github.io/argo-helm - version: 2.4.8 -- name: argo-workflows - repository: https://argoproj.github.io/argo-helm - version: 0.42.5 -- name: gitea - repository: https://dl.gitea.com/charts - version: 10.4.1 -digest: sha256:45897c92d11fd234df71398dc2b5775f46a36c53bd4772f6d6ef9e54367013f6 -generated: "2024-12-10T23:20:09.55863639+02:00" diff --git a/charts/dependencies/templates/secret.yaml b/charts/dependencies/templates/secret.yaml new file mode 100644 index 0000000000000000000000000000000000000000..855724c7b6b9561418491430390926b467d82dce --- /dev/null +++ b/charts/dependencies/templates/secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: gitea-secret +type: Opaque +data: + username: {{ .Values.secrets.gitea.username | b64enc }} + password: {{ .Values.secrets.gitea.password | b64enc }} diff --git a/charts/dependencies/values.env.yaml b/charts/dependencies/values.env.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dc88b06d866c39c000abe88f0c5a1b5de2427a4a --- /dev/null +++ b/charts/dependencies/values.env.yaml @@ -0,0 +1,12 @@ +gitea: + storageClassName: #GITEA_STORAGE_CLASS + volumeSize: #GITEA_VOLUME_SIZE + +argo-cd: + crds: + install: true #ARGO_CD_CRDS_INSTALL + +secrets: + gitea: + username: #GITEA_USERNAME + password: #GITEA_PASSWORD diff --git a/charts/dependencies/values.yaml b/charts/dependencies/values.yaml index d0c50eddd295ce00bce0e67624cc526788f0cf5b..4ed09b9e8147a9365df743fd543c199448af7cb0 100644 --- a/charts/dependencies/values.yaml +++ b/charts/dependencies/values.yaml @@ -1,14 +1,9 @@ crossplane: - imagePullSecrets: - - ec-pull-secret configuration: packages: - - code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/configuration:v0.3.9 + - code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/configuration:v0.3.10 gitea: - # storageClassName: standard - storageClassName: csi-cinder-high-speed - volumeSize: 100G service: http: type: ClusterIP @@ -41,9 +36,7 @@ gitea: queue: TYPE: level admin: - username: "gitops_test" - password: "test1234" - email: "test@gitops.email.com" + existingSecret: gitea-secret argo-cd: server: @@ -57,8 +50,6 @@ argo-cd: enabled: false applicationSet: enabled: false - # Health check for crossplane MRs: - # https://docs.crossplane.io/latest/guides/crossplane-with-argo-cd/#set-health-status configs: cm: timeout.reconciliation: 180s @@ -227,4 +218,4 @@ argo-cd: end end - return health_status \ No newline at end of file + return health_status diff --git a/charts/resources/Chart.yaml b/charts/resources/Chart.yaml deleted file mode 100644 index c64cb7aa1befc6c88f134206835b9915405fc8b9..0000000000000000000000000000000000000000 --- a/charts/resources/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v2 -name: provisioner-resources -description: Chart which installs resources of the Provisioner -version: 0.1.0 diff --git a/charts/resources/templates/cli.yaml b/charts/resources/templates/cli.yaml deleted file mode 100644 index dab3b7759ecfbb9a7ec2c011ff2c91b5d918a790..0000000000000000000000000000000000000000 --- a/charts/resources/templates/cli.yaml +++ /dev/null @@ -1,187 +0,0 @@ -{{- if .Values.cliEnabled }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cli - namespace: {{ .Release.Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: cli - namespace: {{ .Release.Namespace }} -rules: - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - update - - apiGroups: - - "" - resources: - - pods - - pods/exec - verbs: - - create - - get - - list - - watch - - update - - patch - - delete - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - watch - - list - - apiGroups: - - "" - resources: - - persistentvolumeclaims - - persistentvolumeclaims/finalizers - verbs: - - create - - update - - delete - - get - - apiGroups: - - argoproj.io - resources: - - workflows - - workflows/finalizers - - workflowtasksets - - workflowtasksets/finalizers - - workflowartifactgctasks - verbs: - - get - - list - - watch - - update - - patch - - delete - - create - - apiGroups: - - argoproj.io - resources: - - workflowtemplates - - workflowtemplates/finalizers - verbs: - - get - - list - - watch - - apiGroups: - - argoproj.io - resources: - - workflowtaskresults - verbs: - - list - - watch - - deletecollection - - apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - list - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - apiGroups: - - argoproj.io - resources: - - cronworkflows - - cronworkflows/finalizers - verbs: - - get - - list - - watch - - update - - patch - - delete - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - "policy" - resources: - - poddisruptionbudgets - verbs: - - create - - get - - delete ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: cli - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: cli -subjects: -- kind: ServiceAccount - name: cli - namespace: {{ .Release.Namespace }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: operate-workflow-role - namespace: {{ .Release.Namespace }} -rules: - - apiGroups: - - argoproj.io - verbs: - - "*" - resources: - - workflows - - workflowtemplates - - cronworkflows - - clusterworkflowtemplates ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: operate-workflow-role-binding - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: operate-workflow-role -subjects: - - kind: ServiceAccount - name: {{ .Values.workflowOperatorSA }} - namespace: {{ .Release.Namespace }} ---- -apiVersion: v1 -kind: Secret -metadata: - namespace: {{ .Release.Namespace }} - name: cli.service-account-token - annotations: - kubernetes.io/service-account.name: cli -type: kubernetes.io/service-account-token ---- -apiVersion: v1 -kind: Secret -metadata: - name: argocli.service-account-token - annotations: - kubernetes.io/service-account.name: argocli -type: kubernetes.io/service-account-token -{{- end }} \ No newline at end of file diff --git a/charts/resources/templates/events/provision/status-sensor.yaml b/charts/resources/templates/events/provision/status-sensor.yaml deleted file mode 100644 index 349feb598229ea3d4405c999596ca8964c04cd81..0000000000000000000000000000000000000000 --- a/charts/resources/templates/events/provision/status-sensor.yaml +++ /dev/null @@ -1,199 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Sensor -metadata: - name: provision-status - namespace: {{ .Release.Namespace }} -spec: - eventBusName: provisioner-eventbus - template: - serviceAccountName: {{ .Values.workflowOperatorSA }} - dependencies: - - name: message - eventSourceName: provision-status - eventName: provisionStatus - triggers: - - template: - name: argo-workflow-trigger - argoWorkflow: - operation: submit - source: - resource: - apiVersion: argoproj.io/v1alpha1 - kind: Workflow - metadata: - generateName: provision-status- - namespace: {{ .Release.Namespace }} - spec: - podGC: - strategy: OnPodCompletion - deleteDelayDuration: 120s - serviceAccountName: cli - entrypoint: main - workflowMetadata: - arguments: - parameters: - - name: body - value: default-is-overriden - templates: - - name: main - steps: - - - name: validate - template: validate-status - arguments: - parameters: - - name: message - value: "{{`{{workflow.parameters.body}}`}}" - - - name: label - template: done-label - arguments: - parameters: - - name: UUID - value: "{{`{{steps.validate.outputs.result}}`}}" - - - name: parse-application - template: parse-application - arguments: - parameters: - - name: resource - value: "{{`{{steps.label.outputs.parameters.application-resource}}`}}" - - - name: parse-claim - template: parse-claim - arguments: - parameters: - - name: claim-reference - value: "{{`{{steps.parse-application.outputs.result}}`}}" - - - name: payload - template: payload - arguments: - parameters: - - name: resource - value: "{{`{{steps.parse-claim.outputs.parameters.claim-resource}}`}}" - - - name: message - template: message - arguments: - parameters: - - name: payload - value: "{{`{{steps.payload.outputs.result}}`}}" - - name: validate-status - inputs: - parameters: - - name: message - script: - image: python:alpine3.10 - command: [python] - source: | - import json - rawstr = r'''{{`{{inputs.parameters.message}}`}}''' - d = json.loads(rawstr) - try: - app_status = d['status'] - app_meta = d['metadata'] - app_health = app_status['health']['status'] - app_sync = app_status['sync']['status'] - # Application only tracks the Claim, so one resource should be present - claim = app_status['resources'][0] - claim_health = claim['health']['status'] - claim_sync = claim['status'] - claim_id = app_meta['labels']['claim-uuid'] - assert app_health == "Healthy" and claim_health == "Healthy" and app_sync == "Synced" and claim_sync == "Synced" - print(f"{claim_id}") - except (KeyError, IndexError): - raise ValueError("Not all status fields available") - - name: done-label - inputs: - parameters: - - name: UUID - script: - image: bitnami/kubectl:latest - command: [sh] - source: | - echo "patching application with label claim-uuid:{{`{{inputs.parameters.UUID}}`}}" - kubectl label -n {{ .Release.Namespace }} applications crossplane-claim-{{`{{inputs.parameters.UUID}}`}} provisioning-status="finalized" - kubectl get -n {{ .Release.Namespace }} applications --selector=claim-uuid={{`{{inputs.parameters.UUID}}`}} -o json > /tmp/resource.json - outputs: - parameters: - - name: application-resource - valueFrom: - path: /tmp/resource.json - - name: parse-application - inputs: - parameters: - - name: resource - script: - image: python:alpine3.10 - command: [python] - source: | - import json - rawstr = r'''{{`{{inputs.parameters.resource}}`}}''' - d = json.loads(rawstr) - claim_kind = d['items'][0]['metadata']['labels']['claim-kind'] - claim_selector = d['items'][0]['metadata']['labels']['claim-uuid'] - print(f"{claim_kind} --selector=uuid={claim_selector}") - - name: parse-claim - inputs: - parameters: - - name: claim-reference - script: - image: bitnami/kubectl:latest - command: [sh] - source: | - kubectl get -n {{ .Release.Namespace }} {{`{{inputs.parameters.claim-reference}}`}} - kubectl get -n {{ .Release.Namespace }} -o json {{`{{inputs.parameters.claim-reference}}`}} > /tmp/resource.json - outputs: - parameters: - - name: claim-resource - valueFrom: - path: /tmp/resource.json - - name: payload - inputs: - parameters: - - name: resource - script: - image: python:alpine3.10 - command: [python] - source: | - import json - rawstr = r'''{{`{{inputs.parameters.resource}}`}}''' - d = json.loads(rawstr) - payload = {} - payload["vmIps"] = d["items"][0]["status"]["vmIps"] - payload["status"] = "Provisioning finalized successfully" - payload["scriptTriggerId"] = d["items"][0]["metadata"]["labels"]["uuid"] - print(json.dumps(payload).replace('"', '\\"')) - - name: message - inputs: - parameters: - - name: payload - script: - {{- if .Values.kafkaAuth.enable }} - env: - - name: MECHANISM - value: {{ .Values.kafkaAuth.mechanism }} - - name: USERNAME - valueFrom: - secretKeyRef: - name: {{ .Values.kafkaAuth.secretName }} - key: username - - name: PASSWORD - valueFrom: - secretKeyRef: - name: {{ .Values.kafkaAuth.secretName }} - key: password - {{- end }} - image: confluentinc/cp-kafkacat:7.1.14 - command: [sh] - {{- if .Values.kafkaAuth.enable }} - source: | - echo {{`{{inputs.parameters.payload}}`}} | kafkacat -P -b {{ .Values.kafkaEndpoint }} -X security.protocol=SASL_PLAINTEXT -X sasl.username="$USERNAME" -X sasl.password="$PASSWORD" -X sasl.mechanism="$MECHANISM" -t provisioned -J - {{- else }} - source: | - echo {{`{{inputs.parameters.payload}}`}} | kafkacat -P -b {{ .Values.kafkaEndpoint }} -t provisioned -J - {{- end }} - - parameters: - - src: - dependencyName: message - dataKey: body - dest: spec.arguments.parameters.0.value - retryStrategy: - steps: 2 - duration: 30s \ No newline at end of file diff --git a/charts/resources/values.yaml b/charts/resources/values.yaml deleted file mode 100644 index 476211566736ee0ab41a4f65ce9665c49d06db4f..0000000000000000000000000000000000000000 --- a/charts/resources/values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -workflowOperatorSA: operate-workflow -applicationStatusViewerSA: claim-application-status -cliEnabled: true -dependenciesReleaseName: provisioner-dependencies -# kafkaEndpoint: kafka:9092 -kafkaEndpoint: kafka.infrastructure.dev.simpl-europe.eu:9092 -kafkaAuth: - enable: true - mechanism: PLAIN - secretName: kafka-secret diff --git a/charts/resources/templates/claim-manager.yaml b/charts/templates/argo/claim-manager.yaml similarity index 69% rename from charts/resources/templates/claim-manager.yaml rename to charts/templates/argo/claim-manager.yaml index 2e245634f98786cd108861f205be452a9d25dd01..9aa0c2d38fa3c7db6c64d45de77dd5b49d4676ce 100644 --- a/charts/resources/templates/claim-manager.yaml +++ b/charts/templates/argo/claim-manager.yaml @@ -7,7 +7,7 @@ metadata: spec: project: default source: - repoURL: http://{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace}}.svc.cluster.local:3000/gitops_test/management-repo.git + repoURL: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/management-repo.git path: applications targetRevision: master directory: @@ -18,4 +18,4 @@ spec: automated: selfHeal: true prune: true - allowEmpty: true \ No newline at end of file + allowEmpty: true diff --git a/charts/dependencies/templates/post-install-cm.yaml b/charts/templates/configmap.yaml similarity index 71% rename from charts/dependencies/templates/post-install-cm.yaml rename to charts/templates/configmap.yaml index a74435860f4c97d4d0eda9b287ef687f307fa736..298fbbb3256e3a490f3d79266038026d6600360a 100644 --- a/charts/dependencies/templates/post-install-cm.yaml +++ b/charts/templates/configmap.yaml @@ -1,19 +1,19 @@ apiVersion: v1 kind: ConfigMap metadata: - name: {{ .Release.Name }}-postinstall-configmap + name: gitea-setup-job data: setup-gitea.sh: | set -euo pipefail apk add --no-cache curl - GITEA_HEALTH="http://{{ .Release.Name }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/api/healthz" + GITEA_HEALTH="http://{{ .Values.giteaUrl }}/api/healthz" curl -v "$GITEA_HEALTH" git config --global user.email "setup@helm-hook.kube"; git config --global user.name "${GIT_USER}"; DIR1="management-repo" - MGMT_REPO="http://${GIT_USER}:${GIT_PASSWORD}@{{ .Release.Name }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/management-repo.git" + MGMT_REPO="http://${GIT_USER}:${GIT_PASSWORD}@{{ .Values.giteaUrl }}/${GIT_USER}/management-repo.git" echo "Creating directory: $DIR1" mkdir -p "$DIR1" cd "$DIR1" @@ -30,7 +30,7 @@ data: cd .. DIR2="data-repo" - DATA_REPO="http://${GIT_USER}:${GIT_PASSWORD}@{{ .Release.Name }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git" + DATA_REPO="http://${GIT_USER}:${GIT_PASSWORD}@{{ .Values.giteaUrl }}/${GIT_USER}/data-repo.git" echo "Creating directory: $DIR2" mkdir -p "$DIR2" cd "$DIR2" @@ -57,7 +57,7 @@ data: spec: project: default source: - repoURL: http://{{ .Release.Name }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git + repoURL: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/data-repo.git path: claims/claim_{UUID} targetRevision: master destination: @@ -66,4 +66,4 @@ data: automated: selfHeal: true prune: true - allowEmpty: true \ No newline at end of file + allowEmpty: true diff --git a/charts/resources/templates/crossplane/provider-ionos-config.yaml b/charts/templates/crossplane/provider-ionos-config.yaml similarity index 84% rename from charts/resources/templates/crossplane/provider-ionos-config.yaml rename to charts/templates/crossplane/provider-ionos-config.yaml index 9a8d01159d8fc167403803186846acacb5a85524..52c688539867f61171fb01be6bb7f861dfb25fa0 100644 --- a/charts/resources/templates/crossplane/provider-ionos-config.yaml +++ b/charts/templates/crossplane/provider-ionos-config.yaml @@ -1,7 +1,7 @@ apiVersion: ionoscloud.crossplane.io/v1alpha1 kind: ProviderConfig metadata: - name: example + name: {{ .Release.Namespace }}-ionos-pc spec: credentials: source: Secret diff --git a/charts/resources/templates/events/decommission/sensor.yaml b/charts/templates/events/decommission/sensor.yaml similarity index 80% rename from charts/resources/templates/events/decommission/sensor.yaml rename to charts/templates/events/decommission/sensor.yaml index ca7506e5f8cde91999f2a38b9738fad21c39c702..2b9bbcc83ee6939fb4fab92e14481fa5022315d1 100644 --- a/charts/resources/templates/events/decommission/sensor.yaml +++ b/charts/templates/events/decommission/sensor.yaml @@ -6,13 +6,16 @@ metadata: spec: eventBusName: provisioner-eventbus template: - serviceAccountName: {{ .Values.workflowOperatorSA }} + serviceAccountName: provisioner-events dependencies: - name: message eventSourceName: decommission eventName: decommissionRequest triggers: - - template: + - rateLimit: + unit: Second + requestsPerUnit: 1 + template: name: argo-workflow-trigger argoWorkflow: operation: submit @@ -27,17 +30,15 @@ spec: podGC: strategy: OnWorkflowSuccess deleteDelayDuration: 120s - imagePullSecrets: - - name: ec-pull-secret volumes: - name: repos emptyDir: {} - serviceAccountName: cli + serviceAccountName: provisioner-workflows entrypoint: main workflowMetadata: labels: track-workflow: "true" - workflow-type: to-decommission + workflow-type: {{ .Values.kafkaDecommissioningRequestsTopicName }} labelsFrom: scriptTriggerId: expression: workflow.parameters.scriptTriggerId @@ -52,12 +53,12 @@ spec: - name: data-repo path: repos/data git: - repo: http://{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git + repo: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/data-repo.git revision: "master" - name: management-repo path: /repos/management git: - repo: http://{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/management-repo.git + repo: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/management-repo.git revision: "master" parameters: - name: scriptTriggerId @@ -83,17 +84,17 @@ spec: echo ---ls ../repos---; ls ../repos; git config --global user.email "workflow@argo.kube"; - git config --global user.name "gitops_test"; + git config --global user.name "${GIT_USER}"; echo ---commit data changes---; git -C ../repos/data checkout master; git -C ../repos/data rm -r claims/claim_{{`{{inputs.parameters.scriptTriggerId}}`}}; git -C ../repos/data commit -v -m "Remove claim_{{`{{inputs.parameters.scriptTriggerId}}`}}"; - git -C ../repos/data push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git; + git -C ../repos/data push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.giteaUrl }}/${GIT_USER}/data-repo.git; echo ---commit management changes---; git -C ../repos/management checkout master; git -C ../repos/management rm -r applications/application_{{`{{inputs.parameters.scriptTriggerId}}`}}; git -C ../repos/management commit -v -m "Remove application_{{`{{inputs.parameters.scriptTriggerId}}`}}"; - git -C ../repos/management push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/management-repo.git; + git -C ../repos/management push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.giteaUrl }}/${GIT_USER}/management-repo.git; '] volumeMounts: - name: repos @@ -105,6 +106,3 @@ spec: dataKey: headers.scriptTriggerId value: "" dest: spec.arguments.parameters.0.value - retryStrategy: - steps: 2 - duration: 30s \ No newline at end of file diff --git a/charts/resources/templates/events/decommission/source.yaml b/charts/templates/events/decommission/source.yaml similarity index 69% rename from charts/resources/templates/events/decommission/source.yaml rename to charts/templates/events/decommission/source.yaml index d005e94709f32aa325440df80d1e007d8640a2dd..82958092a268c8a993c448161587c18b7a673afb 100644 --- a/charts/resources/templates/events/decommission/source.yaml +++ b/charts/templates/events/decommission/source.yaml @@ -8,17 +8,17 @@ spec: kafka: decommissionRequest: url: {{ .Values.kafkaEndpoint }} - topic: to-decommission + topic: {{ .Values.kafkaDecommissioningRequestsTopicName }} jsonBody: false partition: "0" - {{- if .Values.kafkaAuth.enable }} + {{- if .Values.kafkaAuthEnable }} sasl: - mechanism: {{ .Values.kafkaAuth.mechanism }} + mechanism: {{ .Values.kafkaAuthMechanism }} userSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: username passwordSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: password {{- end }} connectionBackoff: diff --git a/charts/resources/templates/events/decommission/status-sensor.yaml b/charts/templates/events/decommission/status-sensor.yaml similarity index 80% rename from charts/resources/templates/events/decommission/status-sensor.yaml rename to charts/templates/events/decommission/status-sensor.yaml index fbd61f637ac36afad9760fa17080e3825b258790..f6d2f9e208ed1a3cb8c1ce6d196194afa3e8c3b3 100644 --- a/charts/resources/templates/events/decommission/status-sensor.yaml +++ b/charts/templates/events/decommission/status-sensor.yaml @@ -14,15 +14,15 @@ spec: name: kafka kafka: url: {{ .Values.kafkaEndpoint }} - topic: decommissioned - {{- if .Values.kafkaAuth.enable }} + topic: {{ .Values.kafkaDecommissioningResponsesTopicName }} + {{- if .Values.kafkaAuthEnable }} sasl: - mechanism: {{ .Values.kafkaAuth.mechanism }} + mechanism: {{ .Values.kafkaAuthMechanism }} userSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: username passwordSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: password {{- end }} payload: diff --git a/charts/resources/templates/events/decommission/status-source.yaml b/charts/templates/events/decommission/status-source.yaml similarity index 89% rename from charts/resources/templates/events/decommission/status-source.yaml rename to charts/templates/events/decommission/status-source.yaml index cd136e742856549e1ae621f277114cc4e1da9e35..97bd1b9e08e81b5920a0cb34a8138807c113b452 100644 --- a/charts/resources/templates/events/decommission/status-source.yaml +++ b/charts/templates/events/decommission/status-source.yaml @@ -6,7 +6,7 @@ metadata: spec: eventBusName: provisioner-eventbus template: - serviceAccountName: {{ .Values.applicationStatusViewerSA }} + serviceAccountName: provisioner-events resource: decommissionStatus: namespace: {{ .Release.Namespace }} diff --git a/charts/resources/templates/events/eventbus.yaml b/charts/templates/events/eventbus.yaml similarity index 100% rename from charts/resources/templates/events/eventbus.yaml rename to charts/templates/events/eventbus.yaml diff --git a/charts/resources/templates/events/provision/gitops-status-sensor.yaml b/charts/templates/events/provision/gitops-status-sensor.yaml similarity index 86% rename from charts/resources/templates/events/provision/gitops-status-sensor.yaml rename to charts/templates/events/provision/gitops-status-sensor.yaml index 4dfb54db706c5ca95a900901b619aab3e9fa1ce5..8f49d7fcebc6b0ee6f9e03949a1aa40fb98da458 100644 --- a/charts/resources/templates/events/provision/gitops-status-sensor.yaml +++ b/charts/templates/events/provision/gitops-status-sensor.yaml @@ -14,16 +14,16 @@ spec: name: kafka kafka: url: {{ .Values.kafkaEndpoint }} - topic: provisioned + topic: {{ .Values.kafkaProvisioningResponsesTopicName }} partition: 0 - {{- if .Values.kafkaAuth.enable }} + {{- if .Values.kafkaAuthEnable }} sasl: - mechanism: {{ .Values.kafkaAuth.mechanism }} + mechanism: {{ .Values.kafkaAuthMechanism }} userSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: username passwordSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: password {{- end }} payload: diff --git a/charts/resources/templates/events/provision/gitops-status-source.yaml b/charts/templates/events/provision/gitops-status-source.yaml similarity index 91% rename from charts/resources/templates/events/provision/gitops-status-source.yaml rename to charts/templates/events/provision/gitops-status-source.yaml index c1b0fe4580714f683cac5d68b60bcfc396469d75..d664ad586e0e341f1e0d5deb594c43ae2e7f936c 100644 --- a/charts/resources/templates/events/provision/gitops-status-source.yaml +++ b/charts/templates/events/provision/gitops-status-source.yaml @@ -6,7 +6,7 @@ metadata: spec: eventBusName: provisioner-eventbus template: - serviceAccountName: {{ .Values.workflowOperatorSA }} + serviceAccountName: provisioner-events resource: provisionGitops: namespace: {{ .Release.Namespace }} diff --git a/charts/resources/templates/events/provision/sensor.yaml b/charts/templates/events/provision/sensor.yaml similarity index 79% rename from charts/resources/templates/events/provision/sensor.yaml rename to charts/templates/events/provision/sensor.yaml index 704cc81550aa27b7456d91b7bf47898671b907ed..7712a33e213f966ea5236d201eadb51b0ed5b78a 100644 --- a/charts/resources/templates/events/provision/sensor.yaml +++ b/charts/templates/events/provision/sensor.yaml @@ -6,13 +6,16 @@ metadata: spec: eventBusName: provisioner-eventbus template: - serviceAccountName: {{ .Values.workflowOperatorSA }} + serviceAccountName: provisioner-events dependencies: - name: message eventSourceName: provision eventName: provisionRequest triggers: - - template: + - rateLimit: + unit: Second + requestsPerUnit: 1 + template: name: argo-workflow-trigger argoWorkflow: operation: submit @@ -27,12 +30,10 @@ spec: podGC: strategy: OnWorkflowSuccess deleteDelayDuration: 30s - imagePullSecrets: - - name: ec-pull-secret volumes: - name: repos emptyDir: {} - serviceAccountName: cli + serviceAccountName: provisioner-workflows entrypoint: main workflowMetadata: labels: @@ -56,12 +57,12 @@ spec: - name: data-repo path: repos/data git: - repo: http://{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git + repo: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/data-repo.git revision: "master" - name: management-repo path: /repos/management git: - repo: http://{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/management-repo.git + repo: http://{{ .Values.giteaUrl }}/{{ .Values.secrets.gitea.username }}/management-repo.git revision: "master" parameters: - name: headers @@ -71,7 +72,7 @@ spec: - name: body value: {{`"'{{workflow.parameters.body}}'"`}} container: - image: code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/to-provision-workflow:v0.2.1 + image: {{ .Values.provisionWorkflowImage }} env: - name: GIT_USER valueFrom: @@ -90,18 +91,17 @@ spec: ls; echo ---ls ../repos---; ls ../repos; - python doesntexist.py; python main.py {{`{{inputs.parameters.scriptTriggerId}}`}} {{`{{inputs.parameters.body}}`}} || exit $?; git config --global user.email "workflow@argo.kube"; - git config --global user.name "gitops_test"; + git config --global user.name "${GIT_USER}"; echo ---commit data changes---; git -C ../repos/data checkout master; git -C ../repos/data/claims add -A && git -C ../repos/data commit -m "add UUID {{`{{inputs.parameters.scriptTriggerId}}`}}"; - git -C ../repos/data push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/data-repo.git; + git -C ../repos/data push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.giteaUrl }}/${GIT_USER}/data-repo.git; echo ---commit management changes---; git -C ../repos/management checkout master; git -C ../repos/management/applications add -A && git -C ../repos/management commit -m "add UUID {{`{{inputs.parameters.scriptTriggerId}}`}}"; - git -C ../repos/management push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.dependenciesReleaseName }}-gitea-http.{{ .Release.Namespace }}.svc.cluster.local:3000/gitops_test/management-repo.git; + git -C ../repos/management push http://$GIT_USER:$GIT_PASSWORD@{{ .Values.giteaUrl }}/${GIT_USER}/management-repo.git; '] volumeMounts: - name: repos @@ -120,7 +120,4 @@ spec: - src: dependencyName: message dataKey: body - dest: spec.arguments.parameters.2.value - retryStrategy: - steps: 2 - duration: 30s \ No newline at end of file + dest: spec.arguments.parameters.2.value \ No newline at end of file diff --git a/charts/resources/templates/events/provision/source.yaml b/charts/templates/events/provision/source.yaml similarity index 69% rename from charts/resources/templates/events/provision/source.yaml rename to charts/templates/events/provision/source.yaml index fd56ff4c6078aa76e122b32317493a017118e96c..7daf933ac7d60ee6c6c223fd06ed94f1d252f94c 100644 --- a/charts/resources/templates/events/provision/source.yaml +++ b/charts/templates/events/provision/source.yaml @@ -8,17 +8,17 @@ spec: kafka: provisionRequest: url: {{ .Values.kafkaEndpoint }} - topic: to-provision + topic: {{ .Values.kafkaProvisioningRequestsTopicName }} jsonBody: false partition: "0" - {{- if .Values.kafkaAuth.enable }} + {{- if .Values.kafkaAuthEnable }} sasl: - mechanism: {{ .Values.kafkaAuth.mechanism }} + mechanism: {{ .Values.kafkaAuthMechanism }} userSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: username passwordSecret: - name: {{ .Values.kafkaAuth.secretName }} + name: kafka-secret key: password {{- end }} connectionBackoff: diff --git a/charts/templates/events/provision/status-sensor.yaml b/charts/templates/events/provision/status-sensor.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1b80b82f5a5c3bffb9750233911d18b2b975ab0c --- /dev/null +++ b/charts/templates/events/provision/status-sensor.yaml @@ -0,0 +1,279 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Sensor +metadata: + name: provision-status + namespace: {{ .Release.Namespace }} +spec: + eventBusName: provisioner-eventbus + template: + serviceAccountName: provisioner-events + dependencies: + - name: message + eventSourceName: provision-status + eventName: provisionStatus + triggers: + - rateLimit: + unit: Second + requestsPerUnit: 1 + template: + name: argo-workflow-trigger + argoWorkflow: + operation: submit + source: + resource: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: provision-status- + namespace: {{ .Release.Namespace }} + spec: + podGC: + strategy: OnWorkflowSuccess + deleteDelayDuration: 180s + serviceAccountName: provisioner-workflows + entrypoint: main + arguments: + parameters: + - name: body + value: default-body + templates: + - name: main + retryStrategy: + limit: "5" + backoff: + duration: "3m" + steps: + - - name: get-tracking-metadata + template: get-tracking-metadata + arguments: + parameters: + - name: message + value: "{{`{{workflow.parameters.body}}`}}" + - - name: get-application-resource + template: get-application-resource + arguments: + parameters: + - name: uuid + value: "{{`{{steps.get-tracking-metadata.outputs.parameters.uuid}}`}}" + - - name: validate-application-status + template: validate-application-status + arguments: + parameters: + - name: application + value: "{{`{{steps.get-application-resource.outputs.parameters.application}}`}}" + - name: uuid + value: "{{`{{steps.get-tracking-metadata.outputs.parameters.uuid}}`}}" + - - name: get-claim-resource + template: get-claim-resource + arguments: + parameters: + - name: kind + value: "{{`{{steps.get-tracking-metadata.outputs.parameters.kind}}`}}" + - name: uuid + value: "{{`{{steps.get-tracking-metadata.outputs.parameters.uuid}}`}}" + - - name: validate-claim-status + template: validate-claim-status + arguments: + parameters: + - name: claim + value: "{{`{{steps.get-claim-resource.outputs.parameters.claim}}`}}" + - name: uuid + value: "{{`{{steps.get-tracking-metadata.outputs.parameters.uuid}}`}}" + - - name: send-message + template: send-message + arguments: + parameters: + - name: message-body + value: "{{`{{steps.validate-claim-status.outputs.result}}`}}" + - name: get-tracking-metadata + inputs: + parameters: + - name: message + outputs: + parameters: + - name: kind + valueFrom: + path: /tmp/claim-kind + - name: uuid + valueFrom: + path: /tmp/claim-uuid + script: + image: python:alpine3.10 + command: [python] + source: | + import json + LOGFILE = '/dev/termination-log' + def write_termination_log(msg, echo_stdout=True): + if echo_stdout: + print(msg) + with open(LOGFILE, mode="a", encoding="utf-8") as logfile: + print(msg, file=logfile) + SAVE_KIND = '/tmp/claim-kind' + SAVE_UUID = '/tmp/claim-uuid' + rawstr = r'''{{`{{inputs.parameters.message}}`}}''' + d = json.loads(rawstr) + uuid = None + kind = None + try: + uuid = d['metadata']['labels']['claim-uuid'] + kind = d['metadata']['labels']['claim-kind'] + print(f"Kind:{kind}") + print(f"UUID:{uuid}") + except KeyError: + write_termination_log('Missing required Application metadata labels: claim-uuid, claim-kind. Verify the deployment script.') + raise ValueError('Missing required Application metadata labels.') + with open(SAVE_KIND, mode="w", encoding="utf-8") as output: + output.write(kind) + with open(SAVE_UUID, mode="w", encoding="utf-8") as output: + output.write(uuid) + - name: get-application-resource + inputs: + parameters: + - name: uuid + script: + image: bitnami/kubectl:latest + command: [sh] + source: | + # Get the Application after the initial burst of events. + sleep 5 + echo "Retrieving Application with label claim-uuid: {{`{{inputs.parameters.uuid}}`}}" + kubectl get -n {{ .Release.Namespace }} applications --selector=claim-uuid={{`{{inputs.parameters.uuid}}`}} -o json > /tmp/application.json + outputs: + parameters: + - name: application + valueFrom: + path: /tmp/application.json + - name: validate-application-status + inputs: + parameters: + - name: application + - name: uuid + script: + image: python:alpine3.10 + command: [python] + source: | + import json + LOGFILE = '/dev/termination-log' + def write_termination_log(msg, echo_stdout=True): + if echo_stdout: + print(msg) + with open(LOGFILE, mode="a", encoding="utf-8") as logfile: + print(msg, file=logfile) + rawstr = r'''{{`{{inputs.parameters.application}}`}}''' + uuid = r'''{{`{{inputs.parameters.uuid}}`}}''' + d = json.loads(rawstr) + if not len(d["items"]): + raise ValueError(f"Application (UUID: {uuid}) not found") + resource = d["items"][0] + app_sync = None + app_health = None + try: + app_sync = resource['status']['sync']['status'] + app_health = resource['status']['health']['status'] + except KeyError: + missing = "sync" if not app_sync else "sync, health" + write_termination_log(f"Missing required Application (UUID:{uuid}) status fields: {missing}") + raise ValueError("missing status fields") + try: + assert app_health == "Healthy" and app_sync == "Synced" + except AssertionError: + write_termination_log(f"Application (UUID:{uuid}) not ready yet. Sync:{app_sync} Health:{app_health}") + raise ValueError("application not ready") + - name: get-claim-resource + inputs: + parameters: + - name: kind + - name: uuid + script: + image: bitnami/kubectl:latest + command: [sh] + source: | + echo "retrieving Crossplane Claim of kind: {{`{{inputs.parameters.kind}}`}} with label claim-uuid: {{`{{inputs.parameters.uuid}}`}}" + kubectl get -n {{ .Release.Namespace }} {{`{{inputs.parameters.kind}}`}} --selector=uuid={{`{{inputs.parameters.uuid}}`}} -o json > /tmp/claim.json + outputs: + parameters: + - name: claim + valueFrom: + path: /tmp/claim.json + - name: validate-claim-status + inputs: + parameters: + - name: claim + - name: uuid + script: + image: python:alpine3.10 + command: [python] + source: | + import json + LOGFILE = '/dev/termination-log' + def write_termination_log(msg, echo_stdout=True): + if echo_stdout: + print(msg) + with open(LOGFILE, mode="a", encoding="utf-8") as logfile: + print(msg, file=logfile) + + rawstr = r'''{{`{{inputs.parameters.claim}}`}}''' + uuid = r'''{{`{{inputs.parameters.uuid}}`}}''' + d = json.loads(rawstr) + if not len(d["items"]): + raise ValueError(f"Claim (UUID: {uuid}) not found") + resource = d["items"][0] + ready = False + synced = False + reason = None + message = None + for c in resource["status"]["conditions"]: + if c["type"] == "Synced" and c["status"] == "True": + synced = True + if c["type"] == "Ready": + if c["status"] == "True": + ready = True + else: + reason = c["reason"] if "reason" in c else "" + message = c["message"] if "message" in c else "" + try: + assert synced and ready + except AssertionError: + msg = f"Claim (UUID: {uuid}) not ready yet, reason: {reason} - {message}" if reason and message else "Claim not ready yet" + write_termination_log(msg) + raise ValueError(f"Claim (UUID: {uuid}) not ready yet") + message_body = {} + message_body["vmIps"] = resource["status"]["vmIps"] + message_body["status"] = "Provisioning finalized successfully" + message_body["scriptTriggerId"] = uuid + print(json.dumps(message_body).replace('"', '\\"')) + - name: send-message + inputs: + parameters: + - name: message-body + script: + {{- if .Values.kafkaAuthEnable }} + env: + - name: MECHANISM + value: {{ .Values.kafkaAuthMechanism }} + - name: USERNAME + valueFrom: + secretKeyRef: + name: kafka-secret + key: username + - name: PASSWORD + valueFrom: + secretKeyRef: + name: kafka-secret + key: password + {{- end }} + image: confluentinc/cp-kafkacat:7.1.14 + command: [sh] + {{- if .Values.kafkaAuthEnable }} + source: | + echo {{`{{inputs.parameters.message-body}}`}} | kafkacat -P -b {{ .Values.kafkaEndpoint }} -X security.protocol=SASL_PLAINTEXT -X sasl.username="$USERNAME" -X sasl.password="$PASSWORD" -X sasl.mechanism="$MECHANISM" -t {{ .Values.kafkaProvisioningResponsesTopicName }} -J + {{- else }} + source: | + echo {{`{{inputs.parameters.message-body}}`}} | kafkacat -P -b {{ .Values.kafkaEndpoint }} -t {{ .Values.kafkaProvisioningResponsesTopicName }} -J + {{- end }} + + parameters: + - src: + dependencyName: message + dataKey: body + dest: spec.arguments.parameters.0.value diff --git a/charts/resources/templates/events/provision/status-source.yaml b/charts/templates/events/provision/status-source.yaml similarity index 73% rename from charts/resources/templates/events/provision/status-source.yaml rename to charts/templates/events/provision/status-source.yaml index f47ede78a87c20140743e8c974e1620aadf712c7..c0fc36a58c0139516bcd283322d822fea8775aa2 100644 --- a/charts/resources/templates/events/provision/status-source.yaml +++ b/charts/templates/events/provision/status-source.yaml @@ -6,7 +6,7 @@ metadata: spec: eventBusName: provisioner-eventbus template: - serviceAccountName: {{ .Values.applicationStatusViewerSA }} + serviceAccountName: provisioner-events resource: provisionStatus: namespace: {{ .Release.Namespace }} @@ -14,14 +14,10 @@ spec: version: v1alpha1 resource: applications eventTypes: - - UPDATE + - ADD filter: afterStart: true labels: - key: track-events operation: "==" value: "claim-application" - - key: provisioning-status - operation: "!=" - value: "finalized" - diff --git a/charts/dependencies/templates/post-install.yaml b/charts/templates/job.yaml similarity index 85% rename from charts/dependencies/templates/post-install.yaml rename to charts/templates/job.yaml index b36b294e4d39057f9c3e7e8dc92d7003ed157473..67aee8b030491c09c9dc6c07bec878925911a62e 100644 --- a/charts/dependencies/templates/post-install.yaml +++ b/charts/templates/job.yaml @@ -2,12 +2,11 @@ apiVersion: batch/v1 kind: Job metadata: - name: post-install-job + name: gitea-setup-job annotations: "helm.sh/hook": post-install - "helm.sh/hook-weight": "0" spec: - backoffLimit: 12 + backoffLimit: 24 ttlSecondsAfterFinished: 600 template: spec: @@ -33,4 +32,4 @@ spec: volumes: - name: postconfig-volume configMap: - name: {{ .Release.Name }}-postinstall-configmap + name: gitea-setup-job diff --git a/charts/templates/rbac/cli.yaml b/charts/templates/rbac/cli.yaml new file mode 100644 index 0000000000000000000000000000000000000000..671d4a37bcfc8b4984bf8da30defc58b6de0c554 --- /dev/null +++ b/charts/templates/rbac/cli.yaml @@ -0,0 +1,70 @@ +{{- if .Values.cliEnabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cli + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: cli + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - argoproj.io + resources: + - workflows + - workflows/finalizers + - workflowtasksets + - workflowtasksets/finalizers + - workflowartifactgctasks + verbs: + - get + - list + - watch + - update + - patch + - delete + - create + - apiGroups: + - argoproj.io + resources: + - workflowtemplates + - workflowtemplates/finalizers + verbs: + - get + - list + - watch + - apiGroups: + - argoproj.io + resources: + - workflowtaskresults + verbs: + - list + - watch + - deletecollection +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: cli + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cli +subjects: +- kind: ServiceAccount + name: cli + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: {{ .Release.Namespace }} + name: cli.service-account-token + annotations: + kubernetes.io/service-account.name: cli +type: kubernetes.io/service-account-token +{{- end }} \ No newline at end of file diff --git a/charts/resources/templates/rbac.yaml b/charts/templates/rbac/rbac.yaml similarity index 50% rename from charts/resources/templates/rbac.yaml rename to charts/templates/rbac/rbac.yaml index c4e53db5d395cf1e25e42aefd18ec678817e6bbe..237dbe54d530a93cde8e557005a6c1f6a3ddc603 100644 --- a/charts/resources/templates/rbac.yaml +++ b/charts/templates/rbac/rbac.yaml @@ -25,6 +25,38 @@ rules: - applications verbs: - patch + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: view-workflow + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - argoproj.io + resources: + - workflows + - workflowtaskresults + verbs: + - list + - get + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: patch-workflow + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - argoproj.io + resources: + - workflows + - workflowtaskresults + verbs: + - patch + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -37,13 +69,10 @@ roleRef: name: view-application subjects: - kind: ServiceAccount - name: claim-application-status - namespace: {{ .Release.Namespace }} -- kind: ServiceAccount - name: operate-workflow + name: provisioner-events namespace: {{ .Release.Namespace }} - kind: ServiceAccount - name: cli + name: provisioner-workflows namespace: {{ .Release.Namespace }} --- apiVersion: rbac.authorization.k8s.io/v1 @@ -57,21 +86,49 @@ roleRef: name: patch-application subjects: - kind: ServiceAccount - name: cli + name: provisioner-workflows + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: view-workflow + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: view-workflow +subjects: +- kind: ServiceAccount + name: provisioner-events + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: patch-workflow + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: patch-workflow +subjects: +- kind: ServiceAccount + name: provisioner-workflows + namespace: {{ .Release.Namespace }} +- kind: ServiceAccount + name: provisioner-events namespace: {{ .Release.Namespace }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: view-composite-status-events + name: {{ .Release.Namespace }}-view-crossplane roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: crossplane-view subjects: - kind: ServiceAccount - name: composite-status - namespace: {{ .Release.Namespace }} - - kind: ServiceAccount - name: cli + name: provisioner-workflows namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/charts/resources/templates/service-account.yaml b/charts/templates/rbac/service-account.yaml similarity index 65% rename from charts/resources/templates/service-account.yaml rename to charts/templates/rbac/service-account.yaml index f70a04a34d5ec16b40a7fd9ea99286615e373121..4ed8ee5fc70bbe82a8a1fe8736a6a554eb2fba58 100644 --- a/charts/resources/templates/service-account.yaml +++ b/charts/templates/rbac/service-account.yaml @@ -1,11 +1,11 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ .Values.workflowOperatorSA }} + name: provisioner-events namespace: {{ .Release.Namespace }} --- apiVersion: v1 kind: ServiceAccount metadata: - name: {{ .Values.applicationStatusViewerSA }} + name: provisioner-workflows namespace: {{ .Release.Namespace }} diff --git a/charts/templates/secret.yaml b/charts/templates/secret.yaml new file mode 100644 index 0000000000000000000000000000000000000000..12a45e9a851c8164748a730f3e8a8a2cdb031d8a --- /dev/null +++ b/charts/templates/secret.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ionos-provider +type: Opaque +data: + credentials: {{ printf "{\"token\":\"%s\"}" .Values.secrets.ionos.token | b64enc }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: kafka-secret +type: Opaque +data: + username: {{ .Values.secrets.kafka.username | b64enc }} + password: {{ .Values.secrets.kafka.password | b64enc }} +{{- if not (lookup "v1" "Secret" .Release.Namespace "gitea-secret") }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: gitea-secret +type: Opaque +data: + username: {{ .Values.secrets.gitea.username | b64enc }} + password: {{ .Values.secrets.gitea.password | b64enc }} +{{- end }} \ No newline at end of file diff --git a/charts/values.env.yaml b/charts/values.env.yaml new file mode 100644 index 0000000000000000000000000000000000000000..15e24077467d1c9b9a689ebd2c1a3cb9426dd183 --- /dev/null +++ b/charts/values.env.yaml @@ -0,0 +1,12 @@ +kafkaEndpoint: #KAFKA_ENDPOINT +giteaUrl: #GITEA_URL + +secrets: + ionos: + token: #IONOS_TOKEN + kafka: + username: #KAFKA_USERNAME + password: #KAFKA_PASSWORD + gitea: + username: #GITEA_USERNAME + password: #GITEA_PASSWORD diff --git a/charts/values.yaml b/charts/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..45aed983ad40e1de605df1f5a3815cd149798e78 --- /dev/null +++ b/charts/values.yaml @@ -0,0 +1,16 @@ +dependenciesReleaseName: provisioner-dependencies +dependenciesNamespace: infrastructure + +cliEnabled: false +provisionWorkflowImage: code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/to-provision-workflow:v0.5.0 + +kafkaAuthEnable: true +kafkaAuthMechanism: PLAIN +kafkaProvisioningRequestsTopicName: to-provision +kafkaProvisioningResponsesTopicName: provisioned +kafkaDecommissioningRequestsTopicName: to-decommission +kafkaDecommissioningResponsesTopicName: decommissioned + +configuration: + packages: + - code.europa.eu:4567/simpl/simpl-open/development/infrastructure/infrastructure-crossplane/configuration:v0.3.10 \ No newline at end of file diff --git a/package/apis/demo/definition.yaml b/package/apis/demo/definition.yaml index bb15d9a4c63c4e7e6de3981f95f37e211615b6c7..3c30cd0d874f2215ec1935650b5e00825eaac0d3 100644 --- a/package/apis/demo/definition.yaml +++ b/package/apis/demo/definition.yaml @@ -48,6 +48,8 @@ spec: enum: [INTEL_ICELAKE, AMD_EPYC] cloudConfig: type: string + providerConfig: + type: string required: - datacenterName - datacenterLocation @@ -55,6 +57,7 @@ spec: - cores - ram - cpuFamily + - providerConfig required: - parameters status: diff --git a/package/apis/demo/ionos.yaml b/package/apis/demo/ionos.yaml index 32540260409e43b07acdde5f517bf0ec3e494bd5..840bfa7d90eea4a8320a1e0c071ecfa5ac138351 100644 --- a/package/apis/demo/ionos.yaml +++ b/package/apis/demo/ionos.yaml @@ -25,6 +25,9 @@ spec: providerConfigRef: name: example patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.parameters.datacenterLocation toFieldPath: spec.forProvider.location @@ -67,6 +70,9 @@ spec: - type: string string: fmt: "%s_1" + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.parameters.cores toFieldPath: spec.forProvider.cores @@ -99,6 +105,9 @@ spec: providerConfigRef: name: example patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.resourceRefs[0].name toFieldPath: spec.forProvider.datacenterConfig.datacenterIdRef.name @@ -117,6 +126,9 @@ spec: providerConfigRef: name: example patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.parameters.datacenterLocation toFieldPath: spec.forProvider.location @@ -149,6 +161,9 @@ spec: providerConfigRef: name: example patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.resourceRefs[0].name toFieldPath: spec.forProvider.datacenterConfig.datacenterIdRef.name @@ -184,6 +199,9 @@ spec: providerConfigRef: name: example patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.providerConfig + toFieldPath: spec.providerConfigRef.name - type: FromCompositeFieldPath fromFieldPath: spec.resourceRefs[0].name toFieldPath: spec.forProvider.datacenterConfig.datacenterIdRef.name diff --git a/pipeline.variables.sh b/pipeline.variables.sh new file mode 100644 index 0000000000000000000000000000000000000000..f6f3e18951e9f3f4c23963f641283530d42a13a5 --- /dev/null +++ b/pipeline.variables.sh @@ -0,0 +1,2 @@ +PROJECT_VERSION_NUMBER="1.0.0" +RUN_CHART="true" diff --git a/setup.sh b/setup.sh index 2bd1c148d4465b5692ba92cde2de07af8f3d5d4f..baf881d91a50aa3ee8e169362e6261a1bbfe8b26 100644 --- a/setup.sh +++ b/setup.sh @@ -1,16 +1,36 @@ +# For setting up locally, tested using KinD (https://kind.sigs.k8s.io/) NS=infrastructure -kubectl create namespace $NS -kubectl create -n $NS secret docker-registry "ec-pull-secret" --docker-server="code.europa.eu:4567" --docker-username="$EC_USERNAME" --docker-password="$EC_PASSWORD" -kubectl create -n $NS secret generic gitea-secret --from-literal=username=gitops_test --from-literal=password=test1234 -kubectl create -n $NS secret generic kafka-secret --from-literal=username=demo --from-literal=password=demo-password -kubectl create -n $NS secret generic ionos-provider --from-literal=credentials="{\"token\":\"${IONOS_TOKEN}\"}" -helm install provisioner-dependencies -n $NS charts/dependencies -sleep 60 -helm install provisioner-resources -n $NS charts/resources +ENVIRONMENT_VALUES_YAML=values.local.yaml +ARGOCD_LABEL="app.kubernetes.io/name=argocd-server" +GITEA_LABEL="app.kubernetes.io/name=gitea" +ARGO_WORKFLOWS_LABEL="app.kubernetes.io/name=argo-workflows-server" +IONOS_PROVIDER_LABEL="pkg.crossplane.io/provider=provider-ionoscloud" +PROJECT_RELEASE_VERSION=1.0.0-testlocal + +kubectl create namespace $NS +# kubectl create -n $NS secret docker-registry "ec-pull-secret" --docker-server="code.europa.eu:4567" --docker-username="$EC_USERNAME" --docker-password="$EC_PASSWORD" + +helm upgrade --install dependencies -n $NS charts/dependencies -f charts/dependencies/values.yaml -f charts/dependencies/$ENVIRONMENT_VALUES_YAML --set PROJECT_RELEASE_VERSION=$PROJECT_RELEASE_VERSION +sleep 120 +kubectl wait --for=condition=Ready pod -l $IONOS_PROVIDER_LABEL --timeout=300s -n $NS +cp charts/Chart.yaml charts/Chart.yaml.tmp +sed -i "s/\${PROJECT_RELEASE_VERSION}/$PROJECT_RELEASE_VERSION/g" charts/Chart.yaml +helm upgrade --install resources -n $NS charts -f charts/values.yaml -f charts/$ENVIRONMENT_VALUES_YAML --set PROJECT_RELEASE_VERSION=$PROJECT_RELEASE_VERSION +mv charts/Chart.yaml.tmp charts/Chart.yaml # Use when installing locally to get access tokens and forward service ports -echo "Bearer $(kubectl get -n $NS secret cli.service-account-token -o=jsonpath='{.data.token}' | base64 --decode)" > argowftoken +if grep -q "^cliEnabled: true" "charts/values.yaml"; then + echo "Bearer $(kubectl get -n $NS secret cli.service-account-token -o=jsonpath='{.data.token}' | base64 --decode)" > argowftoken +fi kubectl get -n $NS secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d > argopw -# kubectl port-forward -n $NS svc/argocd-server 8888:443 -# kubectl port-forward -n $NS svc/argowf-argo-workflows-server 8777:2746 -# kubectl port-forward -n $NS svc/gitea-http 8333:3000 \ No newline at end of file + +# Wait for pods to be ready +kubectl wait --for=condition=Ready pod -l $ARGOCD_LABEL --timeout=300s -n $NS +kubectl wait --for=condition=Ready pod -l $GITEA_LABEL --timeout=300s -n $NS +kubectl wait --for=condition=Ready pod -l $ARGO_WORKFLOWS_LABEL --timeout=300s -n $NS + +# Port Forwarding to access UIs of each component +kubectl port-forward svc/dependencies-argocd-server 8888:443 -n $NS & +kubectl port-forward svc/dependencies-argo-workflows-server 8777:2746 -n $NS & +kubectl port-forward svc/dependencies-gitea-http 8333:3000 -n $NS & +sleep 3 \ No newline at end of file diff --git a/workflow-images/to-provision/Dockerfile b/workflow-images/to-provision/Dockerfile index 81777c5ec72c2eab5c69025ce85271488f767a37..8834be148008588d7f70c0c9ba05e46d1453781d 100644 --- a/workflow-images/to-provision/Dockerfile +++ b/workflow-images/to-provision/Dockerfile @@ -1,4 +1,5 @@ -FROM python:3 +FROM python:3-alpine +RUN apk add --no-cache git WORKDIR /work diff --git a/workflow-images/to-provision/main.py b/workflow-images/to-provision/main.py index a5db1f6cc29bb3d892aceaa2a2914b357d803e42..f3af6fe7b7f39b2488f4427ca1778cbdb49fdad5 100644 --- a/workflow-images/to-provision/main.py +++ b/workflow-images/to-provision/main.py @@ -13,6 +13,33 @@ CLAIM_KIND_REF_LABEL = "reference-kind" KIND_REF_PLACEHOLDER = "KIND" UUID_PLACEHOLDER = "UUID" +PROVIDER = 'ionos' + +##TODO add all metadata in this way +def insert_claim_metadata(claim, uuid): + as_dict = {} + try: + as_dict = yaml.safe_load(claim) + except Exception as e: + msg = f"claim metadata: cannot deserialize manifest: claim {uuid}" + write_termination_log(msg) + sys.exit(104) + try: + namespace = as_dict["metadata"]["namespace"] + as_dict["spec"]["parameters"]["providerConfig"] = f"{namespace}-{PROVIDER}-pc" + except KeyError: + msg = f"claim metadata: missing required fields: claim {uuid} " + write_termination_log(msg) + sys.exit(104) + try: + claim = yaml.safe_dump(as_dict) + except Exception as e: + msg = f"claim metadata: cannot serialize manifest: claim {uuid} " + write_termination_log(msg) + sys.exit(104) + + return claim + def write_termination_log(msg, echo_stdout=True): if echo_stdout: print(msg) @@ -51,6 +78,7 @@ except Exception as e: sys.exit(103) with open(script_path, mode="w", encoding="utf-8") as claim_file: + script_content = insert_claim_metadata(script_content, UUID) claim_file.write(script_content) print(f"\n---[claim {UUID} created]---\n") @@ -64,7 +92,7 @@ with open(APPLICATION_TEMPLATE_PATH, mode="r", encoding="utf-8") as template_fil except Exception as e: msg = f"Cannot retrieve claim kind reference label from manifest" write_termination_log(msg) - sys.exit(104) + sys.exit(105) application_content = template.format(**{UUID_PLACEHOLDER:UUID,KIND_REF_PLACEHOLDER:reference_kind}) with open(application_path, mode="w", encoding="utf-8") as application_file: