From d00009379bc8d2cd1b3b4700d0d970392c2f2878 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 26 Oct 2022 17:09:45 +0800 Subject: [PATCH 01/75] chore(helmbroker): use dynamic clusterrole name --- charts/helmbroker/templates/helmbroker-clusterrolebinding.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/helmbroker/templates/helmbroker-clusterrolebinding.yaml b/charts/helmbroker/templates/helmbroker-clusterrolebinding.yaml index f193a90..ddf07d3 100644 --- a/charts/helmbroker/templates/helmbroker-clusterrolebinding.yaml +++ b/charts/helmbroker/templates/helmbroker-clusterrolebinding.yaml @@ -1,7 +1,7 @@ kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: drycc:drycc-helmbroker + name: {{ printf "%s:drycc-helmbroker" .Release.Namespace | quote }} labels: app: drycc-helmbroker heritage: drycc From 6057875965e8f7ebd95df6549adb6f46bbdc95af Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 28 Oct 2022 23:08:01 +0800 Subject: [PATCH 02/75] feat(helmbroker): add rabbitmq dependencies --- .drone/drone.yml | 2 +- charts/helmbroker/Chart.yaml | 3 +++ charts/helmbroker/templates/_helpers.tpl | 17 +++++++++++++++++ charts/helmbroker/values.yaml | 11 ++++++++--- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.drone/drone.yml b/.drone/drone.yml index 990a142..864e9b6 100644 --- a/.drone/drone.yml +++ b/.drone/drone.yml @@ -126,7 +126,7 @@ name: chart steps: - name: generate chart commands: - - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && echo \"${DRONE_TAG:1}\" || echo \"canary\") + - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && sed -i "s#oci://registry.drycc.cc/charts-testing#oci://registry.drycc.cc/charts#g" charts/manager/Chart.yaml && echo \"${DRONE_TAG:1}\" || echo \"canary\") - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/helmbroker/values.yaml - helm package -u charts/helmbroker --version $([ -z $DRONE_TAG ] && echo 1.0.0 || echo ${DRONE_TAG#v}) - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index 1fc3fc4..b21064c 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -5,6 +5,9 @@ dependencies: - name: common repository: oci://registry.drycc.cc/charts version: ~1.1.1 + - name: rabbitmq + repository: oci://registry.drycc.cc/charts-testing + version: x.x.x description: Drycc Workflow helmbroker. maintainers: - name: Drycc Team diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 8a3e2dc..1fa77d2 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -7,6 +7,23 @@ env: value: {{ if .Values.username | default "" | ne "" }}{{ .Values.username }}{{ else }}{{ randAlphaNum 32 }}{{ end }} - name: PASSWORD value: {{ if .Values.password | default "" | ne "" }}{{ .Values.password }}{{ else }}{{ randAlphaNum 32 }}{{ end }} +{{- if (.Values.rabbitmqUrl) }} +- name: DRYCC_RABBITMQ_URL + value: {{ .Values.rabbitmqUrl }} +{{- else if eq .Values.global.rabbitmqLocation "on-cluster" }} +- name: "DRYCC_RABBITMQ_USERNAME" + valueFrom: + secretKeyRef: + name: rabbitmq-creds + key: username +- name: "DRYCC_RABBITMQ_PASSWORD" + valueFrom: + secretKeyRef: + name: rabbitmq-creds + key: password +- name: "DRYCC_RABBITMQ_URL" + value: "amqp://$(DRYCC_RABBITMQ_USERNAME):$(DRYCC_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" +{{- end }} {{- range $key, $value := .Values.environment }} - name: {{ $key }} value: {{ $value | quote }} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 1dd0c29..53fab48 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -18,10 +18,15 @@ celeryReplicas: 1 username: admin password: admin +# Configuring this will no longer use the built-in rabbitmq component +rabbitmqUrl: "" + +# Any custom controller environment variables +# can be specified as key-value pairs under environment +# this is usually a non required setting. environment: - RESERVED_NAMES: "drycc, drycc-helmbroker" - # HELMBROKER_CELERY_BROKER: "" - # HELMBROKER_CELERY_BACKEND: "" + # DRYCC_DEBUG: true + # HELMBROKER_ROOT: /etc/helmbroker api: nodeAffinityPreset: From 89f3427384c31746b2bbd124ac607f46231a2334 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 28 Oct 2022 23:29:09 +0800 Subject: [PATCH 03/75] chore(helmbroker): add global values --- .../templates/helmbroker-certificate.yaml | 4 +- .../templates/helmbroker-ingress.yaml | 12 +++--- charts/helmbroker/values.yaml | 42 +++++++++++++------ 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/charts/helmbroker/templates/helmbroker-certificate.yaml b/charts/helmbroker/templates/helmbroker-certificate.yaml index 1410a48..072d886 100644 --- a/charts/helmbroker/templates/helmbroker-certificate.yaml +++ b/charts/helmbroker/templates/helmbroker-certificate.yaml @@ -1,4 +1,4 @@ -{{- if .Values.certManagerEnabled }} +{{- if .Values.global.certManagerEnabled }} apiVersion: cert-manager.io/v1 kind: Certificate metadata: @@ -9,7 +9,7 @@ spec: name: drycc-cluster-issuer kind: ClusterIssuer dnsNames: - - drycc-helmbroker.{{ .Values.platformDomain }} + - drycc-helmbroker.{{ .Values.global.platformDomain }} privateKey: rotationPolicy: Always {{- end }} diff --git a/charts/helmbroker/templates/helmbroker-ingress.yaml b/charts/helmbroker/templates/helmbroker-ingress.yaml index 61f8040..818cae0 100644 --- a/charts/helmbroker/templates/helmbroker-ingress.yaml +++ b/charts/helmbroker/templates/helmbroker-ingress.yaml @@ -10,15 +10,15 @@ metadata: annotations: kubernetes.io/tls-acme: "true" spec: - {{- if not (eq .Values.ingressClass "") }} - ingressClassName: "{{ .Values.ingressClass }}" + {{- if not (eq .Values.global.ingressClass "") }} + ingressClassName: "{{ .Values.global.ingressClass }}" {{ end }} rules: - - host: drycc-helmbroker.{{ .Values.platformDomain }} + - host: drycc-helmbroker.{{ .Values.global.platformDomain }} http: paths: - pathType: Prefix - {{- if eq .Values.ingressClass "gce" "alb" }} + {{- if eq .Values.global.ingressClass "gce" "alb" }} path: /* {{- else }}{{/* Has annotations but ingress class is not "gce" nor "alb" */}} path: / @@ -28,9 +28,9 @@ spec: name: drycc-helmbroker port: number: 80 - {{- if .Values.certManagerEnabled }} + {{- if .Values.global.certManagerEnabled }} tls: - secretName: drycc-helmbroker-certificate-auto hosts: - - drycc-helmbroker.{{ .Values.platformDomain }} + - drycc-helmbroker.{{ .Values.global.platformDomain }} {{- end }} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 53fab48..c83181c 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -69,15 +69,33 @@ persistence: storageClass: "" volumeName: "" -ingressClass: "" -# A domain name consists of one or more parts. -# Periods (.) are used to separate these parts. -# Each part must be 1 to 63 characters in length and can contain lowercase letters, digits, and hyphens (-). -# It must start and end with a lowercase letter or digit. -clusterDomain: "cluster.local" -# The public resolvable hostname to build your cluster with. -# -# This will be the hostname that is used to build endpoints such as "drycc-helmbroker.$HOSTNAME" -platformDomain: "" -# Whether cert_manager is enabled to automatically generate helmbroker certificates -certManagerEnabled: true \ No newline at end of file +global: + # Set the location of Workflow's rabbitmq instance + # Valid values are: + # - on-cluster: Run Rabbitmq within the Kubernetes cluster + # - off-cluster: Run Rabbitmq outside the Kubernetes cluster (configure in controller section) + rabbitmqLocation: "on-cluster" + # Enable usage of RBAC authorization mode + # + # Valid values are: + # - true: all RBAC-related manifests will be installed (in case your cluster supports RBAC) + # - false: no RBAC-related manifests will be installed + rbac: true + # Please check `kubernetes.io/ingress.class` + # The cert-manager component must be installed + # If you want to use HTTPSEnforced or allowlist functions, you must specify: + # - nginx + # - traefik + # Only the above options have been supported so far + ingressClass: "" + # A domain name consists of one or more parts. + # Periods (.) are used to separate these parts. + # Each part must be 1 to 63 characters in length and can contain lowercase letters, digits, and hyphens (-). + # It must start and end with a lowercase letter or digit. + clusterDomain: "cluster.local" + # The public resolvable hostname to build your cluster with. + # + # This will be the hostname that is used to build endpoints such as "drycc-helmbroker.$HOSTNAME" + platformDomain: "" + # Whether cert_manager is enabled to automatically generate helmbroker certificates + certManagerEnabled: true \ No newline at end of file From a3579478d26525962bd5301937ca4af8479b8323 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 1 Nov 2022 16:44:55 +0800 Subject: [PATCH 04/75] chore(helmbroker): add helmbroker initContainers --- .gitignore | 1 + ...r-celery.yaml => helmbroker-celery-deployment.yaml} | 10 ++++++++++ charts/helmbroker/templates/helmbroker-deployment.yaml | 9 +++++++++ 3 files changed, 20 insertions(+) rename charts/helmbroker/templates/{helmbroker-celery.yaml => helmbroker-celery-deployment.yaml} (81%) diff --git a/.gitignore b/.gitignore index d48a71f..54a19de 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ vendor/ # generated bintray scripts during ci client/_scripts/ci/bintray-ci.json .idea +.vscode/ \ No newline at end of file diff --git a/charts/helmbroker/templates/helmbroker-celery.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml similarity index 81% rename from charts/helmbroker/templates/helmbroker-celery.yaml rename to charts/helmbroker/templates/helmbroker-celery-deployment.yaml index 9443b4b..7ff1d13 100644 --- a/charts/helmbroker/templates/helmbroker-celery.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -26,6 +26,16 @@ spec: podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.celery.podAntiAffinityPreset.type "key" .Values.celery.podAntiAffinityPreset.key "values" .Values.celery.podAntiAffinityPreset.values ) | nindent 10 }} nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.celery.nodeAffinityPreset.type "key" .Values.celery.nodeAffinityPreset.key "values" .Values.celery.nodeAffinityPreset.values ) | nindent 10 }} serviceAccount: drycc-helmbroker + initContainers: + - name: drycc-helmbroker-celery-init + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest + imagePullPolicy: {{.Values.imagePullPolicy}} + args: + - netcat + - -v + - -a + - $(DRYCC_HELMBROKER_SERVICE_HOST):$(DRYCC_HELMBROKER_SERVICE_PORT) + {{- include "helmbroker.envs" . | indent 10 }} containers: - name: drycc-helmbroker-celery image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 459142d..17708c4 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -36,6 +36,15 @@ spec: - python -m helmbroker.loader {{- include "helmbroker.envs" . | indent 10 }} {{- include "helmbroker.volumeMounts" . | indent 10 }} + - name: drycc-helmbroker-init + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest + imagePullPolicy: {{.Values.imagePullPolicy}} + args: + - netcat + - -v + - -u + - $(DRYCC_RABBITMQ_URL) + {{- include "helmbroker.envs" . | indent 10 }} containers: - name: drycc-helmbroker image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} From 87360f7ae4ee5451b0e4654a5f902acb2a9a0c95 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 7 Nov 2022 13:07:48 +0800 Subject: [PATCH 05/75] chore(): change env to HELMBROKER_CELERY_BROKER --- charts/helmbroker/templates/_helpers.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 1fa77d2..ee7b88e 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -8,7 +8,7 @@ env: - name: PASSWORD value: {{ if .Values.password | default "" | ne "" }}{{ .Values.password }}{{ else }}{{ randAlphaNum 32 }}{{ end }} {{- if (.Values.rabbitmqUrl) }} -- name: DRYCC_RABBITMQ_URL +- name: HELMBROKER_CELERY_BROKER value: {{ .Values.rabbitmqUrl }} {{- else if eq .Values.global.rabbitmqLocation "on-cluster" }} - name: "DRYCC_RABBITMQ_USERNAME" @@ -21,7 +21,7 @@ env: secretKeyRef: name: rabbitmq-creds key: password -- name: "DRYCC_RABBITMQ_URL" +- name: "HELMBROKER_CELERY_BROKER" value: "amqp://$(DRYCC_RABBITMQ_USERNAME):$(DRYCC_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" {{- end }} {{- range $key, $value := .Values.environment }} From 71c6ca84963a625e281afc52046a0da7dd50c5c1 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 7 Nov 2022 13:17:38 +0800 Subject: [PATCH 06/75] chore(helmbroker): remove result --- charts/helmbroker/templates/_helpers.tpl | 4 ++-- rootfs/helmbroker/celery.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index ee7b88e..1fa77d2 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -8,7 +8,7 @@ env: - name: PASSWORD value: {{ if .Values.password | default "" | ne "" }}{{ .Values.password }}{{ else }}{{ randAlphaNum 32 }}{{ end }} {{- if (.Values.rabbitmqUrl) }} -- name: HELMBROKER_CELERY_BROKER +- name: DRYCC_RABBITMQ_URL value: {{ .Values.rabbitmqUrl }} {{- else if eq .Values.global.rabbitmqLocation "on-cluster" }} - name: "DRYCC_RABBITMQ_USERNAME" @@ -21,7 +21,7 @@ env: secretKeyRef: name: rabbitmq-creds key: password -- name: "HELMBROKER_CELERY_BROKER" +- name: "DRYCC_RABBITMQ_URL" value: "amqp://$(DRYCC_RABBITMQ_USERNAME):$(DRYCC_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" {{- end }} {{- range $key, $value := .Values.environment }} diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 5ecf2f4..10db658 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -22,8 +22,7 @@ class Config: app = Celery( 'helmbroker', - broker=os.environ.get("HELMBROKER_CELERY_BROKER"), - backend=os.environ.get("HELMBROKER_CELERY_BACKEND"), + broker=os.environ.get("DRYCC_RABBITMQ_URL"), include=['helmbroker.tasks'] ) From 6030307c5bdf27014339817be03f2d96890b32f5 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 9 Nov 2022 10:27:57 +0800 Subject: [PATCH 07/75] chore(helmbroker): move org to drycc-addons --- .drone/manifest.tmpl | 6 +++--- Makefile | 2 +- README.md | 4 ++-- .../helmbroker/templates/helmbroker-celery-deployment.yaml | 2 +- charts/helmbroker/templates/helmbroker-deployment.yaml | 2 +- charts/helmbroker/values.yaml | 2 +- codecov.yml | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl index e482eab..f99268e 100644 --- a/.drone/manifest.tmpl +++ b/.drone/manifest.tmpl @@ -1,4 +1,4 @@ -image: registry.drycc.cc/drycc/helmbroker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} +image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} {{#if build.tags}} tags: {{#each build.tags}} @@ -7,12 +7,12 @@ tags: {{/if}} manifests: - - image: registry.drycc.cc/drycc/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 + image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 platform: architecture: amd64 os: linux - - image: registry.drycc.cc/drycc/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 + image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 platform: architecture: arm64 os: linux diff --git a/Makefile b/Makefile index 148a8c0..3ce5870 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # If DRYCC_REGISTRY is not set, try to populate it from legacy DEV_REGISTRY DRYCC_REGISTRY ?= $(DEV_REGISTRY) -IMAGE_PREFIX ?= drycc +IMAGE_PREFIX ?= drycc-addons COMPONENT ?= helmbroker SHORT_NAME ?= $(COMPONENT) PLATFORM ?= linux/amd64,linux/arm64 diff --git a/README.md b/README.md index 676028a..f40edb8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Drycc Helmbroker -[![Build Status](https://drone.drycc.cc/api/badges/drycc/helmbroker/status.svg)](https://drone.drycc.cc/drycc/helmbroker) -[![codecov.io](https://codecov.io/github/drycc/helmbroker/coverage.svg?branch=main)](https://codecov.io/github/drycc/helmbroker?branch=main) +[![Build Status](https://drone.drycc.cc/api/badges/drycc-addons/helmbroker/status.svg)](https://drone.drycc.cc/drycc-addons/helmbroker) +[![codecov.io](https://codecov.io/github/drycc-addons/helmbroker/coverage.svg?branch=main)](https://codecov.io/github/drycc-addons/helmbroker?branch=main) Drycc (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) that adds a developer-friendly layer to any [Kubernetes](http://kubernetes.io) cluster, making it easy to deploy and manage applications on your own servers. diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index 7ff1d13..e1aa054 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -28,7 +28,7 @@ spec: serviceAccount: drycc-helmbroker initContainers: - name: drycc-helmbroker-celery-init - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest + image: registry.drycc.cc/drycc/python-dev:latest imagePullPolicy: {{.Values.imagePullPolicy}} args: - netcat diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 17708c4..73153b0 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -37,7 +37,7 @@ spec: {{- include "helmbroker.envs" . | indent 10 }} {{- include "helmbroker.volumeMounts" . | indent 10 }} - name: drycc-helmbroker-init - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/python-dev:latest + image: registry.drycc.cc/drycc/python-dev:latest imagePullPolicy: {{.Values.imagePullPolicy}} args: - netcat diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index c83181c..1c05d54 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -1,4 +1,4 @@ -imageOrg: "drycc" +imageOrg: "drycc-addons" imageTag: "canary" imageRegistry: "registry.drycc.cc" imagePullPolicy: "Always" diff --git a/codecov.yml b/codecov.yml index c81ef32..b83b98f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,4 @@ -# documentation is at https://codecov.io/gh/drycc/helmbroker/settings/yaml +# documentation is at https://codecov.io/gh/drycc-addons/helmbroker/settings/yaml comment: layout: header, diff coverage: From e11f68db70a20b22b25261bbb2d22c13266fccae Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 7 Feb 2023 15:19:26 +0800 Subject: [PATCH 08/75] chore(helmbroker): use woodpecker replace drone --- .drone/drone.yml | 144 ------------------------------------ .drone/manifest.tmpl | 18 ----- .woodpecker/build-linux.yml | 29 ++++++++ .woodpecker/chart.yaml | 28 +++++++ .woodpecker/manifest.tmpl | 18 +++++ .woodpecker/manifest.yml | 38 ++++++++++ .woodpecker/test-linux.yml | 21 ++++++ 7 files changed, 134 insertions(+), 162 deletions(-) delete mode 100644 .drone/drone.yml delete mode 100644 .drone/manifest.tmpl create mode 100644 .woodpecker/build-linux.yml create mode 100644 .woodpecker/chart.yaml create mode 100644 .woodpecker/manifest.tmpl create mode 100644 .woodpecker/manifest.yml create mode 100644 .woodpecker/test-linux.yml diff --git a/.drone/drone.yml b/.drone/drone.yml deleted file mode 100644 index 864e9b6..0000000 --- a/.drone/drone.yml +++ /dev/null @@ -1,144 +0,0 @@ -kind: pipeline -type: exec -name: linux-amd64 - -platform: - arch: amd64 - os: linux - -steps: -- name: test - commands: - - mkdir -p $HOMEPATH/.docker; echo $IMAGE_PULL_SECRETS > $HOMEPATH/.docker/config.json - - make test-style - environment: - VERSION: ${DRONE_TAG:-latest}-linux-amd64 - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - CODECOV_TOKEN: - from_secret: codecov_token - IMAGE_PULL_SECRETS: - from_secret: container_pull_secrets - when: - event: - - push - - tag - - pull_request - -- name: publish - commands: - - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - - make docker-build docker-immutable-push - environment: - VERSION: ${DRONE_TAG:-latest}-linux-amd64 - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - CONTAINER_USERNAME: - from_secret: container_username - CONTAINER_PASSWORD: - from_secret: container_password - when: - event: - - push - - tag - ---- -kind: pipeline -type: exec -name: linux-arm64 - -platform: - arch: arm64 - os: linux - -steps: -- name: publish - commands: - - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - - make docker-build docker-immutable-push - environment: - VERSION: ${DRONE_TAG:-latest}-linux-arm64 - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - CONTAINER_USERNAME: - from_secret: container_username - CONTAINER_PASSWORD: - from_secret: container_password - when: - event: - - push - - tag - ---- -kind: pipeline -type: docker -name: manifest -image_pull_secrets: -- container_pull_secrets - -steps: - -- name: generate manifest - image: registry.drycc.cc/drycc/python-dev - pull: always - commands: - - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .drone/manifest.tmpl - environment: - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - -- name: publish - image: plugins/manifest - settings: - spec: .drone/manifest.tmpl - username: - from_secret: container_username - password: - from_secret: container_password - environment: - DEV_REGISTRY: - from_secret: dev_registry - DRYCC_REGISTRY: - from_secret: drycc_registry - -trigger: - event: - - push - - tag - -depends_on: -- linux-amd64 -- linux-arm64 - ---- -kind: pipeline -type: exec -name: chart - -steps: -- name: generate chart - commands: - - IMAGE_TAG=$([ ! -z $DRONE_TAG ] && sed -i "s#oci://registry.drycc.cc/charts-testing#oci://registry.drycc.cc/charts#g" charts/manager/Chart.yaml && echo \"${DRONE_TAG:1}\" || echo \"canary\") - - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/helmbroker/values.yaml - - helm package -u charts/helmbroker --version $([ -z $DRONE_TAG ] && echo 1.0.0 || echo ${DRONE_TAG#v}) - - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin - - helm push helmbroker-$([ -z $DRONE_TAG ] && echo 1.0.0 || echo ${DRONE_TAG#v}).tgz oci://$DRYCC_REGISTRY/$([ -z $DRONE_TAG ] && echo charts-testing || echo charts) - environment: - DRYCC_REGISTRY: - from_secret: drycc_registry - CONTAINER_USERNAME: - from_secret: container_username - CONTAINER_PASSWORD: - from_secret: container_password - when: - event: - - push - - tag diff --git a/.drone/manifest.tmpl b/.drone/manifest.tmpl deleted file mode 100644 index f99268e..0000000 --- a/.drone/manifest.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} -{{#if build.tags}} -tags: -{{#each build.tags}} - - {{this}} -{{/each}} -{{/if}} -manifests: - - - image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 - platform: - architecture: amd64 - os: linux - - - image: registry.drycc.cc/drycc-addons/helmbroker:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 - platform: - architecture: arm64 - os: linux diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml new file mode 100644 index 0000000..a345874 --- /dev/null +++ b/.woodpecker/build-linux.yml @@ -0,0 +1,29 @@ +matrix: + platform: + - linux/amd64 + - linux/arm64 + +platform: ${platform} + +labels: + type: exec + +pipeline: +- name: publish-linux + image: bash + commands: + - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_ARCH) + - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 + - make docker-build docker-immutable-push + secrets: + - dev_registry + - drycc_registry + - container_username + - container_password + when: + event: + - push + - tag + +depends_on: +- test-linux \ No newline at end of file diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml new file mode 100644 index 0000000..bfecf10 --- /dev/null +++ b/.woodpecker/chart.yaml @@ -0,0 +1,28 @@ +platform: linux/amd64 + +labels: + type: exec + +pipeline: +- name: generate-chart + type: local + image: bash + commands: + - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG) + - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\") + - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml + - helm package -u charts/$${CI_REPO_NAME} --version $([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) + - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin + - helm push $${CI_REPO_NAME}-$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION).tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) + secrets: + - dev_registry + - drycc_registry + - container_username + - container_password + when: + event: + - push + - tag + +depends_on: +- manifest \ No newline at end of file diff --git a/.woodpecker/manifest.tmpl b/.woodpecker/manifest.tmpl new file mode 100644 index 0000000..a5e50ce --- /dev/null +++ b/.woodpecker/manifest.tmpl @@ -0,0 +1,18 @@ +image: registry.drycc.cc/drycc-addons/{{project}}:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}canary{{/if}} +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - + image: registry.drycc.cc/drycc-addons/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-amd64 + platform: + architecture: amd64 + os: linux + - + image: registry.drycc.cc/drycc-addons/{{project}}:{{#if build.tag}}{{build.tag}}-{{else}}latest-{{/if}}linux-arm64 + platform: + architecture: arm64 + os: linux diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml new file mode 100644 index 0000000..f7023ff --- /dev/null +++ b/.woodpecker/manifest.yml @@ -0,0 +1,38 @@ +platform: linux/amd64 + +labels: + type: exec + +pipeline: +- name: generate-manifest + image: bash + commands: + - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl + - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl + secrets: + - drycc_registry + when: + event: + - tag + - push + +- name: publish-manifest + image: bash + commands: + - docker run --rm + -e PLUGIN_SPEC=.woodpecker/manifest.tmpl + -e PLUGIN_USERNAME=$CONTAINER_USERNAME + -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD + -v $(pwd):$(pwd) + -w $(pwd) + plugins/manifest + secrets: + - container_username + - container_password + when: + event: + - tag + - push + +depends_on: +- build-linux diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml new file mode 100644 index 0000000..d736541 --- /dev/null +++ b/.woodpecker/test-linux.yml @@ -0,0 +1,21 @@ +matrix: + platform: + - linux/amd64 + - linux/arm64 + +platform: ${platform} + +labels: + type: exec + +pipeline: +- name: test-linux + image: bash + commands: + - make test + secrets: + - dev_registry + when: + event: + - push + - tag From 36907c513b4c5f0a7736beadc1782118a4425958 Mon Sep 17 00:00:00 2001 From: jianxiaoguo Date: Fri, 24 Mar 2023 13:52:27 +0800 Subject: [PATCH 09/75] chore(woodpecker): update manifest --- .woodpecker/manifest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml index f7023ff..7f6ec36 100644 --- a/.woodpecker/manifest.yml +++ b/.woodpecker/manifest.yml @@ -23,6 +23,7 @@ pipeline: -e PLUGIN_SPEC=.woodpecker/manifest.tmpl -e PLUGIN_USERNAME=$CONTAINER_USERNAME -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD + -e DRONE_TAG=$CI_COMMIT_TAG -v $(pwd):$(pwd) -w $(pwd) plugins/manifest From c98cd426e3014244fdfec5f7452b15e9b7e288fc Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 29 Mar 2023 16:43:01 +0800 Subject: [PATCH 10/75] chore(helmbroker): change secret name to drycc-helmbroker-auto-tls --- charts/helmbroker/templates/helmbroker-certificate.yaml | 2 +- charts/helmbroker/templates/helmbroker-ingress.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/helmbroker/templates/helmbroker-certificate.yaml b/charts/helmbroker/templates/helmbroker-certificate.yaml index 072d886..7cef32f 100644 --- a/charts/helmbroker/templates/helmbroker-certificate.yaml +++ b/charts/helmbroker/templates/helmbroker-certificate.yaml @@ -4,7 +4,7 @@ kind: Certificate metadata: name: drycc-helmbroker spec: - secretName: drycc-helmbroker-certificate-auto + secretName: drycc-helmbroker-auto-tls issuerRef: name: drycc-cluster-issuer kind: ClusterIssuer diff --git a/charts/helmbroker/templates/helmbroker-ingress.yaml b/charts/helmbroker/templates/helmbroker-ingress.yaml index 818cae0..cfb93d5 100644 --- a/charts/helmbroker/templates/helmbroker-ingress.yaml +++ b/charts/helmbroker/templates/helmbroker-ingress.yaml @@ -30,7 +30,7 @@ spec: number: 80 {{- if .Values.global.certManagerEnabled }} tls: - - secretName: drycc-helmbroker-certificate-auto + - secretName: drycc-helmbroker-auto-tls hosts: - drycc-helmbroker.{{ .Values.global.platformDomain }} {{- end }} From a09a052a8ba1a9ef62bc10379bbe6b24a9651b00 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 16 May 2023 10:59:36 +0800 Subject: [PATCH 11/75] feat(helmbroker): use gateway replace ingress --- charts/helmbroker/Chart.yaml | 3 ++ .../templates/helmbroker-ingress.yaml | 36 ------------------- .../templates/helmbroker-route.yaml | 31 ++++++++++++++++ charts/helmbroker/values.yaml | 2 +- 4 files changed, 35 insertions(+), 37 deletions(-) delete mode 100644 charts/helmbroker/templates/helmbroker-ingress.yaml create mode 100644 charts/helmbroker/templates/helmbroker-route.yaml diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index b21064c..6149389 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -5,6 +5,9 @@ dependencies: - name: common repository: oci://registry.drycc.cc/charts version: ~1.1.1 + - name: gateway + repository: oci://registry.drycc.cc/charts-testing + version: x.x.x - name: rabbitmq repository: oci://registry.drycc.cc/charts-testing version: x.x.x diff --git a/charts/helmbroker/templates/helmbroker-ingress.yaml b/charts/helmbroker/templates/helmbroker-ingress.yaml deleted file mode 100644 index cfb93d5..0000000 --- a/charts/helmbroker/templates/helmbroker-ingress.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: "helmbroker-api-server" - labels: - app: "helmbroker" - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" - annotations: - kubernetes.io/tls-acme: "true" -spec: - {{- if not (eq .Values.global.ingressClass "") }} - ingressClassName: "{{ .Values.global.ingressClass }}" - {{ end }} - rules: - - host: drycc-helmbroker.{{ .Values.global.platformDomain }} - http: - paths: - - pathType: Prefix - {{- if eq .Values.global.ingressClass "gce" "alb" }} - path: /* - {{- else }}{{/* Has annotations but ingress class is not "gce" nor "alb" */}} - path: / - {{- end }} - backend: - service: - name: drycc-helmbroker - port: - number: 80 - {{- if .Values.global.certManagerEnabled }} - tls: - - secretName: drycc-helmbroker-auto-tls - hosts: - - drycc-helmbroker.{{ .Values.global.platformDomain }} - {{- end }} diff --git a/charts/helmbroker/templates/helmbroker-route.yaml b/charts/helmbroker/templates/helmbroker-route.yaml new file mode 100644 index 0000000..e2e4614 --- /dev/null +++ b/charts/helmbroker/templates/helmbroker-route.yaml @@ -0,0 +1,31 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: helmbroker-api-server + labels: + app: "drycc-helmbroker" + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + hostnames: + - drycc-helmbroker.{{ .Values.global.platformDomain }} + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: drycc-gateway + sectionName: drycc-gateway-listener-http + {{- if .Values.global.certManagerEnabled }} + - group: gateway.networking.k8s.io + kind: Gateway + name: drycc-gateway + sectionName: drycc-gateway-listener-https + {{- end }} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: drycc-manager + port: 80 diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 1c05d54..199b355 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -98,4 +98,4 @@ global: # This will be the hostname that is used to build endpoints such as "drycc-helmbroker.$HOSTNAME" platformDomain: "" # Whether cert_manager is enabled to automatically generate helmbroker certificates - certManagerEnabled: true \ No newline at end of file + certManagerEnabled: false \ No newline at end of file From c7aa7be4f3a60ada6afa34a73e1a73af6e4194aa Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 29 May 2023 17:33:40 +0800 Subject: [PATCH 12/75] feat(helmbroker): remove gateway --- charts/helmbroker/Chart.yaml | 3 -- .../templates/helmbroker-certificate.yaml | 15 --------- .../templates/helmbroker-route.yaml | 31 ------------------- charts/helmbroker/values.yaml | 13 -------- 4 files changed, 62 deletions(-) delete mode 100644 charts/helmbroker/templates/helmbroker-certificate.yaml delete mode 100644 charts/helmbroker/templates/helmbroker-route.yaml diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index 6149389..b21064c 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -5,9 +5,6 @@ dependencies: - name: common repository: oci://registry.drycc.cc/charts version: ~1.1.1 - - name: gateway - repository: oci://registry.drycc.cc/charts-testing - version: x.x.x - name: rabbitmq repository: oci://registry.drycc.cc/charts-testing version: x.x.x diff --git a/charts/helmbroker/templates/helmbroker-certificate.yaml b/charts/helmbroker/templates/helmbroker-certificate.yaml deleted file mode 100644 index 7cef32f..0000000 --- a/charts/helmbroker/templates/helmbroker-certificate.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.global.certManagerEnabled }} -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: drycc-helmbroker -spec: - secretName: drycc-helmbroker-auto-tls - issuerRef: - name: drycc-cluster-issuer - kind: ClusterIssuer - dnsNames: - - drycc-helmbroker.{{ .Values.global.platformDomain }} - privateKey: - rotationPolicy: Always -{{- end }} diff --git a/charts/helmbroker/templates/helmbroker-route.yaml b/charts/helmbroker/templates/helmbroker-route.yaml deleted file mode 100644 index e2e4614..0000000 --- a/charts/helmbroker/templates/helmbroker-route.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1beta1 -kind: HTTPRoute -metadata: - name: helmbroker-api-server - labels: - app: "drycc-helmbroker" - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - release: "{{ .Release.Name }}" - heritage: "{{ .Release.Service }}" -spec: - hostnames: - - drycc-helmbroker.{{ .Values.global.platformDomain }} - parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: drycc-gateway - sectionName: drycc-gateway-listener-http - {{- if .Values.global.certManagerEnabled }} - - group: gateway.networking.k8s.io - kind: Gateway - name: drycc-gateway - sectionName: drycc-gateway-listener-https - {{- end }} - rules: - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: drycc-manager - port: 80 diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 199b355..6e27427 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -81,21 +81,8 @@ global: # - true: all RBAC-related manifests will be installed (in case your cluster supports RBAC) # - false: no RBAC-related manifests will be installed rbac: true - # Please check `kubernetes.io/ingress.class` - # The cert-manager component must be installed - # If you want to use HTTPSEnforced or allowlist functions, you must specify: - # - nginx - # - traefik - # Only the above options have been supported so far - ingressClass: "" # A domain name consists of one or more parts. # Periods (.) are used to separate these parts. # Each part must be 1 to 63 characters in length and can contain lowercase letters, digits, and hyphens (-). # It must start and end with a lowercase letter or digit. clusterDomain: "cluster.local" - # The public resolvable hostname to build your cluster with. - # - # This will be the hostname that is used to build endpoints such as "drycc-helmbroker.$HOSTNAME" - platformDomain: "" - # Whether cert_manager is enabled to automatically generate helmbroker certificates - certManagerEnabled: false \ No newline at end of file From f95a77421ac1c0d06e499b646b04c5ade8f11b46 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 31 May 2023 13:02:35 +0800 Subject: [PATCH 13/75] chore(helmbroker): add chart appVersion --- .woodpecker/chart.yaml | 6 ++++-- charts/helmbroker/Chart.yaml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index bfecf10..3204cf5 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -10,10 +10,12 @@ pipeline: commands: - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG) - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\") + - export APP_VERSION=$([ ! -z $CI_COMMIT_TAG ] && echo $VERSION || echo 1.0.0) + - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml - - helm package -u charts/$${CI_REPO_NAME} --version $([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) + - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin - - helm push $${CI_REPO_NAME}-$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION).tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) + - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) secrets: - dev_registry - drycc_registry diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index b21064c..b23b3c2 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -1,6 +1,7 @@ name: helmbroker home: https://github.com/drycc/helmbroker apiVersion: v2 +appVersion: 1.0.0 dependencies: - name: common repository: oci://registry.drycc.cc/charts From b364a9df93442bedc033a6bf8f76a40be8f2e63f Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 20 Jun 2023 16:43:50 +0800 Subject: [PATCH 14/75] chre(helmbroker): add codename build-arg --- .woodpecker/build-linux.yml | 1 + .woodpecker/test-linux.yml | 1 + Makefile | 6 +++--- rootfs/Dockerfile | 9 +++++---- rootfs/Dockerfile.test | 9 +++++---- rootfs/dev_requirements.txt | 11 ++++------- rootfs/requirements.txt | 8 ++++---- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index a345874..db9ad17 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -16,6 +16,7 @@ pipeline: - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - make docker-build docker-immutable-push secrets: + - codename - dev_registry - drycc_registry - container_username diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml index d736541..313179e 100644 --- a/.woodpecker/test-linux.yml +++ b/.woodpecker/test-linux.yml @@ -14,6 +14,7 @@ pipeline: commands: - make test secrets: + - codename - dev_registry when: event: diff --git a/Makefile b/Makefile index 3ce5870..1b472c0 100644 --- a/Makefile +++ b/Makefile @@ -28,14 +28,14 @@ check-docker: build: docker-build docker-build: check-docker - docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE} rootfs + docker build ${DOCKER_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE} rootfs docker tag ${IMAGE} ${MUTABLE_IMAGE} docker-buildx: check-docker - docker buildx build --platform ${PLATFORM} -t ${IMAGE} rootfs --push + docker buildx build --build-arg CODENAME=${CODENAME} --platform ${PLATFORM} -t ${IMAGE} rootfs --push docker-build-test: check-docker - docker build ${DOCKER_BUILD_FLAGS} -t ${IMAGE}.test -f rootfs/Dockerfile.test rootfs + docker build ${DOCKER_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE}.test -f rootfs/Dockerfile.test rootfs deploy: check-kubectl docker-build docker-push kubectl --namespace=drycc patch deployment drycc-$(COMPONENT) --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"$(IMAGE)"}]' diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 5b5baa3..81562a6 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -1,11 +1,12 @@ -FROM registry.drycc.cc/drycc/base:bullseye +ARG CODENAME +FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.10.6" \ - HELM_VERSION="3.9.4" \ - KUBECTL_VERSION="1.25.0" + PYTHON_VERSION="3.11" \ + HELM_VERSION="3.12.1" \ + KUBECTL_VERSION="1.27.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 78edf19..8cbb6e6 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -1,11 +1,12 @@ -FROM registry.drycc.cc/drycc/base:bullseye +ARG CODENAME +FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.10.6" \ - HELM_VERSION="3.9.4" \ - KUBECTL_VERSION="1.25.0" + PYTHON_VERSION="3.11" \ + HELM_VERSION="3.12.1" \ + KUBECTL_VERSION="1.27.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/dev_requirements.txt b/rootfs/dev_requirements.txt index f8bc513..b46d2dc 100644 --- a/rootfs/dev_requirements.txt +++ b/rootfs/dev_requirements.txt @@ -1,16 +1,13 @@ # test module # test # Run "make test-unit" for the % of code exercised during tests -coverage==5.3 +coverage==7.2.7 # Run "make test-style" to check python syntax and style -flake8==3.8.3 +flake8==6.0.0 # code coverage report at https://codecov.io/github/drycc/controller -codecov==2.1.9 +codecov==2.1.13 # mock out python-requests, mostly k8s -requests-mock==1.8.0 - -# tail a log and pipe into tbgrep to find all tracebacks -tbgrep==0.3.0 +requests-mock==1.11.0 diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index 0c459a0..9a30c09 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -1,6 +1,6 @@ PyYAML==6.0 gunicorn==20.1.0 -openbrokerapi==4.1.2 -requests==2.28.1 -celery==5.2.7 -jsonschema==4.14.0 \ No newline at end of file +openbrokerapi==4.5.5 +requests==2.31.0 +celery==5.3.1 +jsonschema==4.17.3 \ No newline at end of file From 0b2398e11492111b0b99833bf54f9d02335f3dc1 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 9 Aug 2023 14:32:07 +0800 Subject: [PATCH 15/75] chore(helmbroker): set broker_connection_retry_on_startup enabled --- rootfs/helmbroker/celery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 10db658..5b2e4cd 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -17,6 +17,7 @@ class Config: task_time_limit = 30 * 60 worker_max_tasks_per_child = 200 result_expires = 24 * 60 * 60 + broker_connection_retry_on_startup = True worker_cancel_long_running_tasks_on_connection_loss = True From 4ce1be9227ab24680df66cce0356543f4a72f2d5 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Tue, 15 Aug 2023 16:54:26 +0800 Subject: [PATCH 16/75] chore(helmbroker): bump common 1.1.2 --- charts/helmbroker/Chart.yaml | 2 +- .../helmbroker-celery-deployment.yaml | 6 +++--- .../templates/helmbroker-deployment.yaml | 6 +++--- charts/helmbroker/values.yaml | 20 ++++++++----------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index b23b3c2..4ef1dc8 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -5,7 +5,7 @@ appVersion: 1.0.0 dependencies: - name: common repository: oci://registry.drycc.cc/charts - version: ~1.1.1 + version: ~1.1.2 - name: rabbitmq repository: oci://registry.drycc.cc/charts-testing version: x.x.x diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index e1aa054..d29b519 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -18,12 +18,12 @@ spec: app: drycc-helmbroker-celery template: metadata: - labels: + labels: {{- include "common.labels.standard" . | nindent 8 }} app: drycc-helmbroker-celery spec: affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.celery.podAffinityPreset.type "key" .Values.celery.podAffinityPreset.key "values" .Values.celery.podAffinityPreset.values ) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.celery.podAntiAffinityPreset.type "key" .Values.celery.podAntiAffinityPreset.key "values" .Values.celery.podAntiAffinityPreset.values ) | nindent 10 }} + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.celery.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.celery.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.celery.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.celery.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.celery.nodeAffinityPreset.type "key" .Values.celery.nodeAffinityPreset.key "values" .Values.celery.nodeAffinityPreset.values ) | nindent 10 }} serviceAccount: drycc-helmbroker initContainers: diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 73153b0..8de5661 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -18,12 +18,12 @@ spec: app: drycc-helmbroker template: metadata: - labels: + labels: {{- include "common.labels.standard" . | nindent 8 }} app: drycc-helmbroker spec: affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.api.podAffinityPreset.type "key" .Values.api.podAffinityPreset.key "values" .Values.api.podAffinityPreset.values ) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.api.podAntiAffinityPreset.type "key" .Values.api.podAntiAffinityPreset.key "values" .Values.api.podAntiAffinityPreset.values ) | nindent 10 }} + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.api.podAffinityPreset.type "component" "" "extraMatchLabels" .Values.api.podAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.api.podAntiAffinityPreset.type "component" "" "extraMatchLabels" .Values.api.podAntiAffinityPreset.extraMatchLabels "topologyKey" "" "context" $) | nindent 10 }} nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.api.nodeAffinityPreset.type "key" .Values.api.nodeAffinityPreset.key "values" .Values.api.nodeAffinityPreset.values ) | nindent 10 }} serviceAccount: drycc-helmbroker initContainers: diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 6e27427..764e82a 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -35,15 +35,13 @@ api: values: - "true" podAffinityPreset: - key: "security" type: "" - values: - - "drycc-security" + extraMatchLabels: + security: "drycc-security" podAntiAffinityPreset: - key: "app" type: "soft" - values: - - "drycc-helmbroker" + extraMatchLabels: + app: "drycc-helmbroker" celery: nodeAffinityPreset: @@ -52,15 +50,13 @@ celery: values: - "true" podAffinityPreset: - key: "security" type: "" - values: - - "drycc-security" + extraMatchLabels: + security: "drycc-security" podAntiAffinityPreset: - key: "app" type: "soft" - values: - - "drycc-helmbroker-celery" + extraMatchLabels: + app: "drycc-helmbroker-celery" persistence: enabled: true From 3d17e5feeb4ccea0a4215a53748c2133bc881fdc Mon Sep 17 00:00:00 2001 From: lijianguo Date: Fri, 18 Aug 2023 12:05:49 +0800 Subject: [PATCH 17/75] chore(helmbroker): fixup extract_tgz path --- rootfs/helmbroker/loader.py | 2 +- rootfs/helmbroker/tasks.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/rootfs/helmbroker/loader.py b/rootfs/helmbroker/loader.py index 9afa699..9c2a668 100644 --- a/rootfs/helmbroker/loader.py +++ b/rootfs/helmbroker/loader.py @@ -105,7 +105,7 @@ def load_addons(repository): addon_tgz_url = f'{url}/{tgz_name}.tgz' download_file(addon_tgz_url, ADDONS_PATH) extract_tgz(f'{ADDONS_PATH}/{tgz_name}.tgz', - f'{ADDONS_PATH}/{tgz_name}') + f'{ADDONS_PATH}') addons_meta_file() diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 3370b65..613a5be 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -2,6 +2,7 @@ import time import shutil import yaml +import logging from openbrokerapi.service_broker import ProvisionDetails, OperationState, \ UpdateDetails, BindDetails @@ -11,6 +12,8 @@ InstanceLock, dump_instance_meta, dump_binding_meta, load_instance_meta, \ get_instance_file, helm +logger = logging.getLogger(__name__) + @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): @@ -42,7 +45,10 @@ def provision(instance_id: str, details: ProvisionDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - + logger.info(f"helm install parameters :{details.parameters}") + logger.info(f"helm install parameters type:{type(details.parameters)}") + # for k, v in details.parameters: + # args.append("--set", k, v) status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) if status != 0: @@ -90,7 +96,8 @@ def update(instance_id: str, details: UpdateDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - + logger.info(f"helm upgrade parameters: {details.parameters}") + logger.info(f"helm upgrade parameters type: {type(details.parameters)}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value From 18a5de9b8a4a9574f8d47f479adf78bad8c73c21 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Fri, 25 Aug 2023 12:01:21 +0800 Subject: [PATCH 18/75] chore(helmbroker): update parameters --- rootfs/helmbroker/broker.py | 5 +++++ rootfs/helmbroker/tasks.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index a629b52..5d479a1 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -1,6 +1,7 @@ import os import time import shutil +import logging from typing import Union, List, Optional from openbrokerapi.catalog import ServicePlan @@ -19,6 +20,8 @@ load_addons_meta from .tasks import provision, bind, deprovision, update +logger = logging.getLogger(__name__) + class HelmServiceBroker(ServiceBroker): @@ -147,6 +150,8 @@ def update(self, details.service_id, details.plan_id) # add the new plan shutil.copytree(addon_plan_path, plan_path) + logger.info(f"service update parameters: {details.parameters}") + logger.info(f"service update parameters type: {type(details.parameters)}") # noqa update.delay(instance_id, details) return UpdateServiceSpec(is_async=True) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 613a5be..3d11b24 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -45,10 +45,11 @@ def provision(instance_id: str, details: ProvisionDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] + + for k, v in details.parameters: + args.extend(["--set", f"{k}={v}"]) logger.info(f"helm install parameters :{details.parameters}") logger.info(f"helm install parameters type:{type(details.parameters)}") - # for k, v in details.parameters: - # args.append("--set", k, v) status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) if status != 0: @@ -72,7 +73,7 @@ def update(instance_id: str, details: UpdateDetails): if details.context: data['details']['context'] = details.context if details.parameters: - data['details']['service_id'] = details.parameters + data['details']['parameters'] = details.parameters data['last_operation'] = { "state": OperationState.IN_PROGRESS.value, "description": ( @@ -96,6 +97,8 @@ def update(instance_id: str, details: UpdateDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] + for k, v in details.parameters: + args.extend(["--set", f"{k}={v}"]) logger.info(f"helm upgrade parameters: {details.parameters}") logger.info(f"helm upgrade parameters type: {type(details.parameters)}") status, output = helm(instance_id, *args) From d13002f539c3dc0cd1a241ca34b3d093291776f8 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Mon, 28 Aug 2023 09:39:40 +0800 Subject: [PATCH 19/75] chore(helmbroker): update load addons --- rootfs/helmbroker/loader.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/rootfs/helmbroker/loader.py b/rootfs/helmbroker/loader.py index 9c2a668..bdf1bc7 100644 --- a/rootfs/helmbroker/loader.py +++ b/rootfs/helmbroker/loader.py @@ -68,12 +68,17 @@ def addons_meta_file(): meta = yaml.load(f.read(), Loader=yaml.Loader) meta['tags'] = meta.get('tags').split(', ') if meta.get('tags') else [] # noqa meta['plans'] = [] - addons_dict[meta['name']] = meta - - for plan_meta in plans_meta: - with open(f'{ADDONS_PATH}/{"/".join(plan_meta)}', 'r') as f: - addons_mata = yaml.load(f.read(), Loader=yaml.Loader) - addons_dict[f'{"-".join(plan_meta[0].split("-")[0:-1])}']['plans'].append(addons_mata) # noqa + addons_dict[meta['displayName']] = meta + addon_plans_meta = [] + for plan_meta in plans_meta: + if plan_meta[0] == meta['displayName']: + addon_plans_meta.append(plan_meta) + elif f'{"-".join(plan_meta[0].split("-")[0:-1])}' == meta['displayName']: # noqa + addon_plans_meta.append(plan_meta) + for addon_plan_meta in addon_plans_meta: + with open(f'{ADDONS_PATH}/{"/".join(addon_plan_meta)}', 'r') as f: + addons_mata = yaml.load(f.read(), Loader=yaml.Loader) + addons_dict[meta['displayName']]['plans'].append(addons_mata) # noqa dump_addons_meta(addons_dict) @@ -98,7 +103,7 @@ def load_addons(repository): save_file(remote_index, ADDONS_PATH, index_name) remote_index = yaml.load(remote_index, Loader=yaml.Loader) # save index.yaml addons - for k, v in remote_index.get('entries', {}).items(): + for _, v in remote_index.get('entries', {}).items(): for _ in v: url = "/".join(repository["url"].split("/")[0:-1]) tgz_name = f'{_["name"]}-{_["version"]}' @@ -106,7 +111,7 @@ def load_addons(repository): download_file(addon_tgz_url, ADDONS_PATH) extract_tgz(f'{ADDONS_PATH}/{tgz_name}.tgz', f'{ADDONS_PATH}') - addons_meta_file() + addons_meta_file() if __name__ == '__main__': From 4513c66d66ff265430e28da4034de69f6ff87b04 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Tue, 29 Aug 2023 11:43:50 +0800 Subject: [PATCH 20/75] chore(helmbroker): verify parameters --- rootfs/helmbroker/broker.py | 12 +++++++++++- rootfs/helmbroker/utils.py | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 5d479a1..8783f94 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -17,7 +17,7 @@ from .utils import get_instance_path, get_chart_path, get_plan_path, \ get_addon_path, get_addon_updateable, get_addon_bindable, InstanceLock, \ load_instance_meta, load_binding_meta, dump_instance_meta, \ - load_addons_meta + load_addons_meta, get_addon_allow_paras, verify_parameters from .tasks import provision, bind, deprovision, update logger = logging.getLogger(__name__) @@ -46,6 +46,11 @@ def provision(self, raise ErrInstanceAlreadyExists() if not async_allowed: raise ErrAsyncRequired() + allow_paras = get_addon_allow_paras(details.service_id) + not_allow_keys = verify_parameters(allow_paras, details.parameters) + if not_allow_keys: + raise ErrBadRequest( + msg="Instance parameters %s does not allowed" % not_allow_keys) os.makedirs(instance_path, exist_ok=True) chart_path, plan_path = ( get_chart_path(instance_id), get_plan_path(instance_id)) @@ -140,6 +145,11 @@ def update(self, if not is_plan_updateable: raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) + allow_paras = get_addon_allow_paras(details.service_id) + not_allow_keys = verify_parameters(allow_paras, details.parameters) + if not_allow_keys: + raise ErrBadRequest( + msg="Instance parameters %s does not allowed" % not_allow_keys) if not async_allowed: raise ErrAsyncRequired() if details.plan_id is not None: diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index dbbf351..f2a686d 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -217,6 +217,11 @@ def get_addon_bindable(service_id): return service.get('bindable', False) +def get_addon_allow_parameters(service_id): + service = get_addon_meta(service_id) + return service.get('allow_parameters', []) + + def get_cred_value(ns, source): if source.get('serviceRef'): return get_service_key_value(ns, source['serviceRef']) @@ -271,3 +276,20 @@ def __exit__(self, exc_type, exc_value, traceback): def __del__(self): if hasattr(self, "fileno"): fcntl.flock(self.fileno, fcntl.LOCK_UN) + + +def verify_parameters(allow_paras, paras): + """verify parameters allowed or not""" + if not paras or not allow_paras: + return "" + else: + not_allow_paras = [] + allow_para_keys = [allow_para["name"] + "." for allow_para in allow_paras] # noqa + para_keys = [k + "." for k in paras] + for para_key in para_keys: + for allow_para_key in allow_para_keys: + # sub string Inclusion relationship + if not para_key.startswith(allow_para_key): + not_allow_paras.append(para_key) + not_allow_keys = ",".split(not_allow_paras) + return not_allow_keys From d33d00e21f96c92269c9f328213af45076f1a142 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 30 Aug 2023 08:39:30 +0800 Subject: [PATCH 21/75] fix(helmbroker): woodpecker build link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f40edb8..3ffb184 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Drycc Helmbroker -[![Build Status](https://drone.drycc.cc/api/badges/drycc-addons/helmbroker/status.svg)](https://drone.drycc.cc/drycc-addons/helmbroker) +[![Build Status](https://woodpecker.drycc.cc/api/badges/drycc-addons/helmbroker/status.svg)](https://woodpecker.drycc.cc/drycc-addons/helmbroker) [![codecov.io](https://codecov.io/github/drycc-addons/helmbroker/coverage.svg?branch=main)](https://codecov.io/github/drycc-addons/helmbroker?branch=main) Drycc (pronounced DAY-iss) Workflow is an open source Platform as a Service (PaaS) that adds a developer-friendly layer to any [Kubernetes](http://kubernetes.io) cluster, making it easy to deploy and manage applications on your own servers. From b508c731c4caecfe9ef9a6065d9b99034fe85d55 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Thu, 31 Aug 2023 13:24:14 +0800 Subject: [PATCH 22/75] chore(helmbroker): change celery priority --- rootfs/helmbroker/celery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 5b2e4cd..b66ed18 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -18,6 +18,7 @@ class Config: worker_max_tasks_per_child = 200 result_expires = 24 * 60 * 60 broker_connection_retry_on_startup = True + task_default_queue = 'helmbroker.priority.low' worker_cancel_long_running_tasks_on_connection_loss = True From 3e36826371443ed40d787b28e3cad9e0e1ec716d Mon Sep 17 00:00:00 2001 From: lijianguo Date: Thu, 31 Aug 2023 15:10:10 +0800 Subject: [PATCH 23/75] feat(helmbroker): support raw_values in parameters --- rootfs/helmbroker/tasks.py | 29 +++++++++++++----- rootfs/helmbroker/utils.py | 60 ++++++++++++++++++++++++++++++-------- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 3d11b24..744b287 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -2,6 +2,7 @@ import time import shutil import yaml +import base64 import logging from openbrokerapi.service_broker import ProvisionDetails, OperationState, \ @@ -10,7 +11,7 @@ from .celery import app from .utils import get_plan_path, get_chart_path, get_cred_value, \ InstanceLock, dump_instance_meta, dump_binding_meta, load_instance_meta, \ - get_instance_file, helm + get_instance_file, helm, dump_raw_values logger = logging.getLogger(__name__) @@ -45,11 +46,16 @@ def provision(instance_id: str, details: ProvisionDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - - for k, v in details.parameters: - args.extend(["--set", f"{k}={v}"]) logger.info(f"helm install parameters :{details.parameters}") - logger.info(f"helm install parameters type:{type(details.parameters)}") + if details.parameters and "rawValues" in details.parameters \ + and details.parameters.get("rawValues", ""): + values = str(base64.b64decode(details.parameters["rawValues"]), "utf-8") # noqa + raw_values_file = dump_raw_values(instance_id, values) + args.extend(["-f", raw_values_file]) + details.parameters.pop("rawValues") + for k, v in details.parameters.items(): + args.extend(["--set", f"{k}={v}"]) + logger.info(f"helm install args:{args}") status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) if status != 0: @@ -92,15 +98,22 @@ def update(instance_id: str, details: UpdateDetails): "--wait", "--timeout", "25m0s", + "--reuse-values", "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - for k, v in details.parameters: - args.extend(["--set", f"{k}={v}"]) logger.info(f"helm upgrade parameters: {details.parameters}") - logger.info(f"helm upgrade parameters type: {type(details.parameters)}") + if details.parameters and "rawValues" in details.parameters \ + and details.parameters.get("rawValues", ""): + values = str(base64.b64decode(details.parameters["rawValues"]), "utf-8") # noqa + raw_values_file = dump_raw_values(instance_id, values) + args.extend(["-f", raw_values_file]) + details.parameters.pop("rawValues") + for k, v in details.parameters.items(): + args.extend(["--set", f"{k}={v}"]) + logger.info(f"helm upgrade args:{args}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index f2a686d..6d6dd63 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -4,6 +4,8 @@ import json import subprocess import time +import base64 +import copy from jsonschema import validate from .config import INSTANCES_PATH, ADDONS_PATH @@ -91,6 +93,15 @@ def dump_instance_meta(instance_id, data): f.write(json.dumps(data, sort_keys=True, indent=2)) +def dump_raw_values(instance_id, data): + timestamp = time.time() + instance_path = get_instance_path(instance_id) + file = f"{instance_path}/raw-values-{timestamp}.yaml" + with open(file, "w") as f: + f.write(data) + return file + + BINDING_META_SCHEMA = { "type": "object", "properties": { @@ -217,7 +228,7 @@ def get_addon_bindable(service_id): return service.get('bindable', False) -def get_addon_allow_parameters(service_id): +def get_addon_allow_paras(service_id): service = get_addon_meta(service_id) return service.get('allow_parameters', []) @@ -282,14 +293,39 @@ def verify_parameters(allow_paras, paras): """verify parameters allowed or not""" if not paras or not allow_paras: return "" - else: - not_allow_paras = [] - allow_para_keys = [allow_para["name"] + "." for allow_para in allow_paras] # noqa - para_keys = [k + "." for k in paras] - for para_key in para_keys: - for allow_para_key in allow_para_keys: - # sub string Inclusion relationship - if not para_key.startswith(allow_para_key): - not_allow_paras.append(para_key) - not_allow_keys = ",".split(not_allow_paras) - return not_allow_keys + tmp_paras = copy.deepcopy(paras) + # raw_values + raw_para_keys = [] + if "rawValues" in tmp_paras: + raw_values = yaml.safe_load(base64.b64decode(tmp_paras["rawValues"])) + raw_para_keys = raw_values_format_keys(raw_values) + tmp_paras.pop("rawValues") + + para_keys = set(list(tmp_paras.keys()) + raw_para_keys) + para_keys = [k + "." for k in para_keys] + allow_para_keys = [allow_para["name"] + "." for allow_para in allow_paras] # noqa + + not_allow_paras = [] + for para_key in para_keys: + for allow_para_key in allow_para_keys: + # sub string, inclusion relationship + if not para_key.startswith(allow_para_key): + not_allow_paras.append(para_key) + not_allow_keys = ",".join(not_allow_paras) + return not_allow_keys + + +def raw_values_format_keys(raw_values, prefix=''): + """ + {'a': {'b': 1, 'c': {'d': 2, 'e': 3}}, 'f': 4} + -> + ['a.b', 'a.c.d', 'a.c.e', 'a.f'] + """ + keys = [] + for key, value in raw_values.items(): + new_prefix = prefix + '.' + key if prefix else key + if isinstance(value, dict): + keys.extend(raw_values_format_keys(value, new_prefix)) + else: + keys.append(new_prefix) + return keys From 95ef305329f3a9679c1d758359c30579083d47ef Mon Sep 17 00:00:00 2001 From: lijianguo Date: Tue, 5 Sep 2023 10:00:57 +0800 Subject: [PATCH 24/75] chore(helmbroker): support addon archive or not --- rootfs/helmbroker/broker.py | 6 +++++- rootfs/helmbroker/loader.py | 6 +++--- rootfs/helmbroker/utils.py | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 8783f94..c9fd111 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -17,7 +17,8 @@ from .utils import get_instance_path, get_chart_path, get_plan_path, \ get_addon_path, get_addon_updateable, get_addon_bindable, InstanceLock, \ load_instance_meta, load_binding_meta, dump_instance_meta, \ - load_addons_meta, get_addon_allow_paras, verify_parameters + load_addons_meta, get_addon_allow_paras, verify_parameters, \ + get_addon_archive from .tasks import provision, bind, deprovision, update logger = logging.getLogger(__name__) @@ -46,6 +47,9 @@ def provision(self, raise ErrInstanceAlreadyExists() if not async_allowed: raise ErrAsyncRequired() + if get_addon_archive(details.service_id): + raise ErrBadRequest( + msg="This addon has archived.") allow_paras = get_addon_allow_paras(details.service_id) not_allow_keys = verify_parameters(allow_paras, details.parameters) if not_allow_keys: diff --git a/rootfs/helmbroker/loader.py b/rootfs/helmbroker/loader.py index bdf1bc7..abdec39 100644 --- a/rootfs/helmbroker/loader.py +++ b/rootfs/helmbroker/loader.py @@ -103,14 +103,14 @@ def load_addons(repository): save_file(remote_index, ADDONS_PATH, index_name) remote_index = yaml.load(remote_index, Loader=yaml.Loader) # save index.yaml addons - for _, v in remote_index.get('entries', {}).items(): + for addon_name, v in remote_index.get('entries', {}).items(): for _ in v: url = "/".join(repository["url"].split("/")[0:-1]) - tgz_name = f'{_["name"]}-{_["version"]}' + tgz_name = f'{addon_name}-{_["version"]}' addon_tgz_url = f'{url}/{tgz_name}.tgz' download_file(addon_tgz_url, ADDONS_PATH) extract_tgz(f'{ADDONS_PATH}/{tgz_name}.tgz', - f'{ADDONS_PATH}') + f'{ADDONS_PATH}/{tgz_name}') addons_meta_file() diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 6d6dd63..53d59cf 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -233,6 +233,11 @@ def get_addon_allow_paras(service_id): return service.get('allow_parameters', []) +def get_addon_archive(service_id): + service = get_addon_meta(service_id) + return service.get('archive', False) + + def get_cred_value(ns, source): if source.get('serviceRef'): return get_service_key_value(ns, source['serviceRef']) From c40cf75f04f8e0e985ae4ba4329e4cf2425b534c Mon Sep 17 00:00:00 2001 From: lijianguo Date: Fri, 22 Sep 2023 09:56:09 +0800 Subject: [PATCH 25/75] fix(helmbroker): service instance parameters verify --- rootfs/helmbroker/tasks.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 744b287..0068b47 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -53,8 +53,9 @@ def provision(instance_id: str, details: ProvisionDetails): raw_values_file = dump_raw_values(instance_id, values) args.extend(["-f", raw_values_file]) details.parameters.pop("rawValues") - for k, v in details.parameters.items(): - args.extend(["--set", f"{k}={v}"]) + if details.parameters: + for k, v in details.parameters.items(): + args.extend(["--set", f"{k}={v}"]) logger.info(f"helm install args:{args}") status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) @@ -111,8 +112,9 @@ def update(instance_id: str, details: UpdateDetails): raw_values_file = dump_raw_values(instance_id, values) args.extend(["-f", raw_values_file]) details.parameters.pop("rawValues") - for k, v in details.parameters.items(): - args.extend(["--set", f"{k}={v}"]) + if details.parameters: + for k, v in details.parameters.items(): + args.extend(["--set", f"{k}={v}"]) logger.info(f"helm upgrade args:{args}") status, output = helm(instance_id, *args) if status != 0: From 2a7d5996111d172d2d07b0723ed2ecb98f5da322 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Mon, 16 Oct 2023 16:27:55 +0800 Subject: [PATCH 26/75] chore(helmbroker): use podman replace docker --- .woodpecker/build-linux.yml | 4 ++-- .woodpecker/manifest.yml | 4 ++-- Makefile | 43 +++++++++++++++++-------------------- versioning.mk | 16 +++++++------- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index db9ad17..8cdd984 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -13,8 +13,8 @@ pipeline: image: bash commands: - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_ARCH) - - echo $CONTAINER_PASSWORD | docker login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - - make docker-build docker-immutable-push + - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 + - make podman-build podman-immutable-push secrets: - codename - dev_registry diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml index 7f6ec36..2d5c4e5 100644 --- a/.woodpecker/manifest.yml +++ b/.woodpecker/manifest.yml @@ -19,14 +19,14 @@ pipeline: - name: publish-manifest image: bash commands: - - docker run --rm + - podman run --rm -e PLUGIN_SPEC=.woodpecker/manifest.tmpl -e PLUGIN_USERNAME=$CONTAINER_USERNAME -e PLUGIN_PASSWORD=$CONTAINER_PASSWORD -e DRONE_TAG=$CI_COMMIT_TAG -v $(pwd):$(pwd) -w $(pwd) - plugins/manifest + docker.io/plugins/manifest secrets: - container_username - container_password diff --git a/Makefile b/Makefile index 1b472c0..45cb1c4 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ PLATFORM ?= linux/amd64,linux/arm64 include versioning.mk -SHELLCHECK_PREFIX := docker run --rm -v ${CURDIR}:/workdir -w /workdir ${DEV_REGISTRY}/drycc/go-dev shellcheck +SHELLCHECK_PREFIX := podman run --rm -v ${CURDIR}:/workdir -w /workdir ${DEV_REGISTRY}/drycc/go-dev shellcheck SHELL_SCRIPTS = $(wildcard rootfs/bin/*) $(shell find "rootfs" -name '*.sh') $(wildcard _scripts/*.sh) # Test processes used in quick unit testing @@ -19,41 +19,38 @@ check-kubectl: exit 2; \ fi -check-docker: - @if [ -z $$(which docker) ]; then \ - echo "Missing \`docker\` client which is required for development"; \ +check-podman: + @if [ -z $$(which podman) ]; then \ + echo "Missing \`podman\` client which is required for development"; \ exit 2; \ fi -build: docker-build +build: podman-build -docker-build: check-docker - docker build ${DOCKER_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE} rootfs - docker tag ${IMAGE} ${MUTABLE_IMAGE} +podman-build: check-podman + podman build ${PODMAN_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE} rootfs + podman tag ${IMAGE} ${MUTABLE_IMAGE} -docker-buildx: check-docker - docker buildx build --build-arg CODENAME=${CODENAME} --platform ${PLATFORM} -t ${IMAGE} rootfs --push +podman-build-test: check-podman + podman build ${PODMAN_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE}.test -f rootfs/Dockerfile.test rootfs -docker-build-test: check-docker - docker build ${DOCKER_BUILD_FLAGS} --build-arg CODENAME=${CODENAME} -t ${IMAGE}.test -f rootfs/Dockerfile.test rootfs - -deploy: check-kubectl docker-build docker-push +deploy: check-kubectl podman-build podman-push kubectl --namespace=drycc patch deployment drycc-$(COMPONENT) --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"$(IMAGE)"}]' -clean: check-docker - docker rmi $(IMAGE) +clean: check-podman + podman rmi $(IMAGE) -full-clean: check-docker - docker images -q $(IMAGE_PREFIX)/$(COMPONENT) | xargs docker rmi -f +full-clean: check-podman + podman images -q $(IMAGE_PREFIX)/$(COMPONENT) | xargs podman rmi -f test: test-style test-unit test-functional -test-style: docker-build-test +test-style: podman-build-test $(shell chown -R 1001:1001 ${CURDIR}) - docker run --rm -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/test-style + podman run --rm -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/test-style ${SHELLCHECK_PREFIX} $(SHELL_SCRIPTS) -test-unit: docker-build-test +test-unit: podman-build-test @echo "Implement in the future" test-functional: @@ -64,6 +61,6 @@ test-integration: upload-coverage: $(eval CI_ENV := $(shell curl -s https://codecov.io/env | bash)) - docker run --rm ${CI_ENV} -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/upload-coverage + podman run --rm ${CI_ENV} -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/upload-coverage -.PHONY: check-kubectl check-docker build docker-build docker-build-test deploy clean commit-hook full-clean test test-style test-unit test-functional test-integration upload-coverage +.PHONY: check-kubectl check-podman build podman-build podman-build-test deploy clean commit-hook full-clean test test-style test-unit test-functional test-integration upload-coverage diff --git a/versioning.mk b/versioning.mk index 02ebe33..4396eab 100644 --- a/versioning.mk +++ b/versioning.mk @@ -10,13 +10,13 @@ info: @echo "Immutable tag: ${IMAGE}" @echo "Mutable tag: ${MUTABLE_IMAGE}" -.PHONY: docker-push -docker-push: docker-mutable-push docker-immutable-push +.PHONY: podman-push +podman-push: podman-mutable-push podman-immutable-push -.PHONY: docker-immutable-push -docker-immutable-push: - docker push ${IMAGE} +.PHONY: podman-immutable-push +podman-immutable-push: + podman push ${IMAGE} -.PHONY: docker-mutable-push -docker-mutable-push: - docker push ${MUTABLE_IMAGE} +.PHONY: podman-mutable-push +podman-mutable-push: + podman push ${MUTABLE_IMAGE} From a9ffe43d1c9953397ab136e61625b11ba6879f14 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 8 Nov 2023 15:43:07 +0800 Subject: [PATCH 27/75] fix(helmbroker): rabbitmq sharding err --- rootfs/helmbroker/celery.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index b66ed18..692719d 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -2,6 +2,8 @@ from celery import Celery +# The queue and exchange names cannot be the same +# otherwise an error will occur when enabling the sharding plugin class Config: # Celery Configuration Options timezone = "Asia/Shanghai" @@ -18,7 +20,9 @@ class Config: worker_max_tasks_per_child = 200 result_expires = 24 * 60 * 60 broker_connection_retry_on_startup = True - task_default_queue = 'helmbroker.priority.low' + task_default_queue = 'low' + task_default_exchange = 'helmbroker.priority' + task_default_routing_key = 'helmbroker.priority.low' worker_cancel_long_running_tasks_on_connection_loss = True From ba52f663526b82cdbb53ba0ca5ec124e175c26b4 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 10 Nov 2023 13:34:56 +0800 Subject: [PATCH 28/75] fix(helmbroker): rabbitmq sharding err --- rootfs/helmbroker/celery.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 692719d..2d10ecd 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -1,10 +1,9 @@ import os +from kombu import Exchange, Queue from celery import Celery -# The queue and exchange names cannot be the same -# otherwise an error will occur when enabling the sharding plugin -class Config: +class Config(object): # Celery Configuration Options timezone = "Asia/Shanghai" enable_utc = True @@ -19,20 +18,38 @@ class Config: task_time_limit = 30 * 60 worker_max_tasks_per_child = 200 result_expires = 24 * 60 * 60 + broker_url = os.environ.get("DRYCC_RABBITMQ_URL", 'amqp://guest:guest@127.0.0.1:5672/') # noqa broker_connection_retry_on_startup = True task_default_queue = 'low' task_default_exchange = 'helmbroker.priority' task_default_routing_key = 'helmbroker.priority.low' + broker_connection_retry_on_startup = True worker_cancel_long_running_tasks_on_connection_loss = True -app = Celery( - 'helmbroker', - broker=os.environ.get("DRYCC_RABBITMQ_URL"), - include=['helmbroker.tasks'] +app = Celery('helmbroker') +app.config_from_object(Config()) +app.conf.update( + task_routes={ + 'helmbroker.tasks': { + 'queue': 'low', + 'exchange': 'helmbroker.priority', + 'routing_key': 'helmbroker.priority.high', + }, + }, + task_queues=( + Queue( + 'low', + exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.low', + queue_arguments={'x-max-priority': 16}, + ), + ), ) +app.autodiscover_tasks() + -app.config_from_object(Config) +app.config_from_object(Config()) if __name__ == '__main__': app.start() From af196b9b099b8cc7d818841c1d694cb7cbf8ade7 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 29 Nov 2023 18:21:37 +0800 Subject: [PATCH 29/75] fix(helmbroker): registered task error --- rootfs/helmbroker/celery.py | 2 +- rootfs/helmbroker/tasks.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 2d10ecd..2c387f4 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -46,7 +46,7 @@ class Config(object): ), ), ) -app.autodiscover_tasks() +app.autodiscover_tasks(("helmbroker.tasks",)) app.config_from_object(Config()) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 0068b47..83c4a81 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -46,7 +46,7 @@ def provision(instance_id: str, details: ProvisionDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - logger.info(f"helm install parameters :{details.parameters}") + logger.debug(f"helm install parameters :{details.parameters}") if details.parameters and "rawValues" in details.parameters \ and details.parameters.get("rawValues", ""): values = str(base64.b64decode(details.parameters["rawValues"]), "utf-8") # noqa @@ -56,7 +56,7 @@ def provision(instance_id: str, details: ProvisionDetails): if details.parameters: for k, v in details.parameters.items(): args.extend(["--set", f"{k}={v}"]) - logger.info(f"helm install args:{args}") + logger.debug(f"helm install args:{args}") status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) if status != 0: From a721e8ddf315d5703a1c31248582bc92e652c59a Mon Sep 17 00:00:00 2001 From: Eamon Date: Mon, 4 Dec 2023 16:51:48 +0800 Subject: [PATCH 30/75] fix(helmbroker): reset the chart of dynamic dependency relationships --- rootfs/helmbroker/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 83c4a81..e5763c7 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -23,7 +23,7 @@ def provision(instance_id: str, details: ProvisionDetails): bind_yaml = f'{chart_path}/templates/bind.yaml' if os.path.exists(bind_yaml): os.remove(bind_yaml) - if os.path.exists(f'{chart_path}/requirements.lock'): + if os.path.exists(f'{chart_path}/Chart.yaml'): args = [ "dependency", "update", From c6c2c968d09af2d1e8258ba993e5539b02c8a6e1 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 6 Dec 2023 10:51:08 +0800 Subject: [PATCH 31/75] fix(helmbroker): verify_parameters error --- rootfs/helmbroker/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 53d59cf..b9ece94 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -316,6 +316,11 @@ def verify_parameters(allow_paras, paras): # sub string, inclusion relationship if not para_key.startswith(allow_para_key): not_allow_paras.append(para_key) + not_allow_paras = list(set(not_allow_paras)) + for allow_para_key in allow_para_keys: + # sub string, inclusion relationship + if para_key.startswith(allow_para_key) and para_key in not_allow_paras: # noqa + not_allow_paras.remove(para_key) not_allow_keys = ",".join(not_allow_paras) return not_allow_keys From ad9bdddd1d4357e7c77b28a2990256ab404e8a5d Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 13 Dec 2023 15:52:42 +0800 Subject: [PATCH 32/75] fix(helmbroker): fix bind error when parameters --- rootfs/helmbroker/broker.py | 5 ++--- rootfs/helmbroker/tasks.py | 43 ++++++++++++++----------------------- rootfs/helmbroker/utils.py | 20 +++++++++++++++-- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index c9fd111..d143616 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -68,7 +68,7 @@ def provision(self, "service_id": details.service_id, "plan_id": details.plan_id, "context": details.context, - "parameters": details.parameters, + "parameters": details.parameters if details.parameters is not None else {}, # noqa }, "last_operation": { "state": OperationState.IN_PROGRESS.value, @@ -150,6 +150,7 @@ def update(self, raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) allow_paras = get_addon_allow_paras(details.service_id) + logger.info(f"service instance update parameters: {details.parameters}") not_allow_keys = verify_parameters(allow_paras, details.parameters) if not_allow_keys: raise ErrBadRequest( @@ -164,8 +165,6 @@ def update(self, details.service_id, details.plan_id) # add the new plan shutil.copytree(addon_plan_path, plan_path) - logger.info(f"service update parameters: {details.parameters}") - logger.info(f"service update parameters type: {type(details.parameters)}") # noqa update.delay(instance_id, details) return UpdateServiceSpec(is_async=True) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index e5763c7..20a892b 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -2,7 +2,6 @@ import time import shutil import yaml -import base64 import logging from openbrokerapi.service_broker import ProvisionDetails, OperationState, \ @@ -11,7 +10,7 @@ from .celery import app from .utils import get_plan_path, get_chart_path, get_cred_value, \ InstanceLock, dump_instance_meta, dump_binding_meta, load_instance_meta, \ - get_instance_file, helm, dump_raw_values + get_instance_file, helm, format_paras_to_helm_args logger = logging.getLogger(__name__) @@ -47,15 +46,7 @@ def provision(instance_id: str, details: ProvisionDetails): f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] logger.debug(f"helm install parameters :{details.parameters}") - if details.parameters and "rawValues" in details.parameters \ - and details.parameters.get("rawValues", ""): - values = str(base64.b64decode(details.parameters["rawValues"]), "utf-8") # noqa - raw_values_file = dump_raw_values(instance_id, values) - args.extend(["-f", raw_values_file]) - details.parameters.pop("rawValues") - if details.parameters: - for k, v in details.parameters.items(): - args.extend(["--set", f"{k}={v}"]) + args = format_paras_to_helm_args(instance_id, details.parameters, args) logger.debug(f"helm install args:{args}") status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) @@ -80,12 +71,12 @@ def update(instance_id: str, details: UpdateDetails): if details.context: data['details']['context'] = details.context if details.parameters: - data['details']['parameters'] = details.parameters - data['last_operation'] = { - "state": OperationState.IN_PROGRESS.value, - "description": ( - "update %s in progress at %s" % (instance_id, time.time())) - } + paras = data['details']['parameters'] + paras.update(details.parameters) + # remove the key which value is null + data['details']['parameters'] = {k: v for k, v in paras.items() if v != ""} # noqa + data['last_operation']["state"] = OperationState.IN_PROGRESS.value + data['last_operation']["description"] = "update %s in progress at %s" % (instance_id, time.time()) dump_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") @@ -105,16 +96,9 @@ def update(instance_id: str, details: UpdateDetails): "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - logger.info(f"helm upgrade parameters: {details.parameters}") - if details.parameters and "rawValues" in details.parameters \ - and details.parameters.get("rawValues", ""): - values = str(base64.b64decode(details.parameters["rawValues"]), "utf-8") # noqa - raw_values_file = dump_raw_values(instance_id, values) - args.extend(["-f", raw_values_file]) - details.parameters.pop("rawValues") - if details.parameters: - for k, v in details.parameters.items(): - args.extend(["--set", f"{k}={v}"]) + paras = data['details']['parameters'] + logger.info(f"helm upgrade parameters: {paras}") + args = format_paras_to_helm_args(instance_id, paras, args) logger.info(f"helm upgrade args:{args}") status, output = helm(instance_id, *args) if status != 0: @@ -156,6 +140,11 @@ def bind(instance_id: str, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] + instance_data = load_instance_meta(instance_id) + paras = instance_data["details"]["parameters"] + logger.info(f"helm template parameters: {paras}") + args = format_paras_to_helm_args(instance_id, paras, args) + logger.info(f"helm template args: {args}") status, templates = helm(instance_id, *args) # output: templates.yaml if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index b9ece94..ea9d04b 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -80,7 +80,7 @@ def helm(instance_id, *args, output_type="text"): def load_instance_meta(instance_id): file = get_instance_file(instance_id) with open(file) as f: - data = json.load(f) + data = json.loads(f.read()) validate(instance=data, schema=INSTANCE_META_SCHEMA) return data @@ -268,7 +268,6 @@ def get_secret_key_value(ns, secret_ref): ] status, output = command("kubectl", *args) if status == 0: - import base64 output = base64.b64decode(output).decode() return status, output @@ -339,3 +338,20 @@ def raw_values_format_keys(raw_values, prefix=''): else: keys.append(new_prefix) return keys + + +def format_paras_to_helm_args(instance_id, parameters, args): + """ + + """ + params = copy.deepcopy(parameters) + if params and "rawValues" in params \ + and params.get("rawValues", ""): + values = str(base64.b64decode(params["rawValues"]), "utf-8") # noqa + raw_values_file = dump_raw_values(instance_id, values) + args.extend(["-f", raw_values_file]) + params.pop("rawValues") + if params: + for k, v in params.items(): + args.extend(["--set", f"{k}={v}"]) + return args From b2ab86c956317930e607b07b3fa7311d5a2c5640 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Fri, 15 Dec 2023 16:53:49 +0800 Subject: [PATCH 33/75] chore(helmbroker): add deprovision log --- build.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..32147bc --- /dev/null +++ b/build.sh @@ -0,0 +1,13 @@ +######################################################################### +# File Name: build.sh +# Author: ma6174 +# mail: ma6174@163.com +# Created Time: 2023年05月10日 星期三 13时14分57秒 +######################################################################### +#!/bin/bash +export CODENAME=bookworm +export DEV_REGISTRY=registry.drycc.cc +make test +#make podman-build +#podman tag registry.drycc.cc/drycc/helmbroker:canary registry.uucin.com/lijianguo/helmbroker:canary +#podman push registry.uucin.com/lijianguo/helmbroker:canary From a562aa5a6464f855c6871bf67357cde5d4dfbaa4 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 20 Dec 2023 14:49:17 +0800 Subject: [PATCH 34/75] chore(helmbroker): add addonValues --- charts/helmbroker/templates/helmbroker-cm.yaml | 2 ++ charts/helmbroker/values.yaml | 3 +++ rootfs/helmbroker/tasks.py | 8 +++++++- rootfs/helmbroker/utils.py | 15 +++++++++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/charts/helmbroker/templates/helmbroker-cm.yaml b/charts/helmbroker/templates/helmbroker-cm.yaml index b9a13ac..05f6e0d 100644 --- a/charts/helmbroker/templates/helmbroker-cm.yaml +++ b/charts/helmbroker/templates/helmbroker-cm.yaml @@ -13,4 +13,6 @@ data: - name: {{ .name }} url: {{ .url }} {{- end }} + addon-values: | + {{- (tpl .Values.addonValues $) | nindent 4 }} {{- end }} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 764e82a..1be72e0 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -58,6 +58,9 @@ celery: extraMatchLabels: app: "drycc-helmbroker-celery" +# Default override of addon values +addonValues: {} + persistence: enabled: true accessMode: ReadWriteMany diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 20a892b..1d398d8 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -10,7 +10,7 @@ from .celery import app from .utils import get_plan_path, get_chart_path, get_cred_value, \ InstanceLock, dump_instance_meta, dump_binding_meta, load_instance_meta, \ - get_instance_file, helm, format_paras_to_helm_args + get_instance_file, helm, dump_addon_values, format_paras_to_helm_args logger = logging.getLogger(__name__) @@ -30,6 +30,7 @@ def provision(instance_id: str, details: ProvisionDetails): ] helm(instance_id, *args) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") + addon_values_file = dump_addon_values(details.service_id, instance_id) args = [ "install", details.context["instance_name"], @@ -41,6 +42,8 @@ def provision(instance_id: str, details: ProvisionDetails): "--timeout", "25m0s", "-f", + addon_values_file, + "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" @@ -80,6 +83,7 @@ def update(instance_id: str, details: UpdateDetails): dump_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") + addon_values_file = dump_addon_values(details.service_id, instance_id) args = [ "upgrade", details.context["instance_name"], @@ -92,6 +96,8 @@ def update(instance_id: str, details: UpdateDetails): "25m0s", "--reuse-values", "-f", + addon_values_file, + "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index ea9d04b..dcfe4ca 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -8,7 +8,7 @@ import copy from jsonschema import validate -from .config import INSTANCES_PATH, ADDONS_PATH +from .config import INSTANCES_PATH, ADDONS_PATH, CONFIG_PATH REGISTRY_CONFIG_SUFFIX = '.config/helm/registry.json' @@ -43,7 +43,7 @@ def helm(instance_id, *args, output_type="text"): "--repository-config", os.path.join(instance_path, REPOSITORY_CONFIG_SUFFIX), ]) - return command("helm", *args, output_type=output_type) + return command("helm", *new_args, output_type=output_type) INSTANCE_META_SCHEMA = { @@ -102,6 +102,17 @@ def dump_raw_values(instance_id, data): return file +def dump_addon_values(service_id, instance_id): + timestamp = time.time() + instance_path = get_instance_path(instance_id) + file = f"{instance_path}/addon-values-{timestamp}.yaml" + with open(file, "w") as f: + with open(f'{CONFIG_PATH}/addon-values', 'r') as f: + addon_values = yaml.load(f.read(), Loader=yaml.Loader) + f.write(yaml.dump(addon_values.get(service_id, {}))) + return file + + BINDING_META_SCHEMA = { "type": "object", "properties": { From 181f7c0b12738f1f3338c18754c87f4aca6ceaac Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 22 Dec 2023 08:56:05 +0800 Subject: [PATCH 35/75] chore(helmbroker): addonValues support version --- rootfs/helmbroker/utils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index dcfe4ca..2b16625 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -106,10 +106,13 @@ def dump_addon_values(service_id, instance_id): timestamp = time.time() instance_path = get_instance_path(instance_id) file = f"{instance_path}/addon-values-{timestamp}.yaml" + service = get_addon_meta(service_id) with open(file, "w") as f: with open(f'{CONFIG_PATH}/addon-values', 'r') as f: addon_values = yaml.load(f.read(), Loader=yaml.Loader) - f.write(yaml.dump(addon_values.get(service_id, {}))) + f.write(yaml.dump( + addon_values.get(service["name"], {}).get(service["version"], {}) + )) return file @@ -217,10 +220,11 @@ def get_addon_meta(service_id): def get_addon_path(service_id, plan_id): service = get_addon_meta(service_id) plan = [plan for plan in service['plans'] if plan['id'] == plan_id][0] - service_name = f'{service["name"]}-{service["version"]}' plan_name = plan['name'] - service_path = f'{ADDONS_PATH}/{service_name}/chart/{service["name"]}' - plan_path = f'{ADDONS_PATH}/{service_name}/plans/{plan_name}' + service_name_path = f'{service["name"]}-{service["version"]}' + base_path = f"{ADDONS_PATH}/{service_name_path}" + service_path = f'{base_path}/chart/{service["name"]}' + plan_path = f'{base_path}/plans/{plan_name}' return service_path, plan_path From 687ce573e6c35ef79fb24de7a7945f893c0c67f8 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Thu, 21 Dec 2023 17:55:32 +0800 Subject: [PATCH 36/75] chore(helmbroker): add debug log --- rootfs/helmbroker/broker.py | 2 +- rootfs/helmbroker/tasks.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index d143616..e95f004 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -150,7 +150,7 @@ def update(self, raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) allow_paras = get_addon_allow_paras(details.service_id) - logger.info(f"service instance update parameters: {details.parameters}") + logger.debug(f"service instance update parameters: {details.parameters}") not_allow_keys = verify_parameters(allow_paras, details.parameters) if not_allow_keys: raise ErrBadRequest( diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 1d398d8..3e537e8 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -103,9 +103,9 @@ def update(instance_id: str, details: UpdateDetails): f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] paras = data['details']['parameters'] - logger.info(f"helm upgrade parameters: {paras}") + logger.debug(f"helm upgrade parameters: {paras}") args = format_paras_to_helm_args(instance_id, paras, args) - logger.info(f"helm upgrade args:{args}") + logger.debug(f"helm upgrade args:{args}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value @@ -148,9 +148,9 @@ def bind(instance_id: str, ] instance_data = load_instance_meta(instance_id) paras = instance_data["details"]["parameters"] - logger.info(f"helm template parameters: {paras}") + logger.debug(f"helm template parameters: {paras}") args = format_paras_to_helm_args(instance_id, paras, args) - logger.info(f"helm template args: {args}") + logger.debug(f"helm template args: {args}") status, templates = helm(instance_id, *args) # output: templates.yaml if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value @@ -199,13 +199,14 @@ def deprovision(instance_id: str): data["last_operation"]["description"] = ( "deprovision %s in progress at %s" % (instance_id, time.time())) dump_instance_meta(instance_id, data) - status, output = helm( - instance_id, + args = [ "uninstall", data["details"]["context"]["instance_name"], "--namespace", data["details"]["context"]["namespace"], - ) + ] + logger.debug(f"helm uninstall args: {args}") + status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value data["last_operation"]["description"] = ( From 400b704491e577f4f0649691d6de7fe55cf93404 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 27 Dec 2023 12:07:06 +0800 Subject: [PATCH 37/75] fix(clean): mistake delete new resource service --- build.sh | 13 ------------- rootfs/helmbroker/broker.py | 2 +- rootfs/helmbroker/cleaner.py | 5 ----- rootfs/helmbroker/tasks.py | 2 +- rootfs/helmbroker/utils.py | 2 +- 5 files changed, 3 insertions(+), 21 deletions(-) delete mode 100755 build.sh diff --git a/build.sh b/build.sh deleted file mode 100755 index 32147bc..0000000 --- a/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -######################################################################### -# File Name: build.sh -# Author: ma6174 -# mail: ma6174@163.com -# Created Time: 2023年05月10日 星期三 13时14分57秒 -######################################################################### -#!/bin/bash -export CODENAME=bookworm -export DEV_REGISTRY=registry.drycc.cc -make test -#make podman-build -#podman tag registry.drycc.cc/drycc/helmbroker:canary registry.uucin.com/lijianguo/helmbroker:canary -#podman push registry.uucin.com/lijianguo/helmbroker:canary diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index e95f004..bd6a257 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -150,7 +150,7 @@ def update(self, raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) allow_paras = get_addon_allow_paras(details.service_id) - logger.debug(f"service instance update parameters: {details.parameters}") + logger.debug(f"service instance update parameters: {details.parameters}") # noqa not_allow_keys = verify_parameters(allow_paras, details.parameters) if not_allow_keys: raise ErrBadRequest( diff --git a/rootfs/helmbroker/cleaner.py b/rootfs/helmbroker/cleaner.py index 0b01814..eeeb5ab 100644 --- a/rootfs/helmbroker/cleaner.py +++ b/rootfs/helmbroker/cleaner.py @@ -6,7 +6,6 @@ from openbrokerapi.service_broker import OperationState from .config import INSTANCES_PATH -from .tasks import deprovision from .utils import get_instance_file, load_instance_meta logger = logging.getLogger(__name__) @@ -21,10 +20,6 @@ def clean_instance(): interval = time.time() - data["last_modified_time"] state = data["last_operation"]["state"] operation = data["last_operation"]["operation"] - if interval > 3600 * 24 and ( - operation == "deprovision" - and state != OperationState.SUCCEEDED): - deprovision.delay(instance_id) if operation == "deprovision": if state == OperationState.SUCCEEDED or ( interval > 3600 * 24 diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 3e537e8..dfcea6e 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -79,7 +79,7 @@ def update(instance_id: str, details: UpdateDetails): # remove the key which value is null data['details']['parameters'] = {k: v for k, v in paras.items() if v != ""} # noqa data['last_operation']["state"] = OperationState.IN_PROGRESS.value - data['last_operation']["description"] = "update %s in progress at %s" % (instance_id, time.time()) + data['last_operation']["description"] = "update %s in progress at %s" % (instance_id, time.time()) # noqa dump_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 2b16625..152f993 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -111,7 +111,7 @@ def dump_addon_values(service_id, instance_id): with open(f'{CONFIG_PATH}/addon-values', 'r') as f: addon_values = yaml.load(f.read(), Loader=yaml.Loader) f.write(yaml.dump( - addon_values.get(service["name"], {}).get(service["version"], {}) + addon_values.get(service["name"], {}).get(service["version"], {}) # noqa )) return file From 8c530c751fe003d46248117177de31515fafb130 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 2 Jan 2024 18:03:51 +0800 Subject: [PATCH 38/75] chore(helmbroker): add required field for allow_parameters --- Makefile | 2 +- rootfs/bin/test-unit | 6 + rootfs/helmbroker/broker.py | 21 ++- rootfs/helmbroker/utils.py | 363 ++++++++++++++++++------------------ rootfs/tests/test_utils.py | 52 ++++++ 5 files changed, 259 insertions(+), 185 deletions(-) create mode 100755 rootfs/bin/test-unit create mode 100644 rootfs/tests/test_utils.py diff --git a/Makefile b/Makefile index 45cb1c4..26df599 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ test-style: podman-build-test ${SHELLCHECK_PREFIX} $(SHELL_SCRIPTS) test-unit: podman-build-test - @echo "Implement in the future" + podman run --rm -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/test-unit test-functional: @echo "Implement functional tests in _tests directory" diff --git a/rootfs/bin/test-unit b/rootfs/bin/test-unit new file mode 100755 index 0000000..ef9755f --- /dev/null +++ b/rootfs/bin/test-unit @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# +# This script is designed to be run inside the container +# + +python -m unittest tests/test_*.py \ No newline at end of file diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index bd6a257..ecd1cc8 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -51,10 +51,14 @@ def provision(self, raise ErrBadRequest( msg="This addon has archived.") allow_paras = get_addon_allow_paras(details.service_id) - not_allow_keys = verify_parameters(allow_paras, details.parameters) + not_allow_keys, required_keys = verify_parameters( + allow_paras, details.parameters) if not_allow_keys: raise ErrBadRequest( - msg="Instance parameters %s does not allowed" % not_allow_keys) + msg="parameters %s does not allowed" % not_allow_keys) + if required_keys: + raise ErrBadRequest( + msg="required parameters %s not exists" % required_keys) os.makedirs(instance_path, exist_ok=True) chart_path, plan_path = ( get_chart_path(instance_id), get_plan_path(instance_id)) @@ -68,7 +72,7 @@ def provision(self, "service_id": details.service_id, "plan_id": details.plan_id, "context": details.context, - "parameters": details.parameters if details.parameters is not None else {}, # noqa + "parameters": details.parameters if details.parameters else {}, }, "last_operation": { "state": OperationState.IN_PROGRESS.value, @@ -150,11 +154,16 @@ def update(self, raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) allow_paras = get_addon_allow_paras(details.service_id) - logger.debug(f"service instance update parameters: {details.parameters}") # noqa - not_allow_keys = verify_parameters(allow_paras, details.parameters) + logger.debug( + f"service instance update parameters: {details.parameters}") + not_allow_keys, required_keys = verify_parameters( + allow_paras, details.parameters) if not_allow_keys: raise ErrBadRequest( - msg="Instance parameters %s does not allowed" % not_allow_keys) + msg="parameters %s does not allowed" % not_allow_keys) + if required_keys: + raise ErrBadRequest( + msg="required parameters %s not exists" % required_keys) if not async_allowed: raise ErrAsyncRequired() if details.plan_id is not None: diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 152f993..7cee329 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -14,38 +14,6 @@ REGISTRY_CONFIG_SUFFIX = '.config/helm/registry.json' REPOSITORY_CACHE_SUFFIX = '.cache/helm/repository' REPOSITORY_CONFIG_SUFFIX = '.config/helm/repository' - - -def command(cmd, *args, output_type="text"): - status, output = subprocess.getstatusoutput("%s %s" % (cmd, " ".join(args))) # noqa - if output_type == "yaml": - return yaml.load(output, Loader=yaml.Loader) - elif output_type == "json": - return json.loads(output) - return status, output - - -get_instance_path = lambda instance_id: os.path.join(INSTANCES_PATH, instance_id) # noqa -get_instance_file = lambda instance_id: os.path.join(get_instance_path(instance_id), "instance.json") # noqa -get_chart_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "chart") # noqa -get_plan_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "plan") # noqa - - -def helm(instance_id, *args, output_type="text"): - instance_path = get_instance_path(instance_id) - new_args = [] - new_args.extend(args) - new_args.extend([ - "--registry-config", - os.path.join(instance_path, REGISTRY_CONFIG_SUFFIX), - "--repository-cache", - os.path.join(instance_path, REPOSITORY_CACHE_SUFFIX), - "--repository-config", - os.path.join(instance_path, REPOSITORY_CONFIG_SUFFIX), - ]) - return command("helm", *new_args, output_type=output_type) - - INSTANCE_META_SCHEMA = { "type": "object", "properties": { @@ -75,47 +43,6 @@ def helm(instance_id, *args, output_type="text"): "last_modified_time": {"type": "number"} }, } - - -def load_instance_meta(instance_id): - file = get_instance_file(instance_id) - with open(file) as f: - data = json.loads(f.read()) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - return data - - -def dump_instance_meta(instance_id, data): - data["last_modified_time"] = time.time() - file = get_instance_file(instance_id) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - with open(file, "w") as f: - f.write(json.dumps(data, sort_keys=True, indent=2)) - - -def dump_raw_values(instance_id, data): - timestamp = time.time() - instance_path = get_instance_path(instance_id) - file = f"{instance_path}/raw-values-{timestamp}.yaml" - with open(file, "w") as f: - f.write(data) - return file - - -def dump_addon_values(service_id, instance_id): - timestamp = time.time() - instance_path = get_instance_path(instance_id) - file = f"{instance_path}/addon-values-{timestamp}.yaml" - service = get_addon_meta(service_id) - with open(file, "w") as f: - with open(f'{CONFIG_PATH}/addon-values', 'r') as f: - addon_values = yaml.load(f.read(), Loader=yaml.Loader) - f.write(yaml.dump( - addon_values.get(service["name"], {}).get(service["version"], {}) # noqa - )) - return file - - BINDING_META_SCHEMA = { "type": "object", "properties": { @@ -133,24 +60,6 @@ def dump_addon_values(service_id, instance_id): "last_modified_time": {"type": "number"} } } - - -def load_binding_meta(instance_id): - file = os.path.join(get_instance_path(instance_id), "binding.json") - with open(file, 'r') as f: - data = json.loads(f.read()) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - return data - - -def dump_binding_meta(instance_id, data): - data["last_modified_time"] = time.time() - file = os.path.join(get_instance_path(instance_id), "binding.json") - validate(instance=data, schema=INSTANCE_META_SCHEMA) - with open(file, "w") as f: - f.write(json.dumps(data, sort_keys=True, indent=2)) - - ADDONS_META_SCHEMA = { "type": "object", "patternProperties": { @@ -193,6 +102,91 @@ def dump_binding_meta(instance_id, data): } +def command(cmd, *args, output_type="text"): + status, output = subprocess.getstatusoutput("%s %s" % (cmd, " ".join(args))) # noqa + if output_type == "yaml": + return yaml.load(output, Loader=yaml.Loader) + elif output_type == "json": + return json.loads(output) + return status, output + + +get_instance_path = lambda instance_id: os.path.join(INSTANCES_PATH, instance_id) # noqa +get_instance_file = lambda instance_id: os.path.join(get_instance_path(instance_id), "instance.json") # noqa +get_chart_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "chart") # noqa +get_plan_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "plan") # noqa + + +def helm(instance_id, *args, output_type="text"): + instance_path = get_instance_path(instance_id) + new_args = [] + new_args.extend(args) + new_args.extend([ + "--registry-config", + os.path.join(instance_path, REGISTRY_CONFIG_SUFFIX), + "--repository-cache", + os.path.join(instance_path, REPOSITORY_CACHE_SUFFIX), + "--repository-config", + os.path.join(instance_path, REPOSITORY_CONFIG_SUFFIX), + ]) + return command("helm", *new_args, output_type=output_type) + + +def load_instance_meta(instance_id): + file = get_instance_file(instance_id) + with open(file) as f: + data = json.loads(f.read()) + validate(instance=data, schema=INSTANCE_META_SCHEMA) + return data + + +def dump_instance_meta(instance_id, data): + data["last_modified_time"] = time.time() + file = get_instance_file(instance_id) + validate(instance=data, schema=INSTANCE_META_SCHEMA) + with open(file, "w") as f: + f.write(json.dumps(data, sort_keys=True, indent=2)) + + +def dump_raw_values(instance_id, data): + timestamp = time.time() + instance_path = get_instance_path(instance_id) + file = f"{instance_path}/raw-values-{timestamp}.yaml" + with open(file, "w") as f: + f.write(data) + return file + + +def dump_addon_values(service_id, instance_id): + timestamp = time.time() + instance_path = get_instance_path(instance_id) + file = f"{instance_path}/addon-values-{timestamp}.yaml" + service = _get_addon_meta(service_id) + with open(file, "w") as f: + with open(f'{CONFIG_PATH}/addon-values', 'r') as f: + addon_values = yaml.load(f.read(), Loader=yaml.Loader) + f.write(yaml.dump( + addon_values.get(service["name"], {}).get(service["version"], {}) # noqa + )) + return file + + +def load_binding_meta(instance_id): + file = os.path.join(get_instance_path(instance_id), "binding.json") + with open(file, 'r') as f: + data = json.loads(f.read()) + validate(instance=data, schema=INSTANCE_META_SCHEMA) + return data + + +def dump_binding_meta(instance_id, data): + data["last_modified_time"] = time.time() + file = os.path.join(get_instance_path(instance_id), "binding.json") + validate(instance=data, schema=INSTANCE_META_SCHEMA) + with open(file, "w") as f: + f.write(json.dumps(data, sort_keys=True, indent=2)) + + def load_addons_meta(): file = os.path.join(ADDONS_PATH, "addons.json") with open(file, 'r') as f: @@ -210,15 +204,8 @@ def dump_addons_meta(data): f.write(json.dumps(data, sort_keys=True, indent=2)) -def get_addon_meta(service_id): - services = load_addons_meta() - service = [addon for addon in [addons for _, addons in services.items()] - if addon['id'] == service_id][0] - return service - - def get_addon_path(service_id, plan_id): - service = get_addon_meta(service_id) + service = _get_addon_meta(service_id) plan = [plan for plan in service['plans'] if plan['id'] == plan_id][0] plan_name = plan['name'] service_name_path = f'{service["name"]}-{service["version"]}' @@ -228,65 +215,36 @@ def get_addon_path(service_id, plan_id): return service_path, plan_path -def get_addon_name(service_id): - service = get_addon_meta(service_id) - return service['name'] - - def get_addon_updateable(service_id): - service = get_addon_meta(service_id) + service = _get_addon_meta(service_id) return service.get('plan_updateable', False) def get_addon_bindable(service_id): - service = get_addon_meta(service_id) + service = _get_addon_meta(service_id) return service.get('bindable', False) def get_addon_allow_paras(service_id): - service = get_addon_meta(service_id) + service = _get_addon_meta(service_id) return service.get('allow_parameters', []) def get_addon_archive(service_id): - service = get_addon_meta(service_id) + service = _get_addon_meta(service_id) return service.get('archive', False) def get_cred_value(ns, source): if source.get('serviceRef'): - return get_service_key_value(ns, source['serviceRef']) + return _get_service_key_value(ns, source['serviceRef']) if source.get('configMapRef'): - return get_config_map_key_value(ns, source['configMapRef']) + return _get_config_map_key_value(ns, source['configMapRef']) if source.get('secretKeyRef'): - return get_secret_key_value(ns, source['secretKeyRef']) + return _get_secret_key_value(ns, source['secretKeyRef']) return -1, 'invalid valueFrom' -def get_service_key_value(ns, service_ref): - args = [ - "get", "svc", service_ref['name'], "-n", ns, '-o', f"jsonpath=\'{service_ref['jsonpath']}\'", # noqa - ] - return command("kubectl", *args) - - -def get_config_map_key_value(ns, config_map_ref): - args = [ - "get", "cm", config_map_ref['name'], "-n", ns, '-o', f"jsonpath=\'{config_map_ref['jsonpath']}\'", # noqa - ] - return command("kubectl", *args) - - -def get_secret_key_value(ns, secret_ref): - args = [ - "get", "secret", secret_ref['name'], "-n", ns, '-o', f"jsonpath=\'{secret_ref['jsonpath']}\'", # noqa - ] - status, output = command("kubectl", *args) - if status == 0: - output = base64.b64decode(output).decode() - return status, output - - class InstanceLock(object): def __init__(self, instance_id): @@ -308,51 +266,24 @@ def __del__(self): fcntl.flock(self.fileno, fcntl.LOCK_UN) -def verify_parameters(allow_paras, paras): +def verify_parameters(allow_parameters, parameters): """verify parameters allowed or not""" - if not paras or not allow_paras: + def merge_parameters(parameters): + raw_para_keys = [] + if "rawValues" in parameters: + raw_values = yaml.safe_load( + base64.b64decode(parameters["rawValues"])) + raw_para_keys = _raw_values_format_keys(raw_values) + parameters.pop("rawValues") + return set(list(parameters.keys()) + raw_para_keys) + + if not parameters or not allow_parameters: return "" - tmp_paras = copy.deepcopy(paras) - # raw_values - raw_para_keys = [] - if "rawValues" in tmp_paras: - raw_values = yaml.safe_load(base64.b64decode(tmp_paras["rawValues"])) - raw_para_keys = raw_values_format_keys(raw_values) - tmp_paras.pop("rawValues") - - para_keys = set(list(tmp_paras.keys()) + raw_para_keys) - para_keys = [k + "." for k in para_keys] - allow_para_keys = [allow_para["name"] + "." for allow_para in allow_paras] # noqa - - not_allow_paras = [] - for para_key in para_keys: - for allow_para_key in allow_para_keys: - # sub string, inclusion relationship - if not para_key.startswith(allow_para_key): - not_allow_paras.append(para_key) - not_allow_paras = list(set(not_allow_paras)) - for allow_para_key in allow_para_keys: - # sub string, inclusion relationship - if para_key.startswith(allow_para_key) and para_key in not_allow_paras: # noqa - not_allow_paras.remove(para_key) - not_allow_keys = ",".join(not_allow_paras) - return not_allow_keys - - -def raw_values_format_keys(raw_values, prefix=''): - """ - {'a': {'b': 1, 'c': {'d': 2, 'e': 3}}, 'f': 4} - -> - ['a.b', 'a.c.d', 'a.c.e', 'a.f'] - """ - keys = [] - for key, value in raw_values.items(): - new_prefix = prefix + '.' + key if prefix else key - if isinstance(value, dict): - keys.extend(raw_values_format_keys(value, new_prefix)) - else: - keys.append(new_prefix) - return keys + parameters = merge_parameters(copy.deepcopy(parameters)) + return ( + ",".join(_verify_allow_parameters(allow_parameters, parameters)), + ",".join(_verify_required_parameters(allow_parameters, parameters)), + ) def format_paras_to_helm_args(instance_id, parameters, args): @@ -370,3 +301,79 @@ def format_paras_to_helm_args(instance_id, parameters, args): for k, v in params.items(): args.extend(["--set", f"{k}={v}"]) return args + + +def _get_addon_meta(service_id): + services = load_addons_meta() + service = [addon for addon in [addons for _, addons in services.items()] + if addon['id'] == service_id][0] + return service + + +def _get_service_key_value(ns, service_ref): + args = [ + "get", "svc", service_ref['name'], "-n", ns, '-o', f"jsonpath=\'{service_ref['jsonpath']}\'", # noqa + ] + return command("kubectl", *args) + + +def _get_config_map_key_value(ns, config_map_ref): + args = [ + "get", "cm", config_map_ref['name'], "-n", ns, '-o', f"jsonpath=\'{config_map_ref['jsonpath']}\'", # noqa + ] + return command("kubectl", *args) + + +def _get_secret_key_value(ns, secret_ref): + args = [ + "get", "secret", secret_ref['name'], "-n", ns, '-o', f"jsonpath=\'{secret_ref['jsonpath']}\'", # noqa + ] + status, output = command("kubectl", *args) + if status == 0: + output = base64.b64decode(output).decode() + return status, output + + +def _raw_values_format_keys(raw_values, prefix=''): + """ + {'a': {'b': 1, 'c': {'d': 2, 'e': 3}}, 'f': 4} + -> + ['a.b', 'a.c.d', 'a.c.e', 'a.f'] + """ + keys = [] + for key, value in raw_values.items(): + new_prefix = prefix + '.' + key if prefix else key + if isinstance(value, dict): + keys.extend(_raw_values_format_keys(value, new_prefix)) + else: + keys.append(new_prefix) + return keys + + +def _verify_allow_parameters(allow_parameters, parameters): + error_parameters = set() + for parameter in parameters: + error = True + for allow_parameter in allow_parameters: + if parameter.startswith("%s." % allow_parameter["name"]) \ + or parameter == allow_parameter["name"]: + error = False + break + if error: + error_parameters.add(parameter) + return error_parameters + + +def _verify_required_parameters(allow_parameters, parameters): + error_parameters = set() + for allow_parameter in allow_parameters: + if allow_parameter.get("required", False): + error = True + for parameter in parameters: + if parameter.startswith("%s." % allow_parameter["name"]) \ + or parameter == allow_parameter["name"]: + error = False + break + if error: + error_parameters.add(allow_parameter["name"]) + return error_parameters diff --git a/rootfs/tests/test_utils.py b/rootfs/tests/test_utils.py new file mode 100644 index 0000000..cf83025 --- /dev/null +++ b/rootfs/tests/test_utils.py @@ -0,0 +1,52 @@ +import unittest +from helmbroker import utils + + +class TestUtils(unittest.TestCase): + + default_allow_parameters = [ + { + "name": "deployment.image", + "required": True, + "description": "deployment.image", + }, + { + "name": "deployment.test1.test2", + "required": False, + "description": "deployment.image", + }, + { + "name": "deployment.test1.test3", + "required": False, + "description": "deployment.image", + }, + ] + + def test_verify_parameters(self): + not_allow_keys, required_keys = utils.verify_parameters( + self.default_allow_parameters, + { + "deployment.image.registry": "registry.drycc.cc", + "deployment.image.repository": "drycc-addons/config-reloader", + "deployment.test1.test2": "test2", + "deployment.test1.test3.test4": "test4", + }, + ) + self.assertEqual(required_keys, '') + self.assertEqual(not_allow_keys, '') + not_allow_keys, required_keys = utils.verify_parameters( + self.default_allow_parameters, + { + "deployment.test1.test3": "deployment.test1.test3", + }, + ) + self.assertEqual(required_keys, 'deployment.image') + self.assertEqual(not_allow_keys, '') + not_allow_keys, required_keys = utils.verify_parameters( + self.default_allow_parameters, + { + "deployment.test1.test9": "deployment.test1.test3", + }, + ) + self.assertEqual(required_keys, 'deployment.image') + self.assertEqual(not_allow_keys, 'deployment.test1.test9') From e58459dfd4b46db080bec9a0d3b700670b3fb997 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 3 Jan 2024 10:21:50 +0800 Subject: [PATCH 39/75] fix(helmbroker): avoid addonValues error --- rootfs/helmbroker/tasks.py | 14 ++++++++------ rootfs/helmbroker/utils.py | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index dfcea6e..a289313 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -30,7 +30,6 @@ def provision(instance_id: str, details: ProvisionDetails): ] helm(instance_id, *args) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") - addon_values_file = dump_addon_values(details.service_id, instance_id) args = [ "install", details.context["instance_name"], @@ -42,12 +41,14 @@ def provision(instance_id: str, details: ProvisionDetails): "--timeout", "25m0s", "-f", - addon_values_file, - "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] + addon_values_file = dump_addon_values(details.service_id, instance_id) + if addon_values_file: + args.insert(9, "-f") + args.insert(10, addon_values_file) logger.debug(f"helm install parameters :{details.parameters}") args = format_paras_to_helm_args(instance_id, details.parameters, args) logger.debug(f"helm install args:{args}") @@ -83,7 +84,6 @@ def update(instance_id: str, details: UpdateDetails): dump_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") - addon_values_file = dump_addon_values(details.service_id, instance_id) args = [ "upgrade", details.context["instance_name"], @@ -96,12 +96,14 @@ def update(instance_id: str, details: UpdateDetails): "25m0s", "--reuse-values", "-f", - addon_values_file, - "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] + addon_values_file = dump_addon_values(details.service_id, instance_id) + if addon_values_file: + args.insert(10, "-f") + args.insert(11, addon_values_file) paras = data['details']['parameters'] logger.debug(f"helm upgrade parameters: {paras}") args = format_paras_to_helm_args(instance_id, paras, args) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 7cee329..9c8d8f3 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -6,10 +6,12 @@ import time import base64 import copy +import logging from jsonschema import validate from .config import INSTANCES_PATH, ADDONS_PATH, CONFIG_PATH +logger = logging.getLogger(__name__) REGISTRY_CONFIG_SUFFIX = '.config/helm/registry.json' REPOSITORY_CACHE_SUFFIX = '.cache/helm/repository' @@ -162,12 +164,19 @@ def dump_addon_values(service_id, instance_id): instance_path = get_instance_path(instance_id) file = f"{instance_path}/addon-values-{timestamp}.yaml" service = _get_addon_meta(service_id) - with open(file, "w") as f: + logger.debug(f"dump_addon_values service: {service}") + if not os.path.exists(f'{CONFIG_PATH}/addon-values'): + return None + with open(file, "w") as fw: with open(f'{CONFIG_PATH}/addon-values', 'r') as f: - addon_values = yaml.load(f.read(), Loader=yaml.Loader) - f.write(yaml.dump( - addon_values.get(service["name"], {}).get(service["version"], {}) # noqa - )) + addons_values = yaml.load(f.read(), Loader=yaml.Loader) + logger.debug(f"dump_addon_values addons_values: {addons_values}") + addon_values = addons_values.get(service["name"], {}).\ + get(service["version"], {}) + logger.debug(f"dump_addon_values addon_values: {addon_values}") + if not addon_values: + return None + fw.write(yaml.dump(addon_values)) return file From 7a4c8434e116237a4f4ed8e6be6cebe219f0a4d9 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 16 Jan 2024 18:11:13 +0800 Subject: [PATCH 40/75] fix(helmbroker): file lock --- rootfs/helmbroker/broker.py | 18 ------------------ rootfs/helmbroker/tasks.py | 18 ++++++++++++++++++ rootfs/helmbroker/utils.py | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index ecd1cc8..db633ff 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -66,24 +66,6 @@ def provision(self, get_addon_path(details.service_id, details.plan_id)) shutil.copytree(addon_chart_path, chart_path) shutil.copytree(addon_plan_path, plan_path) - data = { - "id": instance_id, - "details": { - "service_id": details.service_id, - "plan_id": details.plan_id, - "context": details.context, - "parameters": details.parameters if details.parameters else {}, - }, - "last_operation": { - "state": OperationState.IN_PROGRESS.value, - "operation": "provision", - "description": ( - "provision %s in progress at %s" % ( - instance_id, time.time())) - } - } - with InstanceLock(instance_id): - dump_instance_meta(instance_id, data) provision.delay(instance_id, details) return ProvisionedServiceSpec(state=ProvisionState.IS_ASYNC) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index a289313..380d306 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -18,6 +18,24 @@ @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): with InstanceLock(instance_id): + # create instance.json + dump_instance_meta(instance_id, { + "id": instance_id, + "details": { + "service_id": details.service_id, + "plan_id": details.plan_id, + "context": details.context, + "parameters": details.parameters if details.parameters else {}, + }, + "last_operation": { + "state": OperationState.IN_PROGRESS.value, + "operation": "provision", + "description": ( + "provision %s in progress at %s" % ( + instance_id, time.time())) + } + }) + chart_path = get_chart_path(instance_id) bind_yaml = f'{chart_path}/templates/bind.yaml' if os.path.exists(bind_yaml): diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 9c8d8f3..a2f6d31 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -287,7 +287,7 @@ def merge_parameters(parameters): return set(list(parameters.keys()) + raw_para_keys) if not parameters or not allow_parameters: - return "" + return "", "" parameters = merge_parameters(copy.deepcopy(parameters)) return ( ",".join(_verify_allow_parameters(allow_parameters, parameters)), From 7c53cc6efaa2d73ebc9c125cd999b79975b96fc8 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 17 Jan 2024 11:08:18 +0800 Subject: [PATCH 41/75] chore(style): style error --- rootfs/helmbroker/broker.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index db633ff..a8dfe75 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -1,5 +1,4 @@ import os -import time import shutil import logging from typing import Union, List, Optional @@ -16,9 +15,8 @@ from .utils import get_instance_path, get_chart_path, get_plan_path, \ get_addon_path, get_addon_updateable, get_addon_bindable, InstanceLock, \ - load_instance_meta, load_binding_meta, dump_instance_meta, \ - load_addons_meta, get_addon_allow_paras, verify_parameters, \ - get_addon_archive + load_instance_meta, load_binding_meta, load_addons_meta, \ + get_addon_allow_paras, verify_parameters, get_addon_archive from .tasks import provision, bind, deprovision, update logger = logging.getLogger(__name__) From fb4eaf11bf3f2574beebe70a4ea5f88b490492aa Mon Sep 17 00:00:00 2001 From: lijianguo Date: Tue, 30 Jan 2024 11:57:25 +0800 Subject: [PATCH 42/75] fix(charts): fix drycc-helmbroker-cm addonValues error --- charts/helmbroker/templates/helmbroker-cm.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/charts/helmbroker/templates/helmbroker-cm.yaml b/charts/helmbroker/templates/helmbroker-cm.yaml index 05f6e0d..d13e2ee 100644 --- a/charts/helmbroker/templates/helmbroker-cm.yaml +++ b/charts/helmbroker/templates/helmbroker-cm.yaml @@ -13,6 +13,8 @@ data: - name: {{ .name }} url: {{ .url }} {{- end }} + {{- if .Values.addonValues }} addon-values: | {{- (tpl .Values.addonValues $) | nindent 4 }} + {{- end }} {{- end }} From 98ceef7a3ccf5305d9f56460d711a7a5bda2a12c Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 18 Mar 2024 17:43:58 +0800 Subject: [PATCH 43/75] chore(woodpecker): migrations woodpecker-ci to 2 --- .woodpecker/build-linux.yml | 5 ++--- .woodpecker/chart.yaml | 6 ++---- .woodpecker/manifest.yml | 5 ++--- .woodpecker/test-linux.yml | 5 ++--- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index 8cdd984..f411033 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -3,12 +3,11 @@ matrix: - linux/amd64 - linux/arm64 -platform: ${platform} - labels: type: exec + platform: ${platform} -pipeline: +steps: - name: publish-linux image: bash commands: diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index 3204cf5..20f9ea9 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -1,11 +1,9 @@ -platform: linux/amd64 - labels: type: exec + platform: linux/amd64 -pipeline: +steps: - name: generate-chart - type: local image: bash commands: - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG) diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml index 2d5c4e5..b4735ee 100644 --- a/.woodpecker/manifest.yml +++ b/.woodpecker/manifest.yml @@ -1,9 +1,8 @@ -platform: linux/amd64 - labels: type: exec + platform: linux/amd64 -pipeline: +steps: - name: generate-manifest image: bash commands: diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml index 313179e..d47ccc5 100644 --- a/.woodpecker/test-linux.yml +++ b/.woodpecker/test-linux.yml @@ -3,12 +3,11 @@ matrix: - linux/amd64 - linux/arm64 -platform: ${platform} - labels: type: exec + platform: ${platform} -pipeline: +steps: - name: test-linux image: bash commands: From 05fe6799cd9d625f41e04e0c46ad0a23877af0d8 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 19 Mar 2024 23:05:40 +0800 Subject: [PATCH 44/75] fix(woodpecker): CI_SYSTEM_ARCH env removed --- .woodpecker/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index f411033..782d2f5 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -11,7 +11,7 @@ steps: - name: publish-linux image: bash commands: - - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_ARCH) + - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM) - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - make podman-build podman-immutable-push secrets: From 18957f190b459de1b3ba10fdedd425062b559379 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 20 Mar 2024 14:35:14 +0800 Subject: [PATCH 45/75] chore(charts): change canary app version --- .woodpecker/chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index 20f9ea9..214841c 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -8,7 +8,7 @@ steps: commands: - export VERSION=$(sed 's#v##' <<< $CI_COMMIT_TAG) - export IMAGE_TAG=$([ ! -z $CI_COMMIT_TAG ] && echo \"$VERSION\" || echo \"canary\") - - export APP_VERSION=$([ ! -z $CI_COMMIT_TAG ] && echo $VERSION || echo 1.0.0) + - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION) - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION From 2fb7e07164f48f110b1ea4e22fafd8757c13879b Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 1 Apr 2024 15:40:28 +0800 Subject: [PATCH 46/75] fix(celery): task not execute --- charts/helmbroker/templates/_helpers.tpl | 11 ++-- .../helmbroker-celery-deployment.yaml | 25 ++++++---- .../templates/helmbroker-cronjob-daily.yaml | 5 ++ .../templates/helmbroker-deployment.yaml | 50 +++++++++++-------- charts/helmbroker/values.yaml | 15 ++++++ rootfs/helmbroker/celery.py | 35 +++++++++++-- 6 files changed, 99 insertions(+), 42 deletions(-) diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 1fa77d2..b99e10a 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -30,17 +30,16 @@ env: {{- end }} {{- end }} - {{/* Generate helmbroker deployment limits */}} {{- define "helmbroker.limits" -}} -{{- if or (.Values.limits_cpu) (.Values.limits_memory) }} +{{- if or (.Values.limitsCpu) (.Values.limitsMemory) }} resources: limits: -{{- if (.Values.limits_cpu) }} - cpu: {{.Values.limits_cpu}} +{{- if (.Values.limitsCpu) }} + cpu: {{.Values.limitsCpu}} {{- end }} -{{- if (.Values.limits_memory) }} - memory: {{.Values.limits_memory}} +{{- if (.Values.limitsMemory) }} + memory: {{.Values.limitsMemory}} {{- end }} {{- end }} {{- end }} diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index d29b519..7388b2c 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -37,14 +37,19 @@ spec: - $(DRYCC_HELMBROKER_SERVICE_HOST):$(DRYCC_HELMBROKER_SERVICE_PORT) {{- include "helmbroker.envs" . | indent 10 }} containers: - - name: drycc-helmbroker-celery - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} - imagePullPolicy: {{.Values.imagePullPolicy}} - args: - - /bin/bash - - -c - - celery -A helmbroker worker --autoscale=32,1 --loglevel=info - {{- include "helmbroker.limits" . | indent 10 }} - {{- include "helmbroker.envs" . | indent 10 }} - {{- include "helmbroker.volumeMounts" . | indent 10 }} + - name: drycc-helmbroker-celery + image: {{$.Values.imageRegistry}}/{{$.Values.imageOrg}}/helmbroker:{{$.Values.imageTag}} + imagePullPolicy: {{$.Values.imagePullPolicy}} + {{- if $.Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.command "context" $) | nindent 8 }} + args: {{- include "common.tplvalues.render" (dict "value" $.Values.diagnosticMode.args "context" $) | nindent 8 }} + {{- else }} + args: + - /bin/bash + - -c + - celery --app helmbroker worker --queues helmbroker.low,helmbroker.middle,helmbroker.high --autoscale=32,1 --loglevel=WARNING + {{- end }} + {{- include "helmbroker.limits" $ | indent 8 }} + {{- include "helmbroker.envs" $ | indent 8 }} + {{- include "helmbroker.volumeMounts" $ | indent 8 }} {{- include "helmbroker.volumes" . | indent 6 }} diff --git a/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml b/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml index c608862..909da7d 100644 --- a/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml +++ b/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml @@ -21,10 +21,15 @@ spec: - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} imagePullPolicy: {{.Values.imagePullPolicy}} name: drycc-helmbroker-cleaner + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 14 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 14 }} + {{- else }} args: - /bin/bash - -c - python -m helmbroker.cleaner + {{- end }} {{- include "helmbroker.envs" . | indent 12 }} {{- include "helmbroker.volumeMounts" . | indent 12 }} {{- include "helmbroker.volumes" . | indent 10 }} \ No newline at end of file diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 8de5661..ee1fa74 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -46,26 +46,32 @@ spec: - $(DRYCC_RABBITMQ_URL) {{- include "helmbroker.envs" . | indent 10 }} containers: - - name: drycc-helmbroker - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} - imagePullPolicy: {{.Values.imagePullPolicy}} - livenessProbe: - httpGet: - path: /healthz - port: 8000 - initialDelaySeconds: 30 - timeoutSeconds: 10 - readinessProbe: - httpGet: - path: /readiness - port: 8000 - initialDelaySeconds: 30 - timeoutSeconds: 10 - periodSeconds: 5 - ports: - - containerPort: 8000 - name: http - {{- include "helmbroker.limits" . | indent 10 }} - {{- include "helmbroker.envs" . | indent 10 }} - {{- include "helmbroker.volumeMounts" . | indent 10 }} + - name: drycc-helmbroker + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} + imagePullPolicy: {{.Values.imagePullPolicy}} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 8 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 8 }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + livenessProbe: + httpGet: + path: /healthz + port: 8000 + initialDelaySeconds: 30 + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 8000 + initialDelaySeconds: 30 + timeoutSeconds: 10 + periodSeconds: 5 + {{- end }} + ports: + - containerPort: 8000 + name: http + {{- include "helmbroker.limits" . | indent 8 }} + {{- include "helmbroker.envs" . | indent 8 }} + {{- include "helmbroker.volumeMounts" . | indent 8 }} {{- include "helmbroker.volumes" . | indent 6 }} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 1be72e0..2e457b9 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -6,6 +6,21 @@ replicas: 1 # limitsCpu: "100m" # limitsMemory: "50Mi" +## Enable diagnostic mode +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers + ## + args: + - infinity + ## config the helm-broker repositories repositories: - name: drycc-helm-broker diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 2c387f4..7923690 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -20,7 +20,7 @@ class Config(object): result_expires = 24 * 60 * 60 broker_url = os.environ.get("DRYCC_RABBITMQ_URL", 'amqp://guest:guest@127.0.0.1:5672/') # noqa broker_connection_retry_on_startup = True - task_default_queue = 'low' + task_default_queue = 'helmbroker.low' task_default_exchange = 'helmbroker.priority' task_default_routing_key = 'helmbroker.priority.low' broker_connection_retry_on_startup = True @@ -31,19 +31,46 @@ class Config(object): app.config_from_object(Config()) app.conf.update( task_routes={ - 'helmbroker.tasks': { - 'queue': 'low', + 'helmbroker.tasks.provision': { + 'queue': 'helmbroker.high', 'exchange': 'helmbroker.priority', 'routing_key': 'helmbroker.priority.high', }, + 'helmbroker.tasks.update': { + 'queue': 'helmbroker.high', + 'exchange': 'helmbroker.priority', + 'routing_key': 'helmbroker.priority.high', + }, + 'helmbroker.tasks.bind': { + 'queue': 'helmbroker.high', + 'exchange': 'helmbroker.priority', + 'routing_key': 'helmbroker.priority.high', + }, + 'helmbroker.tasks.deprovision': { + 'queue': 'helmbroker.middle', + 'exchange': 'helmbroker.priority', + 'routing_key': 'helmbroker.priority.middle', + }, }, task_queues=( Queue( - 'low', + 'helmbroker.low', exchange=Exchange('helmbroker.priority', type="direct"), routing_key='helmbroker.priority.low', queue_arguments={'x-max-priority': 16}, ), + Queue( + 'helmbroker.high', + exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.high', + queue_arguments={'x-max-priority': 64}, + ), + Queue( + 'helmbroker.middle', + exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.middle', + queue_arguments={'x-max-priority': 32}, + ), ), ) app.autodiscover_tasks(("helmbroker.tasks",)) From 7d01cabc0db0791965474eaac7b3783ebcdf3178 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 23 Apr 2024 11:17:01 +0800 Subject: [PATCH 47/75] chore(deps): bump all requirementsversion --- rootfs/requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index 9a30c09..df3a567 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -1,6 +1,6 @@ -PyYAML==6.0 -gunicorn==20.1.0 -openbrokerapi==4.5.5 +PyYAML==6.0.1 +gunicorn==22.0.0 +openbrokerapi==4.6.0 requests==2.31.0 -celery==5.3.1 -jsonschema==4.17.3 \ No newline at end of file +celery==5.4.0 +jsonschema==4.21.1 \ No newline at end of file From d7e71fb0efa61c35b033bc9e3407ec46ca8c8be9 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Thu, 9 May 2024 18:20:26 +0800 Subject: [PATCH 48/75] chore(helmbroker): bind use namespace --- rootfs/helmbroker/tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 380d306..2e6f6e6 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -164,7 +164,9 @@ def bind(instance_id: str, "-f", values_file, "--set", - f"fullnameOverride=helmbroker-{details.context['instance_name']}" + f"fullnameOverride=helmbroker-{details.context['instance_name']}", + "--namespace", + details.context["namespace"], ] instance_data = load_instance_meta(instance_id) paras = instance_data["details"]["parameters"] From bb95893b3d23bb0a29e427a7837ce3a2e4448bb7 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 31 Jul 2024 15:48:15 +0800 Subject: [PATCH 49/75] feat(database): add redis cache --- charts/helmbroker/Chart.yaml | 5 +- charts/helmbroker/templates/_helpers.tpl | 31 +- .../templates/helmbroker-cronjob-daily.yaml | 1 + .../templates/helmbroker-deployment.yaml | 20 +- charts/helmbroker/values.yaml | 16 +- rootfs/Dockerfile | 2 +- rootfs/helmbroker/broker.py | 70 ++--- rootfs/helmbroker/celery.py | 2 +- rootfs/helmbroker/cleaner.py | 3 +- rootfs/helmbroker/config.py | 17 +- rootfs/helmbroker/database/__init__.py | 0 rootfs/helmbroker/database/fetch.py | 100 ++++++ rootfs/helmbroker/database/metadata.py | 187 +++++++++++ rootfs/helmbroker/database/query.py | 104 +++++++ rootfs/helmbroker/database/savepoint.py | 54 ++++ rootfs/helmbroker/gunicorn/config.py | 4 +- rootfs/helmbroker/loader.py | 120 ------- rootfs/helmbroker/tasks.py | 293 ++++++++---------- rootfs/helmbroker/utils.py | 285 +---------------- rootfs/requirements.txt | 1 + rootfs/setup.cfg | 4 + 21 files changed, 692 insertions(+), 627 deletions(-) create mode 100644 rootfs/helmbroker/database/__init__.py create mode 100644 rootfs/helmbroker/database/fetch.py create mode 100644 rootfs/helmbroker/database/metadata.py create mode 100644 rootfs/helmbroker/database/query.py create mode 100644 rootfs/helmbroker/database/savepoint.py delete mode 100644 rootfs/helmbroker/loader.py create mode 100644 rootfs/setup.cfg diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index 4ef1dc8..da3007f 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -6,8 +6,11 @@ dependencies: - name: common repository: oci://registry.drycc.cc/charts version: ~1.1.2 + - name: redis + repository: oci://registry.drycc.cc/charts + version: x.x.x - name: rabbitmq - repository: oci://registry.drycc.cc/charts-testing + repository: oci://registry.drycc.cc/charts version: x.x.x description: Drycc Workflow helmbroker. maintainers: diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index b99e10a..39282a7 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -3,26 +3,43 @@ env: - name: "TZ" value: {{ .Values.time_zone | default "UTC" | quote }} -- name: USERNAME +- name: HELMBROKER_USERNAME value: {{ if .Values.username | default "" | ne "" }}{{ .Values.username }}{{ else }}{{ randAlphaNum 32 }}{{ end }} -- name: PASSWORD +- name: HELMBROKER_PASSWORD value: {{ if .Values.password | default "" | ne "" }}{{ .Values.password }}{{ else }}{{ randAlphaNum 32 }}{{ end }} {{- if (.Values.rabbitmqUrl) }} -- name: DRYCC_RABBITMQ_URL +- name: HELMBROKER_RABBITMQ_URL value: {{ .Values.rabbitmqUrl }} {{- else if eq .Values.global.rabbitmqLocation "on-cluster" }} -- name: "DRYCC_RABBITMQ_USERNAME" +- name: "HELMBROKER_RABBITMQ_USERNAME" valueFrom: secretKeyRef: name: rabbitmq-creds key: username -- name: "DRYCC_RABBITMQ_PASSWORD" +- name: "HELMBROKER_RABBITMQ_PASSWORD" valueFrom: secretKeyRef: name: rabbitmq-creds key: password -- name: "DRYCC_RABBITMQ_URL" - value: "amqp://$(DRYCC_RABBITMQ_USERNAME):$(DRYCC_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" +- name: "HELMBROKER_RABBITMQ_URL" + value: "amqp://$(HELMBROKER_RABBITMQ_USERNAME):$(HELMBROKER_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" +{{- end }} +{{- if (.Values.redisUrl) }} +- name: HELMBROKER_REDIS_URL + value: {{ .Values.redisUrl }} +{{- else if eq .Values.global.redisLocation "on-cluster" }} +- name: "HELMBROKER_REDIS_ADDRS" + valueFrom: + secretKeyRef: + name: redis-creds + key: addrs +- name: "HELMBROKER_REDIS_PASSWORD" + valueFrom: + secretKeyRef: + name: redis-creds + key: password +- name: "HELMBROKER_REDIS_URL" + value: "redis://:$(HELMBROKER_REDIS_PASSWORD)@$(HELMBROKER_REDIS_ADDRS)/0" {{- end }} {{- range $key, $value := .Values.environment }} - name: {{ $key }} diff --git a/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml b/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml index 909da7d..825e3c8 100644 --- a/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml +++ b/charts/helmbroker/templates/helmbroker-cronjob-daily.yaml @@ -29,6 +29,7 @@ spec: - /bin/bash - -c - python -m helmbroker.cleaner + python -m helmbroker.database.fetch {{- end }} {{- include "helmbroker.envs" . | indent 12 }} {{- include "helmbroker.volumeMounts" . | indent 12 }} diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index ee1fa74..7a711ed 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -27,15 +27,6 @@ spec: nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.api.nodeAffinityPreset.type "key" .Values.api.nodeAffinityPreset.key "values" .Values.api.nodeAffinityPreset.values ) | nindent 10 }} serviceAccount: drycc-helmbroker initContainers: - - name: loader - image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} - imagePullPolicy: {{.Values.imagePullPolicy}} - args: - - /bin/bash - - -c - - python -m helmbroker.loader - {{- include "helmbroker.envs" . | indent 10 }} - {{- include "helmbroker.volumeMounts" . | indent 10 }} - name: drycc-helmbroker-init image: registry.drycc.cc/drycc/python-dev:latest imagePullPolicy: {{.Values.imagePullPolicy}} @@ -43,8 +34,17 @@ spec: - netcat - -v - -u - - $(DRYCC_RABBITMQ_URL) + - $(HELMBROKER_REDIS_URL),$(HELMBROKER_RABBITMQ_URL) {{- include "helmbroker.envs" . | indent 10 }} + - name: drycc-helmbroker-fetch + image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} + imagePullPolicy: {{.Values.imagePullPolicy}} + args: + - /bin/bash + - -c + - python -m helmbroker.database.fetch + {{- include "helmbroker.envs" . | indent 10 }} + {{- include "helmbroker.volumeMounts" . | indent 10 }} containers: - name: drycc-helmbroker image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 2e457b9..d8c5375 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -33,6 +33,9 @@ celeryReplicas: 1 username: admin password: admin +# Configuring this will no longer use the built-in redis component +redisUrl: "" + # Configuring this will no longer use the built-in rabbitmq component rabbitmqUrl: "" @@ -40,8 +43,8 @@ rabbitmqUrl: "" # can be specified as key-value pairs under environment # this is usually a non required setting. environment: - # DRYCC_DEBUG: true - # HELMBROKER_ROOT: /etc/helmbroker + # HELMBROKER_DEBUG: true + # HELMBROKER_CONFIG_ROOT: /etc/helmbroker api: nodeAffinityPreset: @@ -73,6 +76,10 @@ celery: extraMatchLabels: app: "drycc-helmbroker-celery" +# drycc redis replicas must always be set to 1 +redis: + replicas: 1 + # Default override of addon values addonValues: {} @@ -84,6 +91,11 @@ persistence: volumeName: "" global: + # Set the location of Workflow's redis instance + # Valid values are: + # - on-cluster: Run Redis within the Kubernetes cluster + # - off-cluster: Run Redis outside the Kubernetes cluster (configure in controller section) + redisLocation: "on-cluster" # Set the location of Workflow's rabbitmq instance # Valid values are: # - on-cluster: Run Rabbitmq within the Kubernetes cluster diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 81562a6..806f7c7 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -15,7 +15,7 @@ COPY . ${DRYCC_HOME_DIR} WORKDIR ${DRYCC_HOME_DIR} RUN buildDeps='musl-dev' \ - && install-packages ${buildDeps} openssl ca-certificates \ + && install-packages ${buildDeps} patch openssl ca-certificates \ && install-stack python ${PYTHON_VERSION} \ && install-stack helm ${HELM_VERSION} \ && install-stack kubectl ${KUBECTL_VERSION} && . init-stack \ diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index a8dfe75..561299e 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -6,17 +6,19 @@ from openbrokerapi.catalog import ServicePlan from openbrokerapi.errors import ErrInstanceAlreadyExists, ErrAsyncRequired, \ ErrBindingAlreadyExists, ErrBadRequest, ErrInstanceDoesNotExist, \ - ServiceException + ServiceException, ErrBindingDoesNotExist from openbrokerapi.service_broker import ServiceBroker, Service, \ ProvisionDetails, ProvisionedServiceSpec, ProvisionState, GetBindingSpec, \ BindDetails, Binding, BindState, UnbindDetails, UnbindSpec, \ UpdateDetails, UpdateServiceSpec, DeprovisionDetails, \ DeprovisionServiceSpec, LastOperation, OperationState -from .utils import get_instance_path, get_chart_path, get_plan_path, \ - get_addon_path, get_addon_updateable, get_addon_bindable, InstanceLock, \ - load_instance_meta, load_binding_meta, load_addons_meta, \ - get_addon_allow_paras, verify_parameters, get_addon_archive +from .utils import verify_parameters, new_instance_lock +from .database.fetch import fetch_chart_plan +from .database.query import get_instance_path, get_chart_path, get_plan_path, \ + get_addon_updateable, get_addon_bindable, get_addon_allow_params, \ + get_addon_archive, get_binding_file, get_instance_file +from .database.metadata import load_instance_meta, load_binding_meta, load_addons_meta from .tasks import provision, bind, deprovision, update logger = logging.getLogger(__name__) @@ -48,9 +50,9 @@ def provision(self, if get_addon_archive(details.service_id): raise ErrBadRequest( msg="This addon has archived.") - allow_paras = get_addon_allow_paras(details.service_id) + allow_params = get_addon_allow_params(details.service_id) not_allow_keys, required_keys = verify_parameters( - allow_paras, details.parameters) + allow_params, details.parameters) if not_allow_keys: raise ErrBadRequest( msg="parameters %s does not allowed" % not_allow_keys) @@ -58,12 +60,8 @@ def provision(self, raise ErrBadRequest( msg="required parameters %s not exists" % required_keys) os.makedirs(instance_path, exist_ok=True) - chart_path, plan_path = ( - get_chart_path(instance_id), get_plan_path(instance_id)) - addon_chart_path, addon_plan_path = ( - get_addon_path(details.service_id, details.plan_id)) - shutil.copytree(addon_chart_path, chart_path) - shutil.copytree(addon_plan_path, plan_path) + chart_path, plan_path = get_chart_path(instance_id), get_plan_path(instance_id) + fetch_chart_plan(details.service_id, chart_path, details.plan_id, plan_path) provision.delay(instance_id, details) return ProvisionedServiceSpec(state=ProvisionState.IS_ASYNC) @@ -114,10 +112,9 @@ def unbind(self, async_allowed: bool, **kwargs ) -> UnbindSpec: - instance_path = get_instance_path(instance_id) - binding_info = f'{instance_path}/binding.json' - if os.path.exists(binding_info): - os.remove(binding_info) + binding_file = get_binding_file(instance_id) + if os.path.exists(binding_file): + os.remove(binding_file) return UnbindSpec(is_async=False) def update(self, @@ -133,11 +130,11 @@ def update(self, if not is_plan_updateable: raise ErrBadRequest( msg="Instance %s does not updateable" % instance_id) - allow_paras = get_addon_allow_paras(details.service_id) + allow_params = get_addon_allow_params(details.service_id) logger.debug( f"service instance update parameters: {details.parameters}") not_allow_keys, required_keys = verify_parameters( - allow_paras, details.parameters) + allow_params, details.parameters) if not_allow_keys: raise ErrBadRequest( msg="parameters %s does not allowed" % not_allow_keys) @@ -147,13 +144,8 @@ def update(self, if not async_allowed: raise ErrAsyncRequired() if details.plan_id is not None: - plan_path = get_plan_path(instance_id) - # delete the pre plan - shutil.rmtree(plan_path, ignore_errors=True) - _, addon_plan_path = get_addon_path( - details.service_id, details.plan_id) - # add the new plan - shutil.copytree(addon_plan_path, plan_path) + chart_path, plan_path = get_chart_path(instance_id), get_plan_path(instance_id) + fetch_chart_plan(details.service_id, chart_path, details.plan_id, plan_path) update.delay(instance_id, details) return UpdateServiceSpec(is_async=True) @@ -164,7 +156,7 @@ def deprovision(self, **kwargs) -> DeprovisionServiceSpec: if not os.path.exists(get_instance_path(instance_id)): raise ErrInstanceDoesNotExist() - with InstanceLock(instance_id): + with new_instance_lock(instance_id): data = load_instance_meta(instance_id) operation = data["last_operation"]["operation"] if operation == "provision": @@ -181,11 +173,13 @@ def last_operation(self, operation_data: Optional[str], **kwargs ) -> LastOperation: - data = load_instance_meta(instance_id) - return LastOperation( - OperationState(data["last_operation"]["state"]), - data["last_operation"]["description"] - ) + if os.path.exists(get_instance_file(instance_id)): + data = load_instance_meta(instance_id) + return LastOperation( + OperationState(data["last_operation"]["state"]), + data["last_operation"]["description"] + ) + raise ErrInstanceDoesNotExist() def last_binding_operation(self, instance_id: str, @@ -193,8 +187,10 @@ def last_binding_operation(self, operation_data: Optional[str], **kwargs ) -> LastOperation: - data = load_binding_meta(instance_id) - return LastOperation( - OperationState(data["last_operation"]["state"]), - data["last_operation"]["description"] - ) + if os.path.exists(get_binding_file(instance_id)): + data = load_binding_meta(instance_id) + return LastOperation( + OperationState(data["last_operation"]["state"]), + data["last_operation"]["description"] + ) + raise ErrBindingDoesNotExist() diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 7923690..e9699d2 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -18,7 +18,7 @@ class Config(object): task_time_limit = 30 * 60 worker_max_tasks_per_child = 200 result_expires = 24 * 60 * 60 - broker_url = os.environ.get("DRYCC_RABBITMQ_URL", 'amqp://guest:guest@127.0.0.1:5672/') # noqa + broker_url = os.environ.get("HELMBROKER_RABBITMQ_URL", 'amqp://guest:guest@127.0.0.1:5672/') broker_connection_retry_on_startup = True task_default_queue = 'helmbroker.low' task_default_exchange = 'helmbroker.priority' diff --git a/rootfs/helmbroker/cleaner.py b/rootfs/helmbroker/cleaner.py index eeeb5ab..6cd456a 100644 --- a/rootfs/helmbroker/cleaner.py +++ b/rootfs/helmbroker/cleaner.py @@ -6,7 +6,8 @@ from openbrokerapi.service_broker import OperationState from .config import INSTANCES_PATH -from .utils import get_instance_file, load_instance_meta +from .database.query import get_instance_file +from .database.metadata import load_instance_meta logger = logging.getLogger(__name__) diff --git a/rootfs/helmbroker/config.py b/rootfs/helmbroker/config.py index 682d58a..6def522 100644 --- a/rootfs/helmbroker/config.py +++ b/rootfs/helmbroker/config.py @@ -1,14 +1,17 @@ import os -HELMBROKER_ROOT = os.environ.get("HELMBROKER_ROOT", '/etc/helmbroker') -ADDONS_PATH = os.path.join(HELMBROKER_ROOT, 'addons') -CONFIG_PATH = os.path.join(HELMBROKER_ROOT, 'config') -INSTANCES_PATH = os.path.join(HELMBROKER_ROOT, 'instances') +CONFIG_ROOT = os.environ.get("HELMBROKER_CONFIG_ROOT", '/etc/helmbroker') +ADDONS_PATH = os.path.join(CONFIG_ROOT, 'addons') +CONFIG_PATH = os.path.join(CONFIG_ROOT, 'config') +INSTANCES_PATH = os.path.join(CONFIG_ROOT, 'instances') -USERNAME = os.environ.get('USERNAME') -PASSWORD = os.environ.get('PASSWORD') + +USERNAME = os.environ.get('HELMBROKER_USERNAME') +PASSWORD = os.environ.get('HELMBROKER_PASSWORD') + +REDIS_URL = os.environ.get("HELMBROKER_REDIS_URL", 'redis://localhost:6379/0') class Config: - DEBUG = bool(os.environ.get('DRYCC_DEBUG', True)) + DEBUG = bool(os.environ.get('HELMBROKER_DEBUG', True)) diff --git a/rootfs/helmbroker/database/__init__.py b/rootfs/helmbroker/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rootfs/helmbroker/database/fetch.py b/rootfs/helmbroker/database/fetch.py new file mode 100644 index 0000000..4fe252f --- /dev/null +++ b/rootfs/helmbroker/database/fetch.py @@ -0,0 +1,100 @@ +import os +import tarfile +import requests +import yaml +import json +import glob +import shutil +import tempfile +import collections + + +def fetch_addons(repository): + if not repository: + return + temp = tempfile.TemporaryDirectory() + try: + index_name = repository['url'].split('/')[-1] + # download index.yaml + remote_index = requests.get(repository['url']).content.decode(encoding="utf-8") + # new index + with open(f"{temp.name}/{index_name}", 'w') as f: + f.write(remote_index) + remote_index = yaml.load(remote_index, Loader=yaml.Loader) + # save index.yaml addons + download_urls = {} + for addon_name, addon_metas in remote_index.get('entries', {}).items(): + download_urls[addon_name] = {} + for addon_meta in addon_metas: + url = "/".join(repository["url"].split("/")[0:-1]) + meta_name = f'{addon_name}-{addon_meta["version"]}' + addon_tgz_url = f'{url}/{meta_name}.tgz' + _fetch_addon(addon_tgz_url, f'{temp.name}/{meta_name}') + return _read_addons_meta(temp.name) + finally: + temp.cleanup() + + +def fetch_chart_plan(addon_id, chart_path, plan_id, plan_path): + from .query import get_addon_meta + addon_meta = get_addon_meta(addon_id) + addon_plan = [plan for plan in addon_meta['plans'] if plan['id'] == plan_id][0] + temp = tempfile.TemporaryDirectory() + try: + _fetch_addon(addon_meta['url'], temp.name) + shutil.rmtree(chart_path, ignore_errors=True) + shutil.rmtree(plan_path, ignore_errors=True) + shutil.copytree(os.path.join(temp.name, "chart", addon_meta["name"]), chart_path) + shutil.copytree(os.path.join(temp.name, "plans", addon_plan['name']), plan_path) + finally: + temp.cleanup() + + +def _fetch_addon(url, dest): + with tempfile.TemporaryFile(suffix=".tgz") as tgz_file: + tgz_file.write(requests.get(url).content) + tgz_file.flush() + tgz_file.seek(0) + os.makedirs(dest, exist_ok=True) + with tarfile.open(fileobj=tgz_file, mode="r:gz") as tarobj: + for tarinfo in tarobj: + tarobj.extract(tarinfo.name, dest) + filename1 = os.path.join(dest, "meta.yaml") + with open(filename1, "r") as f1: + meta = yaml.load(stream=f1, Loader=yaml.Loader) + meta['url'] = url + meta['version'] = str(meta['version']) + meta['tags'] = [tag.strip() for tag in meta.get('tags').split(',') if tag.strip()] + with open(os.path.join(dest, "meta.json"), "w") as f2: + json.dump(meta, f2) + os.remove(filename1) + + +def _read_addons_meta(addons_path): + addons_meta = collections.OrderedDict() + for metafile in glob.glob(os.path.join(addons_path, "*", "meta.json")): + with open(metafile) as f1: + meta = json.load(f1) + meta['plans'] = [] + metapath = os.path.join(os.path.dirname(metafile)) + for planfile in glob.glob(os.path.join(metapath, "plans", "*", "meta.yaml")): + with open(planfile, 'r') as f2: + plan = yaml.load(f2.read(), Loader=yaml.Loader) + meta["plans"].append(plan) + addons_meta[meta['displayName']] = meta + return addons_meta + + +def main(): + from ..config import CONFIG_PATH + from .metadata import save_addons_meta + addons_meta = collections.OrderedDict() + with open(f'{CONFIG_PATH}/repositories', 'r') as f: + repositories = yaml.load(f.read(), Loader=yaml.Loader) + for repository in repositories: + addons_meta.update(fetch_addons(repository)) + save_addons_meta(addons_meta) + + +if __name__ == '__main__': + main() diff --git a/rootfs/helmbroker/database/metadata.py b/rootfs/helmbroker/database/metadata.py new file mode 100644 index 0000000..6b37041 --- /dev/null +++ b/rootfs/helmbroker/database/metadata.py @@ -0,0 +1,187 @@ +import os +import json +import time +import logging +import jsonschema + +from redis import client +from ..config import ADDONS_PATH, REDIS_URL + +logger = logging.getLogger(__name__) + +INSTANCE_META_SCHEMA = { + "type": "object", + "properties": { + "id": {"type": "string"}, + "details": { + "type": "object", + "properties": { + "service_id": {"type": "string"}, + "plan_id": {"type": "string"}, + "context": {"type": "object"}, + "parameters": { + 'oneOf': [{'type': 'object'}, {'type': 'null'}] + }, + }, + "required": [ + "service_id", "plan_id", "context" + ] + }, + "last_operation": { + "type": "object", + "properties": { + "state": {"type": "string"}, + "operation": {"type": "string"}, + "description": {"type": "string"} + } + }, + "last_modified_time": {"type": "number"} + }, +} + +BINDING_META_SCHEMA = { + "type": "object", + "properties": { + "id": {"type": "string"}, + "credentials": { + "type": "object", + }, + "last_operation": { + "type": "object", + "properties": { + "state": {"type": "string"}, + "description": {"type": "string"} + } + }, + "last_modified_time": {"type": "number"} + } +} + +ADDONS_META_SCHEMA = { + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "version": {"type": "string"}, + "bindable": {"type": "boolean"}, + "instances_retrievable": {"type": "boolean"}, + "bindings_retrievable": {"type": "boolean"}, + "allow_context_updates": {"type": "boolean"}, + "description": {"type": "string"}, + "tags": {"type": "array", "items": {"type": "string"}}, + "requires": {"type": "array"}, + "metadata": {"type": "object"}, + "plan_updateable": {"type": "boolean"}, + "dashboard_client": {"type": "object"}, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "description": {"type": "string"}, + "metadata": {"type": "object"}, + "free": {"type": "boolean"}, + "bindable": {"type": "boolean"}, + "binding_rotatable": {"type": "boolean"}, + "plan_updateable": {"type": "boolean"}, + "schemas": {"type": "object"}, + "maximum_polling_duration": {"type": "integer"}, + "maintenance_info": {"type": "object"}, + }, + "required": ["id", "name", "description"] + }, + "minItems": 1, + + }, + }, + "required": ["id", "name", "description", "bindable", "version", "plans"] + }, + }, + "additionalProperties": False, +} + + +def save_instance_meta(instance_id, data): + cache_key = f"helmbroker:instance:{instance_id}" + from .query import get_instance_file + data["last_modified_time"] = time.time() + file = get_instance_file(instance_id) + jsonschema.validate(instance=data, schema=INSTANCE_META_SCHEMA) + + json_data = json.dumps(data, sort_keys=True, indent=2) + with open(file, "w") as f: + f.write(json_data) + redis = client.Redis.from_url(REDIS_URL) + redis.set(cache_key, json_data) + + +def load_instance_meta(instance_id): + cache_key = f"helmbroker:instance:{instance_id}" + redis = client.Redis.from_url(REDIS_URL) + + json_data = redis.get(cache_key) + if not json_data: + from .query import get_instance_file + file = get_instance_file(instance_id) + with open(file) as f: + json_data = f.read() + redis.set(cache_key, json_data) + return json.loads(json_data) + + +def save_binding_meta(instance_id, data): + from .query import get_binding_file + cache_key = f"helmbroker:binding:{instance_id}" + data["last_modified_time"] = time.time() + file = get_binding_file(instance_id) + jsonschema.validate(instance=data, schema=BINDING_META_SCHEMA) + + json_data = json.dumps(data, sort_keys=True, indent=2) + with open(file, "w") as f: + f.write(json_data) + redis = client.Redis.from_url(REDIS_URL) + redis.set(cache_key, json_data) + + +def load_binding_meta(instance_id): + from .query import get_binding_file + cache_key = f"helmbroker:binding:{instance_id}" + redis = client.Redis.from_url(REDIS_URL) + json_data = redis.get(cache_key) + if not json_data: + file = get_binding_file(instance_id) + with open(file, 'r') as f: + json_data = f.read() + redis.set(cache_key, json_data) + return json.loads(json_data) + + +def save_addons_meta(data): + cache_key = "helmbroker:addons" + os.makedirs(ADDONS_PATH, exist_ok=True) + file = os.path.join(ADDONS_PATH, "addons.json") + jsonschema.validate(instance=data, schema=ADDONS_META_SCHEMA) + + json_data = json.dumps(data, sort_keys=True, indent=2) + with open(file, "w") as f: + f.write(json_data) + redis = client.Redis.from_url(REDIS_URL) + redis.set(cache_key, json_data) + + +def load_addons_meta(): + cache_key = "helmbroker:addons" + redis = client.Redis.from_url(REDIS_URL) + + json_data = redis.get(cache_key) + if not json_data: + file = os.path.join(ADDONS_PATH, "addons.json") + with open(file, 'r') as f: + json_data = f.read() + redis.set(cache_key, json_data) + return json.loads(json_data) diff --git a/rootfs/helmbroker/database/query.py b/rootfs/helmbroker/database/query.py new file mode 100644 index 0000000..b87810b --- /dev/null +++ b/rootfs/helmbroker/database/query.py @@ -0,0 +1,104 @@ +import os +import base64 + +from ..utils import command +from ..config import INSTANCES_PATH +from .metadata import load_addons_meta + + +def get_instance_path(instance_id): + return os.path.join(INSTANCES_PATH, instance_id) + + +def get_instance_file(instance_id): + return os.path.join(get_instance_path(instance_id), "instance.json") + + +def get_chart_path(instance_id): + return os.path.join(get_instance_path(instance_id), "chart") + + +def get_plan_path(instance_id): + return os.path.join(get_instance_path(instance_id), "plan") + + +def get_binding_file(instance_id): + return os.path.join(get_instance_path(instance_id), "binding.json") + + +def get_backups_path(instance_id): + return os.path.join(get_instance_path(instance_id), "backups") + + +def get_addon_values_file(instance_id): + return os.path.join(get_instance_path(instance_id), "addon-values.yaml") + + +def get_custom_addon_values_file(instance_id): + return os.path.join(get_instance_path(instance_id), "custom-addon-values.yaml") + + +def get_addon_updateable(addon_id): + addon_meta = get_addon_meta(addon_id) + return addon_meta.get('plan_updateable', False) + + +def get_addon_bindable(addon_id): + addon_meta = get_addon_meta(addon_id) + return addon_meta.get('bindable', False) + + +def get_addon_allow_params(addon_id): + addon_meta = get_addon_meta(addon_id) + return addon_meta.get('allow_parameters', []) + + +def get_addon_archive(addon_id): + addon_meta = get_addon_meta(addon_id) + return addon_meta.get('archive', False) + + +def get_cred_value(ns, source): + if source.get('serviceRef'): + return _get_service_key_value(ns, source['serviceRef']) + if source.get('configMapRef'): + return _get_config_map_key_value(ns, source['configMapRef']) + if source.get('secretKeyRef'): + return _get_secret_key_value(ns, source['secretKeyRef']) + return -1, 'invalid valueFrom' + + +def get_addon_meta(addon_id): + addons_meta = load_addons_meta() + addons_meta = [ + addon for addon in [addons for _, addons in addons_meta.items()] + if addon['id'] == addon_id + ] + return addons_meta[0] if len(addons_meta) > 0 else None + + +def _get_service_key_value(ns, service_ref): + args = [ + "get", "svc", service_ref['name'], "-n", ns, + '-o', f"jsonpath=\'{service_ref['jsonpath']}\'", + ] + return command("kubectl", *args) + + +def _get_config_map_key_value(ns, config_map_ref): + args = [ + "get", "cm", config_map_ref['name'], "-n", ns, + '-o', f"jsonpath=\'{config_map_ref['jsonpath']}\'", + ] + return command("kubectl", *args) + + +def _get_secret_key_value(ns, secret_ref): + args = [ + "get", "secret", secret_ref['name'], "-n", ns, '-o', + f"jsonpath=\'{secret_ref['jsonpath']}\'", + ] + status, output = command("kubectl", *args) + if status == 0: + output = base64.b64decode(output).decode() + return status, output diff --git a/rootfs/helmbroker/database/savepoint.py b/rootfs/helmbroker/database/savepoint.py new file mode 100644 index 0000000..2467349 --- /dev/null +++ b/rootfs/helmbroker/database/savepoint.py @@ -0,0 +1,54 @@ +import os +import logging +import yaml +import shutil +import datetime + +from ..config import CONFIG_PATH +from .query import get_instance_path, get_backups_path, get_addon_meta, get_addon_values_file, \ + get_custom_addon_values_file + +logger = logging.getLogger(__name__) + + +def save_raw_values(instance_id, data): + file = get_custom_addon_values_file(instance_id) + with open(file, "w") as f: + f.write(data) + return file + + +def save_addon_values(service_id, instance_id): + file = get_addon_values_file(instance_id) + service = get_addon_meta(service_id) + logger.debug(f"save_addon_values service: {service}") + if not os.path.exists(f'{CONFIG_PATH}/addon-values'): + return None + with open(file, "w") as fw: + with open(f'{CONFIG_PATH}/addon-values', 'r') as f: + addons_values = yaml.load(f.read(), Loader=yaml.Loader) + logger.debug(f"save_addon_values addons_values: {addons_values}") + addon_values = addons_values.get(service["name"], {}).\ + get(service["version"], {}) + logger.debug(f"save_addon_values addon_values: {addon_values}") + if not addon_values: + return None + fw.write(yaml.dump(addon_values)) + return file + + +def backup_instance(instance_id): + now = datetime.datetime.now(datetime.timezone.utc) + backup_path = os.path.join(get_backups_path(instance_id), now.isoformat()) + os.makedirs(backup_path, exist_ok=True) + + addon_values_file = get_addon_values_file(instance_id) + if os.path.exists(addon_values_file): + shutil.copy(addon_values_file, backup_path) + custom_addon_values_file = get_custom_addon_values_file(instance_id) + if os.path.exists(custom_addon_values_file): + shutil.copy(custom_addon_values_file, backup_path) + + instance_path = get_instance_path(instance_id) + shutil.copytree(os.path.join(instance_path, "plan"), os.path.join(backup_path, "plan")) + shutil.copytree(os.path.join(instance_path, "chart"), os.path.join(backup_path, "chart")) diff --git a/rootfs/helmbroker/gunicorn/config.py b/rootfs/helmbroker/gunicorn/config.py index 4038c74..716f9e5 100644 --- a/rootfs/helmbroker/gunicorn/config.py +++ b/rootfs/helmbroker/gunicorn/config.py @@ -18,14 +18,14 @@ def worker_int(worker): - """Print a stack trace when a worker receives a SIGINT or SIGQUIT signal.""" # noqa + """Print a stack trace when a worker receives a SIGINT or SIGQUIT signal.""" worker.log.warning('worker terminated') import traceback traceback.print_stack() def worker_abort(worker): - """Print a stack trace when a worker receives a SIGABRT signal, generally on timeout.""" # noqa + """Print a stack trace when a worker receives a SIGABRT signal, generally on timeout.""" worker.log.warning('worker aborted') import traceback traceback.print_stack() diff --git a/rootfs/helmbroker/loader.py b/rootfs/helmbroker/loader.py deleted file mode 100644 index abdec39..0000000 --- a/rootfs/helmbroker/loader.py +++ /dev/null @@ -1,120 +0,0 @@ -import os -import shutil -import tarfile -import requests -import yaml - -from .config import ADDONS_PATH, CONFIG_PATH -from .utils import dump_addons_meta - - -def download_file(url, dest): - if not os.path.exists(dest): - os.system(f'mkdir -p {dest}') - filename = url.split('/')[-1] - file = requests.get(url) - with open(f"{dest}/{filename}", 'wb') as f: - f.write(file.content) - if filename.endswith(".yaml") or filename.endswith(".yml"): - return yaml.load(file.content.decode(encoding="utf-8"), - Loader=yaml.Loader) - - -def read_file(filename): - if not os.path.exists(filename): - return - with open(filename, 'r') as f: - file_content = f.read() - return file_content - - -def save_file(content, dest, filename): - if not os.path.exists(dest): - os.system(f'mkdir -p {dest}') - with open(f"{dest}/{filename}", 'w') as f: - f.write(content) - - -def extract_tgz(tgz_file, dest): - if not os.path.exists(tgz_file): - return - if not os.path.exists(dest): - os.system(f'mkdir -p {dest}') - - tarobj = tarfile.open(tgz_file, "r:gz") - for tarinfo in tarobj: - tarobj.extract(tarinfo.name, dest) - tarobj.close() - - -def addons_meta_file(): - meta_files = [] - # get meta.yaml - for root, dirnames, filenames in os.walk(ADDONS_PATH): - for filename in filenames: - if filename == 'meta.yaml': - meta_files.append(os.path.join(root, filename)) - meta_files = [meta_file.split(ADDONS_PATH)[1] for meta_file in meta_files] - addons_meta = [] - plans_meta = [] - for meta_file in meta_files: - if len(meta_file.split('/')) == 3: - addons_meta.append(meta_file.split('/')[1:]) - else: - plans_meta.append(meta_file.split('/')[1:]) - addons_dict = {} - for addon_meta in addons_meta: - with open(f'{ADDONS_PATH}/{"/".join(addon_meta)}', 'r') as f: - meta = yaml.load(f.read(), Loader=yaml.Loader) - meta['tags'] = meta.get('tags').split(', ') if meta.get('tags') else [] # noqa - meta['plans'] = [] - addons_dict[meta['displayName']] = meta - addon_plans_meta = [] - for plan_meta in plans_meta: - if plan_meta[0] == meta['displayName']: - addon_plans_meta.append(plan_meta) - elif f'{"-".join(plan_meta[0].split("-")[0:-1])}' == meta['displayName']: # noqa - addon_plans_meta.append(plan_meta) - for addon_plan_meta in addon_plans_meta: - with open(f'{ADDONS_PATH}/{"/".join(addon_plan_meta)}', 'r') as f: - addons_mata = yaml.load(f.read(), Loader=yaml.Loader) - addons_dict[meta['displayName']]['plans'].append(addons_mata) # noqa - dump_addons_meta(addons_dict) - - -def load_addons(repository): - if not repository: - return - index_name = repository['url'].split('/')[-1] - local_index_file = f'{ADDONS_PATH}/{index_name}' - # download index.yaml - remote_index = requests.get(repository['url']).content.decode( - encoding="utf-8") - # compare index.yaml, is update - local_index = read_file(local_index_file) - if local_index and remote_index == local_index: - return - # delete old repository catalog - if os.path.exists(ADDONS_PATH): - shutil.rmtree(ADDONS_PATH, ignore_errors=True) - else: - os.makedirs(ADDONS_PATH, exist_ok=True) - # new index - save_file(remote_index, ADDONS_PATH, index_name) - remote_index = yaml.load(remote_index, Loader=yaml.Loader) - # save index.yaml addons - for addon_name, v in remote_index.get('entries', {}).items(): - for _ in v: - url = "/".join(repository["url"].split("/")[0:-1]) - tgz_name = f'{addon_name}-{_["version"]}' - addon_tgz_url = f'{url}/{tgz_name}.tgz' - download_file(addon_tgz_url, ADDONS_PATH) - extract_tgz(f'{ADDONS_PATH}/{tgz_name}.tgz', - f'{ADDONS_PATH}/{tgz_name}') - addons_meta_file() - - -if __name__ == '__main__': - with open(f'{CONFIG_PATH}/repositories', 'r') as f: - repositories = yaml.load(f.read(), Loader=yaml.Loader) - load_addons(repositories[0]) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 2e6f6e6..22c3e94 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -1,6 +1,5 @@ import os import time -import shutil import yaml import logging @@ -8,31 +7,30 @@ UpdateDetails, BindDetails from .celery import app -from .utils import get_plan_path, get_chart_path, get_cred_value, \ - InstanceLock, dump_instance_meta, dump_binding_meta, load_instance_meta, \ - get_instance_file, helm, dump_addon_values, format_paras_to_helm_args +from .utils import helm, format_params_to_helm_args, new_instance_lock + +from .database.metadata import save_instance_meta, save_binding_meta, load_instance_meta +from .database.savepoint import save_addon_values, backup_instance +from .database.query import get_plan_path, get_chart_path, get_cred_value logger = logging.getLogger(__name__) @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): - with InstanceLock(instance_id): + with new_instance_lock(instance_id): + backup_instance(instance_id) # create instance.json - dump_instance_meta(instance_id, { + save_instance_meta(instance_id, { "id": instance_id, "details": { - "service_id": details.service_id, - "plan_id": details.plan_id, + "service_id": details.service_id, "plan_id": details.plan_id, "context": details.context, "parameters": details.parameters if details.parameters else {}, }, "last_operation": { - "state": OperationState.IN_PROGRESS.value, - "operation": "provision", - "description": ( - "provision %s in progress at %s" % ( - instance_id, time.time())) + "state": OperationState.IN_PROGRESS.value, "operation": "provision", + "description": ("provision %s in progress at %s" % (instance_id, time.time())) } }) @@ -41,101 +39,78 @@ def provision(instance_id: str, details: ProvisionDetails): if os.path.exists(bind_yaml): os.remove(bind_yaml) if os.path.exists(f'{chart_path}/Chart.yaml'): - args = [ - "dependency", - "update", - chart_path, - ] + args = ["dependency", "update", chart_path] helm(instance_id, *args) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") args = [ - "install", - details.context["instance_name"], - chart_path, - "--namespace", - details.context["namespace"], - "--create-namespace", - "--wait", - "--timeout", - "25m0s", - "-f", - values_file, - "--set", - f"fullnameOverride=helmbroker-{details.context['instance_name']}" + "install", details.context["instance_name"], chart_path, + "--namespace", details.context["namespace"], "--create-namespace", + "--wait", "--timeout", "25m0s", "-f", values_file, + "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] - addon_values_file = dump_addon_values(details.service_id, instance_id) + addon_values_file = save_addon_values(details.service_id, instance_id) if addon_values_file: args.insert(9, "-f") args.insert(10, addon_values_file) logger.debug(f"helm install parameters :{details.parameters}") - args = format_paras_to_helm_args(instance_id, details.parameters, args) + args = format_params_to_helm_args(instance_id, details.parameters, args) logger.debug(f"helm install args:{args}") status, output = helm(instance_id, *args) data = load_instance_meta(instance_id) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = ( - "provision error:\n%s" % output) + data["last_operation"]["description"] = "provision error:\n%s" % output else: data["last_operation"]["state"] = OperationState.SUCCEEDED.value - data["last_operation"]["description"] = ( - "provision succeeded at %s" % time.time()) - dump_instance_meta(instance_id, data) + data["last_operation"]["description"] = "provision succeeded at %s" % time.time() + save_instance_meta(instance_id, data) @app.task(serializer='pickle') def update(instance_id: str, details: UpdateDetails): - data = load_instance_meta(instance_id) - if details.service_id: - data['details']['service_id'] = details.service_id - if details.plan_id: - data['details']['service_id'] = details.plan_id - if details.context: - data['details']['context'] = details.context - if details.parameters: - paras = data['details']['parameters'] - paras.update(details.parameters) - # remove the key which value is null - data['details']['parameters'] = {k: v for k, v in paras.items() if v != ""} # noqa - data['last_operation']["state"] = OperationState.IN_PROGRESS.value - data['last_operation']["description"] = "update %s in progress at %s" % (instance_id, time.time()) # noqa - dump_instance_meta(instance_id, data) - chart_path = get_chart_path(instance_id) - values_file = os.path.join(get_plan_path(instance_id), "values.yaml") - args = [ - "upgrade", - details.context["instance_name"], - chart_path, - "--namespace", - details.context["namespace"], - "--create-namespace", - "--wait", - "--timeout", - "25m0s", - "--reuse-values", - "-f", - values_file, - "--set", - f"fullnameOverride=helmbroker-{details.context['instance_name']}" - ] - addon_values_file = dump_addon_values(details.service_id, instance_id) - if addon_values_file: - args.insert(10, "-f") - args.insert(11, addon_values_file) - paras = data['details']['parameters'] - logger.debug(f"helm upgrade parameters: {paras}") - args = format_paras_to_helm_args(instance_id, paras, args) - logger.debug(f"helm upgrade args:{args}") - status, output = helm(instance_id, *args) - if status != 0: - data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = ( - "update %s failed: %s" % (instance_id, output)) - else: - data["last_operation"]["state"] = OperationState.SUCCEEDED.value - data["last_operation"]["description"] = ( - "update %s succeeded at %s" % (instance_id, time.time())) - dump_instance_meta(instance_id, data) + with new_instance_lock(instance_id): + backup_instance(instance_id) + data = load_instance_meta(instance_id) + if details.service_id: + data['details']['service_id'] = details.service_id + if details.plan_id: + data['details']['service_id'] = details.plan_id + if details.context: + data['details']['context'] = details.context + if details.parameters: + params = data['details']['parameters'] + params.update(details.parameters) + # remove the key which value is null + data['details']['parameters'] = {k: v for k, v in params.items() if v != ""} + data['last_operation']["state"] = OperationState.IN_PROGRESS.value + data['last_operation']["description"] = "update %s in progress at %s" % ( + instance_id, time.time()) + save_instance_meta(instance_id, data) + chart_path = get_chart_path(instance_id) + values_file = os.path.join(get_plan_path(instance_id), "values.yaml") + args = [ + "upgrade", details.context["instance_name"], chart_path, + "--namespace", details.context["namespace"], "--create-namespace", + "--wait", "--timeout", "25m0s", "--reuse-values", "-f", values_file, + "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" + ] + addon_values_file = save_addon_values(details.service_id, instance_id) + if addon_values_file: + args.insert(10, "-f") + args.insert(11, addon_values_file) + params = data['details']['parameters'] + logger.debug(f"helm upgrade parameters: {params}") + args = format_params_to_helm_args(instance_id, params, args) + logger.debug(f"helm upgrade args:{args}") + status, output = helm(instance_id, *args) + if status != 0: + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = "update %s failed: %s" % (instance_id, output) + else: + data["last_operation"]["state"] = OperationState.SUCCEEDED.value + data["last_operation"]["description"] = ( + "update %s succeeded at %s" % (instance_id, time.time())) + save_instance_meta(instance_id, data) @app.task(serializer='pickle') @@ -144,98 +119,86 @@ def bind(instance_id: str, details: BindDetails, async_allowed: bool, **kwargs): - data = { - "binding_id": binding_id, - "credentials": {}, - "last_operation": { - "state": OperationState.IN_PROGRESS.value, - "description": ( - "binding %s in progress at %s" % (binding_id, time.time())) + with new_instance_lock(instance_id): + backup_instance(instance_id) + data = { + "binding_id": binding_id, "credentials": {}, + "last_operation": { + "state": OperationState.IN_PROGRESS.value, + "description": "binding %s in progress at %s" % (binding_id, time.time()) + } } - } - dump_binding_meta(instance_id, data) - - chart_path = get_chart_path(instance_id) - values_file = os.path.join(get_plan_path(instance_id), "values.yaml") - args = [ - "template", - details.context["instance_name"], - chart_path, - "-f", - values_file, - "--set", - f"fullnameOverride=helmbroker-{details.context['instance_name']}", - "--namespace", - details.context["namespace"], - ] - instance_data = load_instance_meta(instance_id) - paras = instance_data["details"]["parameters"] - logger.debug(f"helm template parameters: {paras}") - args = format_paras_to_helm_args(instance_id, paras, args) - logger.debug(f"helm template args: {args}") - status, templates = helm(instance_id, *args) # output: templates.yaml - if status != 0: - data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = "binding %s failed: %s" % (instance_id, templates) # noqa - - credential_template = yaml.load(templates.split('bind.yaml')[1], Loader=yaml.Loader) # noqa - success_flag = True - errors = [] - for _ in credential_template['credential']: - if _.get('valueFrom'): - status, val = get_cred_value(details.context["namespace"], _['valueFrom']) # noqa - elif _.get('value'): - status, val = 0, _['value'] - else: - status, val = -1, 'invalid value' + save_binding_meta(instance_id, data) + chart_path = get_chart_path(instance_id) + values_file = os.path.join(get_plan_path(instance_id), "values.yaml") + args = [ + "template", details.context["instance_name"], chart_path, + "-f", values_file, + "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}", + "--namespace", details.context["namespace"], + ] + instance_data = load_instance_meta(instance_id) + params = instance_data["details"]["parameters"] + logger.debug(f"helm template parameters: {params}") + args = format_params_to_helm_args(instance_id, params, args) + logger.debug(f"helm template args: {args}") + status, templates = helm(instance_id, *args) # output: templates.yaml if status != 0: - success_flag = False - errors.append(val) + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = "binding %s failed: %s" % ( + instance_id, templates) + + credential_template = yaml.load(templates.split('bind.yaml')[1], Loader=yaml.Loader) + success_flag = True + errors = [] + for _ in credential_template['credential']: + if _.get('valueFrom'): + status, val = get_cred_value(details.context["namespace"], _['valueFrom']) + elif _.get('value'): + status, val = 0, _['value'] + else: + status, val = -1, 'invalid value' + if status != 0: + success_flag = False + errors.append(val) + else: + data['credentials'][_['name']] = val + if success_flag: + data['last_operation'] = { + 'state': OperationState.SUCCEEDED.value, + 'description': "binding %s succeeded at %s" % (instance_id, time.time()) + } else: - data['credentials'][_['name']] = val - if success_flag: - data['last_operation'] = { - 'state': OperationState.SUCCEEDED.value, - 'description': "binding %s succeeded at %s" % (instance_id, time.time()) # noqa - } - else: - data['last_operation'] = { - 'state': OperationState.FAILED.value, - 'description': "binding %s failed: %s" % (instance_id, ','.join(errors)) # noqa - } - dump_binding_meta(instance_id, data) - bind_yaml = f'{chart_path}/templates/bind.yaml' - if os.path.exists(bind_yaml): - os.remove(bind_yaml) + data['last_operation'] = { + 'state': OperationState.FAILED.value, + 'description': "binding %s failed: %s" % (instance_id, ','.join(errors)) + } + bind_yaml = f'{chart_path}/templates/bind.yaml' + if os.path.exists(bind_yaml): + os.remove(bind_yaml) + save_binding_meta(instance_id, data) @app.task() def deprovision(instance_id: str): - with InstanceLock(instance_id): - shutil.copy(get_instance_file(instance_id), "%s.%s" % ( - get_instance_file(instance_id), time.time() - )) + with new_instance_lock(instance_id): + backup_instance(instance_id) data = load_instance_meta(instance_id) data["last_operation"]["operation"] = "deprovision" data["last_operation"]["state"] = OperationState.IN_PROGRESS.value data["last_operation"]["description"] = ( "deprovision %s in progress at %s" % (instance_id, time.time())) - dump_instance_meta(instance_id, data) + save_instance_meta(instance_id, data) args = [ - "uninstall", - data["details"]["context"]["instance_name"], - "--namespace", - data["details"]["context"]["namespace"], + "uninstall", data["details"]["context"]["instance_name"], + "--namespace", data["details"]["context"]["namespace"], ] logger.debug(f"helm uninstall args: {args}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = ( - "deprovision error:\n%s" % output) + data["last_operation"]["description"] = "deprovision error:\n%s" % output else: - data["last_operation"]["state"] = ( - OperationState.SUCCEEDED.value) - data["last_operation"]["description"] = ( - "deprovision succeeded at %s" % time.time()) - dump_instance_meta(instance_id, data) + data["last_operation"]["state"] = OperationState.SUCCEEDED.value + data["last_operation"]["description"] = "deprovision succeeded at %s" % time.time() + save_instance_meta(instance_id, data) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index a2f6d31..b4271bf 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -1,111 +1,23 @@ import os -import fcntl import yaml import json import subprocess -import time import base64 import copy import logging -from jsonschema import validate -from .config import INSTANCES_PATH, ADDONS_PATH, CONFIG_PATH +from redis import client +from .config import REDIS_URL logger = logging.getLogger(__name__) REGISTRY_CONFIG_SUFFIX = '.config/helm/registry.json' REPOSITORY_CACHE_SUFFIX = '.cache/helm/repository' REPOSITORY_CONFIG_SUFFIX = '.config/helm/repository' -INSTANCE_META_SCHEMA = { - "type": "object", - "properties": { - "id": {"type": "string"}, - "details": { - "type": "object", - "properties": { - "service_id": {"type": "string"}, - "plan_id": {"type": "string"}, - "context": {"type": "object"}, - "parameters": { - 'oneOf': [{'type': 'object'}, {'type': 'null'}] - }, - }, - "required": [ - "service_id", "plan_id", "context" - ] - }, - "last_operation": { - "type": "object", - "properties": { - "state": {"type": "string"}, - "operation": {"type": "string"}, - "description": {"type": "string"} - } - }, - "last_modified_time": {"type": "number"} - }, -} -BINDING_META_SCHEMA = { - "type": "object", - "properties": { - "id": {"type": "string"}, - "credentials": { - "type": "object", - }, - "last_operation": { - "type": "object", - "properties": { - "state": {"type": "string"}, - "description": {"type": "string"} - } - }, - "last_modified_time": {"type": "number"} - } -} -ADDONS_META_SCHEMA = { - "type": "object", - "patternProperties": { - ".*": { - "id": {"type": "string"}, - "name": {"type": "string"}, - "version": {"type": "string"}, - "bindable": {"type": "boolean"}, - "instances_retrievable": {"type": "boolean"}, - "bindings_retrievable": {"type": "boolean"}, - "allow_context_updates": {"type": "boolean"}, - "description": {"type": "string"}, - "tags": {"type": "string"}, - "requires": {"type": "array"}, - "metadata": {"type": "object"}, - "plan_updateable": {"type": "boolean"}, - "dashboard_client": {"type": "object"}, - "plans": { - "type": "object", - "id": {"type": "string"}, - "name": {"type": "string"}, - "description": {"type": "string"}, - "metadata": {"type": "object"}, - "free": {"type": "boolean"}, - "bindable": {"type": "boolean"}, - "binding_rotatable": {"type": "boolean"}, - "plan_updateable": {"type": "boolean"}, - "schemas": {"type": "object"}, - "maximum_polling_duration": {"type": "integer"}, - "maintenance_info": {"type": "object"}, - "required": [ - "id", "name", "description" - ] - }, - "required": [ - "id", "name", "description", "bindable", "version", "plans" - ] - } - } -} def command(cmd, *args, output_type="text"): - status, output = subprocess.getstatusoutput("%s %s" % (cmd, " ".join(args))) # noqa + status, output = subprocess.getstatusoutput("%s %s" % (cmd, " ".join(args))) if output_type == "yaml": return yaml.load(output, Loader=yaml.Loader) elif output_type == "json": @@ -113,13 +25,8 @@ def command(cmd, *args, output_type="text"): return status, output -get_instance_path = lambda instance_id: os.path.join(INSTANCES_PATH, instance_id) # noqa -get_instance_file = lambda instance_id: os.path.join(get_instance_path(instance_id), "instance.json") # noqa -get_chart_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "chart") # noqa -get_plan_path = lambda instance_id: os.path.join(get_instance_path(instance_id), "plan") # noqa - - def helm(instance_id, *args, output_type="text"): + from .database.query import get_instance_path instance_path = get_instance_path(instance_id) new_args = [] new_args.extend(args) @@ -134,145 +41,9 @@ def helm(instance_id, *args, output_type="text"): return command("helm", *new_args, output_type=output_type) -def load_instance_meta(instance_id): - file = get_instance_file(instance_id) - with open(file) as f: - data = json.loads(f.read()) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - return data - - -def dump_instance_meta(instance_id, data): - data["last_modified_time"] = time.time() - file = get_instance_file(instance_id) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - with open(file, "w") as f: - f.write(json.dumps(data, sort_keys=True, indent=2)) - - -def dump_raw_values(instance_id, data): - timestamp = time.time() - instance_path = get_instance_path(instance_id) - file = f"{instance_path}/raw-values-{timestamp}.yaml" - with open(file, "w") as f: - f.write(data) - return file - - -def dump_addon_values(service_id, instance_id): - timestamp = time.time() - instance_path = get_instance_path(instance_id) - file = f"{instance_path}/addon-values-{timestamp}.yaml" - service = _get_addon_meta(service_id) - logger.debug(f"dump_addon_values service: {service}") - if not os.path.exists(f'{CONFIG_PATH}/addon-values'): - return None - with open(file, "w") as fw: - with open(f'{CONFIG_PATH}/addon-values', 'r') as f: - addons_values = yaml.load(f.read(), Loader=yaml.Loader) - logger.debug(f"dump_addon_values addons_values: {addons_values}") - addon_values = addons_values.get(service["name"], {}).\ - get(service["version"], {}) - logger.debug(f"dump_addon_values addon_values: {addon_values}") - if not addon_values: - return None - fw.write(yaml.dump(addon_values)) - return file - - -def load_binding_meta(instance_id): - file = os.path.join(get_instance_path(instance_id), "binding.json") - with open(file, 'r') as f: - data = json.loads(f.read()) - validate(instance=data, schema=INSTANCE_META_SCHEMA) - return data - - -def dump_binding_meta(instance_id, data): - data["last_modified_time"] = time.time() - file = os.path.join(get_instance_path(instance_id), "binding.json") - validate(instance=data, schema=INSTANCE_META_SCHEMA) - with open(file, "w") as f: - f.write(json.dumps(data, sort_keys=True, indent=2)) - - -def load_addons_meta(): - file = os.path.join(ADDONS_PATH, "addons.json") - with open(file, 'r') as f: - data = json.loads(f.read()) - if not data: - return {} - validate(instance=data, schema=INSTANCE_META_SCHEMA) - return data - - -def dump_addons_meta(data): - file = os.path.join(ADDONS_PATH, "addons.json") - validate(instance=data, schema=INSTANCE_META_SCHEMA) - with open(file, "w") as f: - f.write(json.dumps(data, sort_keys=True, indent=2)) - - -def get_addon_path(service_id, plan_id): - service = _get_addon_meta(service_id) - plan = [plan for plan in service['plans'] if plan['id'] == plan_id][0] - plan_name = plan['name'] - service_name_path = f'{service["name"]}-{service["version"]}' - base_path = f"{ADDONS_PATH}/{service_name_path}" - service_path = f'{base_path}/chart/{service["name"]}' - plan_path = f'{base_path}/plans/{plan_name}' - return service_path, plan_path - - -def get_addon_updateable(service_id): - service = _get_addon_meta(service_id) - return service.get('plan_updateable', False) - - -def get_addon_bindable(service_id): - service = _get_addon_meta(service_id) - return service.get('bindable', False) - - -def get_addon_allow_paras(service_id): - service = _get_addon_meta(service_id) - return service.get('allow_parameters', []) - - -def get_addon_archive(service_id): - service = _get_addon_meta(service_id) - return service.get('archive', False) - - -def get_cred_value(ns, source): - if source.get('serviceRef'): - return _get_service_key_value(ns, source['serviceRef']) - if source.get('configMapRef'): - return _get_config_map_key_value(ns, source['configMapRef']) - if source.get('secretKeyRef'): - return _get_secret_key_value(ns, source['secretKeyRef']) - return -1, 'invalid valueFrom' - - -class InstanceLock(object): - - def __init__(self, instance_id): - self.instance_id = instance_id - - def __enter__(self): - self.fileno = open( - os.path.join(INSTANCES_PATH, self.instance_id, "instance.lock"), - "w" - ) - fcntl.flock(self.fileno, fcntl.LOCK_EX) - return self - - def __exit__(self, exc_type, exc_value, traceback): - fcntl.flock(self.fileno, fcntl.LOCK_UN) - - def __del__(self): - if hasattr(self, "fileno"): - fcntl.flock(self.fileno, fcntl.LOCK_UN) +def new_instance_lock(instance_id): + redis = client.Redis.from_url(REDIS_URL) + return redis.lock(instance_id) def verify_parameters(allow_parameters, parameters): @@ -295,15 +66,14 @@ def merge_parameters(parameters): ) -def format_paras_to_helm_args(instance_id, parameters, args): - """ - - """ +def format_params_to_helm_args(instance_id, parameters, args): + """format helm args""" + from .database.savepoint import save_raw_values params = copy.deepcopy(parameters) if params and "rawValues" in params \ and params.get("rawValues", ""): - values = str(base64.b64decode(params["rawValues"]), "utf-8") # noqa - raw_values_file = dump_raw_values(instance_id, values) + values = str(base64.b64decode(params["rawValues"]), "utf-8") + raw_values_file = save_raw_values(instance_id, values) args.extend(["-f", raw_values_file]) params.pop("rawValues") if params: @@ -312,37 +82,6 @@ def format_paras_to_helm_args(instance_id, parameters, args): return args -def _get_addon_meta(service_id): - services = load_addons_meta() - service = [addon for addon in [addons for _, addons in services.items()] - if addon['id'] == service_id][0] - return service - - -def _get_service_key_value(ns, service_ref): - args = [ - "get", "svc", service_ref['name'], "-n", ns, '-o', f"jsonpath=\'{service_ref['jsonpath']}\'", # noqa - ] - return command("kubectl", *args) - - -def _get_config_map_key_value(ns, config_map_ref): - args = [ - "get", "cm", config_map_ref['name'], "-n", ns, '-o', f"jsonpath=\'{config_map_ref['jsonpath']}\'", # noqa - ] - return command("kubectl", *args) - - -def _get_secret_key_value(ns, secret_ref): - args = [ - "get", "secret", secret_ref['name'], "-n", ns, '-o', f"jsonpath=\'{secret_ref['jsonpath']}\'", # noqa - ] - status, output = command("kubectl", *args) - if status == 0: - output = base64.b64decode(output).decode() - return status, output - - def _raw_values_format_keys(raw_values, prefix=''): """ {'a': {'b': 1, 'c': {'d': 2, 'e': 3}}, 'f': 4} diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index df3a567..b88c2ac 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -3,4 +3,5 @@ gunicorn==22.0.0 openbrokerapi==4.6.0 requests==2.31.0 celery==5.4.0 +redis==5.0.8 jsonschema==4.21.1 \ No newline at end of file diff --git a/rootfs/setup.cfg b/rootfs/setup.cfg new file mode 100644 index 0000000..e000ebb --- /dev/null +++ b/rootfs/setup.cfg @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 99 +exclude = api/migrations,templates,venv +max-complexity = 12 \ No newline at end of file From eecabf79c26dc933e2db1e35f14fd590a9a01eda Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 2 Aug 2024 18:10:39 +0800 Subject: [PATCH 50/75] feat(task): add hooks --- rootfs/helmbroker/database/query.py | 8 +++++ rootfs/helmbroker/database/savepoint.py | 43 ++++++++++++++++--------- rootfs/helmbroker/tasks.py | 10 +++--- rootfs/helmbroker/utils.py | 28 ++++++++++++++++ 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/rootfs/helmbroker/database/query.py b/rootfs/helmbroker/database/query.py index b87810b..b7aa331 100644 --- a/rootfs/helmbroker/database/query.py +++ b/rootfs/helmbroker/database/query.py @@ -22,6 +22,14 @@ def get_plan_path(instance_id): return os.path.join(get_instance_path(instance_id), "plan") +def get_hooks_path(instance_id): + return os.path.join(get_plan_path(instance_id), "hooks") + + +def get_hooks_result_file(instance_id): + return os.path.join(get_instance_path(instance_id), "hooks-result.json") + + def get_binding_file(instance_id): return os.path.join(get_instance_path(instance_id), "binding.json") diff --git a/rootfs/helmbroker/database/savepoint.py b/rootfs/helmbroker/database/savepoint.py index 2467349..ba97960 100644 --- a/rootfs/helmbroker/database/savepoint.py +++ b/rootfs/helmbroker/database/savepoint.py @@ -1,4 +1,5 @@ import os +import json import logging import yaml import shutil @@ -6,11 +7,31 @@ from ..config import CONFIG_PATH from .query import get_instance_path, get_backups_path, get_addon_meta, get_addon_values_file, \ - get_custom_addon_values_file + get_custom_addon_values_file, get_hooks_result_file logger = logging.getLogger(__name__) +def backup_instance(instance_id): + now = datetime.datetime.now(datetime.timezone.utc) + backup_path = os.path.join(get_backups_path(instance_id), now.isoformat()) + os.makedirs(backup_path, exist_ok=True) + + hooks_result_file = get_hooks_result_file(instance_id) + if os.path.exists(hooks_result_file): + shutil.copy(hooks_result_file, backup_path) + addon_values_file = get_addon_values_file(instance_id) + if os.path.exists(addon_values_file): + shutil.copy(addon_values_file, backup_path) + custom_addon_values_file = get_custom_addon_values_file(instance_id) + if os.path.exists(custom_addon_values_file): + shutil.copy(custom_addon_values_file, backup_path) + + instance_path = get_instance_path(instance_id) + shutil.copytree(os.path.join(instance_path, "plan"), os.path.join(backup_path, "plan")) + shutil.copytree(os.path.join(instance_path, "chart"), os.path.join(backup_path, "chart")) + + def save_raw_values(instance_id, data): file = get_custom_addon_values_file(instance_id) with open(file, "w") as f: @@ -37,18 +58,8 @@ def save_addon_values(service_id, instance_id): return file -def backup_instance(instance_id): - now = datetime.datetime.now(datetime.timezone.utc) - backup_path = os.path.join(get_backups_path(instance_id), now.isoformat()) - os.makedirs(backup_path, exist_ok=True) - - addon_values_file = get_addon_values_file(instance_id) - if os.path.exists(addon_values_file): - shutil.copy(addon_values_file, backup_path) - custom_addon_values_file = get_custom_addon_values_file(instance_id) - if os.path.exists(custom_addon_values_file): - shutil.copy(custom_addon_values_file, backup_path) - - instance_path = get_instance_path(instance_id) - shutil.copytree(os.path.join(instance_path, "plan"), os.path.join(backup_path, "plan")) - shutil.copytree(os.path.join(instance_path, "chart"), os.path.join(backup_path, "chart")) +def save_hooks_result(instance_id, data): + file = get_hooks_result_file(instance_id) + with open(file, "w") as f: + f.write(json.dumps(data, sort_keys=True, indent=2)) + return file diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 22c3e94..fd08b72 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -7,7 +7,7 @@ UpdateDetails, BindDetails from .celery import app -from .utils import helm, format_params_to_helm_args, new_instance_lock +from .utils import helm, format_params_to_helm_args, new_instance_lock, run_instance_hooks from .database.metadata import save_instance_meta, save_binding_meta, load_instance_meta from .database.savepoint import save_addon_values, backup_instance @@ -18,7 +18,7 @@ @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): - with new_instance_lock(instance_id): + with new_instance_lock(instance_id), run_instance_hooks(instance_id, "provision"): backup_instance(instance_id) # create instance.json save_instance_meta(instance_id, { @@ -68,7 +68,7 @@ def provision(instance_id: str, details: ProvisionDetails): @app.task(serializer='pickle') def update(instance_id: str, details: UpdateDetails): - with new_instance_lock(instance_id): + with new_instance_lock(instance_id), run_instance_hooks(instance_id, "update"): backup_instance(instance_id) data = load_instance_meta(instance_id) if details.service_id: @@ -119,7 +119,7 @@ def bind(instance_id: str, details: BindDetails, async_allowed: bool, **kwargs): - with new_instance_lock(instance_id): + with new_instance_lock(instance_id), run_instance_hooks(instance_id, "bind"): backup_instance(instance_id) data = { "binding_id": binding_id, "credentials": {}, @@ -181,7 +181,7 @@ def bind(instance_id: str, @app.task() def deprovision(instance_id: str): - with new_instance_lock(instance_id): + with new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision"): backup_instance(instance_id) data = load_instance_meta(instance_id) data["last_operation"]["operation"] = "deprovision" diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index b4271bf..ea664b4 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -6,6 +6,7 @@ import copy import logging +from contextlib import contextmanager from redis import client from .config import REDIS_URL @@ -46,6 +47,33 @@ def new_instance_lock(instance_id): return redis.lock(instance_id) +@contextmanager +def run_instance_hooks(instance_id, stage): + if stage not in ["provision", "bind", "unbind", "update", "deprovision"]: + raise ValueError(f"Unknown stage {stage}") + from .database.query import get_hooks_path + from .database.savepoint import save_hooks_result + pre_script_file = os.path.join(get_hooks_path(instance_id), f"pre_{stage}.sh") + post_script_file = os.path.join(get_hooks_path(instance_id), f"post_{stage}.sh") + logger.debug(f"instance hook running: {instance_id}, {instance_id}") + result = [] + try: + if os.path.exists(pre_script_file): + status, output = subprocess.getstatusoutput(pre_script_file) + result.append({"script": pre_script_file, "status": status, "output": output}) + else: + logger.debug(f"skip running {pre_script_file}") + yield + finally: + if os.path.exists(pre_script_file): + status, output = subprocess.getstatusoutput(post_script_file) + result.append({"script": pre_script_file, "status": status, "output": output}) + else: + logger.debug(f"skip running {post_script_file}") + save_hooks_result(instance_id, result) + logger.debug(f"instance hook completed: {instance_id}, {instance_id}") + + def verify_parameters(allow_parameters, parameters): """verify parameters allowed or not""" def merge_parameters(parameters): From cc07840dd74bb93120708353f9cd84e065620a22 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Fri, 2 Aug 2024 19:13:43 +0800 Subject: [PATCH 51/75] feat(tasks): change unbind async --- rootfs/helmbroker/broker.py | 15 +++++++-------- rootfs/helmbroker/tasks.py | 13 +++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 561299e..0f82132 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -6,7 +6,7 @@ from openbrokerapi.catalog import ServicePlan from openbrokerapi.errors import ErrInstanceAlreadyExists, ErrAsyncRequired, \ ErrBindingAlreadyExists, ErrBadRequest, ErrInstanceDoesNotExist, \ - ServiceException, ErrBindingDoesNotExist + ServiceException from openbrokerapi.service_broker import ServiceBroker, Service, \ ProvisionDetails, ProvisionedServiceSpec, ProvisionState, GetBindingSpec, \ BindDetails, Binding, BindState, UnbindDetails, UnbindSpec, \ @@ -19,7 +19,7 @@ get_addon_updateable, get_addon_bindable, get_addon_allow_params, \ get_addon_archive, get_binding_file, get_instance_file from .database.metadata import load_instance_meta, load_binding_meta, load_addons_meta -from .tasks import provision, bind, deprovision, update +from .tasks import provision, bind, deprovision, update, unbind logger = logging.getLogger(__name__) @@ -112,10 +112,9 @@ def unbind(self, async_allowed: bool, **kwargs ) -> UnbindSpec: - binding_file = get_binding_file(instance_id) - if os.path.exists(binding_file): - os.remove(binding_file) - return UnbindSpec(is_async=False) + logger.debug(f"unbind instance {instance_id}") + unbind.delay(instance_id) + return UnbindSpec(is_async=True) def update(self, instance_id: str, @@ -179,7 +178,7 @@ def last_operation(self, OperationState(data["last_operation"]["state"]), data["last_operation"]["description"] ) - raise ErrInstanceDoesNotExist() + raise LastOperation(OperationState.IN_PROGRESS) def last_binding_operation(self, instance_id: str, @@ -193,4 +192,4 @@ def last_binding_operation(self, OperationState(data["last_operation"]["state"]), data["last_operation"]["description"] ) - raise ErrBindingDoesNotExist() + return LastOperation(OperationState.SUCCEEDED) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index fd08b72..1b6066d 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -11,7 +11,7 @@ from .database.metadata import save_instance_meta, save_binding_meta, load_instance_meta from .database.savepoint import save_addon_values, backup_instance -from .database.query import get_plan_path, get_chart_path, get_cred_value +from .database.query import get_plan_path, get_chart_path, get_cred_value, get_binding_file logger = logging.getLogger(__name__) @@ -179,7 +179,16 @@ def bind(instance_id: str, save_binding_meta(instance_id, data) -@app.task() +@app.task(serializer='pickle') +def unbind(instance_id): + with new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision"): + backup_instance(instance_id) + binding_file = get_binding_file(instance_id) + if os.path.exists(binding_file): + os.remove(binding_file) + + +@app.task(serializer='pickle') def deprovision(instance_id: str): with new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision"): backup_instance(instance_id) From 60d66c8eafb28ca515beb1f85e5b44dd2985fb79 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 5 Aug 2024 17:37:37 +0800 Subject: [PATCH 52/75] chore(hooks): always run save_hooks_result --- rootfs/helmbroker/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index ea664b4..6114f4a 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -70,7 +70,7 @@ def run_instance_hooks(instance_id, stage): result.append({"script": pre_script_file, "status": status, "output": output}) else: logger.debug(f"skip running {post_script_file}") - save_hooks_result(instance_id, result) + save_hooks_result(instance_id, result) logger.debug(f"instance hook completed: {instance_id}, {instance_id}") From 658ba12c2c34f363ab88210f026600701ded4121 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 5 Aug 2024 20:01:29 +0800 Subject: [PATCH 53/75] chore(broker): add check install_hooks status --- rootfs/helmbroker/broker.py | 4 +- rootfs/helmbroker/tasks.py | 121 +++++++++++++++++++++++------------- rootfs/helmbroker/utils.py | 5 +- 3 files changed, 84 insertions(+), 46 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 0f82132..8b38269 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -178,7 +178,7 @@ def last_operation(self, OperationState(data["last_operation"]["state"]), data["last_operation"]["description"] ) - raise LastOperation(OperationState.IN_PROGRESS) + return LastOperation(OperationState.IN_PROGRESS) def last_binding_operation(self, instance_id: str, @@ -192,4 +192,4 @@ def last_binding_operation(self, OperationState(data["last_operation"]["state"]), data["last_operation"]["description"] ) - return LastOperation(OperationState.SUCCEEDED) + return LastOperation(OperationState.IN_PROGRESS) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 1b6066d..9cfda51 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -9,7 +9,8 @@ from .celery import app from .utils import helm, format_params_to_helm_args, new_instance_lock, run_instance_hooks -from .database.metadata import save_instance_meta, save_binding_meta, load_instance_meta +from .database.metadata import save_instance_meta, save_binding_meta, load_instance_meta, \ + load_binding_meta from .database.savepoint import save_addon_values, backup_instance from .database.query import get_plan_path, get_chart_path, get_cred_value, get_binding_file @@ -18,22 +19,30 @@ @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): - with new_instance_lock(instance_id), run_instance_hooks(instance_id, "provision"): + with ( + new_instance_lock(instance_id), + run_instance_hooks(instance_id, "provision") as (status, output) + ): backup_instance(instance_id) # create instance.json - save_instance_meta(instance_id, { - "id": instance_id, + data = { + "id": instance_id, "last_operation": {}, "details": { "service_id": details.service_id, "plan_id": details.plan_id, "context": details.context, "parameters": details.parameters if details.parameters else {}, }, - "last_operation": { - "state": OperationState.IN_PROGRESS.value, "operation": "provision", - "description": ("provision %s in progress at %s" % (instance_id, time.time())) - } - }) - + } + if status != 0: + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = f"provision {instance_id} error: {output}" + save_instance_meta(instance_id, data) + return + data["last_operation"]["state"] = OperationState.IN_PROGRESS.value + data["last_operation"]["operation"] = "provision" + data["last_operation"]["description"] = ( + f"provision {instance_id} in progress at {time.time()}") + save_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) bind_yaml = f'{chart_path}/templates/bind.yaml' if os.path.exists(bind_yaml): @@ -56,19 +65,22 @@ def provision(instance_id: str, details: ProvisionDetails): args = format_params_to_helm_args(instance_id, details.parameters, args) logger.debug(f"helm install args:{args}") status, output = helm(instance_id, *args) - data = load_instance_meta(instance_id) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = "provision error:\n%s" % output + data["last_operation"]["description"] = f"provision {instance_id} error: {output}" else: data["last_operation"]["state"] = OperationState.SUCCEEDED.value - data["last_operation"]["description"] = "provision succeeded at %s" % time.time() + data["last_operation"]["description"] = ( + f"provision {instance_id} succeeded at {time.time()}") save_instance_meta(instance_id, data) @app.task(serializer='pickle') def update(instance_id: str, details: UpdateDetails): - with new_instance_lock(instance_id), run_instance_hooks(instance_id, "update"): + with ( + new_instance_lock(instance_id), + run_instance_hooks(instance_id, "update") as (status, output) + ): backup_instance(instance_id) data = load_instance_meta(instance_id) if details.service_id: @@ -82,9 +94,14 @@ def update(instance_id: str, details: UpdateDetails): params.update(details.parameters) # remove the key which value is null data['details']['parameters'] = {k: v for k, v in params.items() if v != ""} + if status != 0: + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = f"update {instance_id} failed: {output}" + save_instance_meta(instance_id, data) + return data['last_operation']["state"] = OperationState.IN_PROGRESS.value - data['last_operation']["description"] = "update %s in progress at %s" % ( - instance_id, time.time()) + data['last_operation']["description"] = ( + f"update {instance_id} in progress at {time.time()}") save_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") @@ -105,11 +122,11 @@ def update(instance_id: str, details: UpdateDetails): status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = "update %s failed: %s" % (instance_id, output) + data["last_operation"]["description"] = f"update {time.time()} failed: {output}" else: data["last_operation"]["state"] = OperationState.SUCCEEDED.value data["last_operation"]["description"] = ( - "update %s succeeded at %s" % (instance_id, time.time())) + f"update {instance_id} succeeded at {time.time()}") save_instance_meta(instance_id, data) @@ -119,15 +136,17 @@ def bind(instance_id: str, details: BindDetails, async_allowed: bool, **kwargs): - with new_instance_lock(instance_id), run_instance_hooks(instance_id, "bind"): + with ( + new_instance_lock(instance_id), + run_instance_hooks(instance_id, "bind") as (status, output) + ): backup_instance(instance_id) - data = { - "binding_id": binding_id, "credentials": {}, - "last_operation": { - "state": OperationState.IN_PROGRESS.value, - "description": "binding %s in progress at %s" % (binding_id, time.time()) - } - } + data = {"binding_id": binding_id, "credentials": {}, "last_operation": {}} + if status != 0: + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = f"binding {instance_id} failed: {output}" + save_binding_meta(instance_id, data) + return save_binding_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") @@ -145,9 +164,7 @@ def bind(instance_id: str, status, templates = helm(instance_id, *args) # output: templates.yaml if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = "binding %s failed: %s" % ( - instance_id, templates) - + data["last_operation"]["description"] = f"binding {instance_id} failed: {templates}" credential_template = yaml.load(templates.split('bind.yaml')[1], Loader=yaml.Loader) success_flag = True errors = [] @@ -164,15 +181,13 @@ def bind(instance_id: str, else: data['credentials'][_['name']] = val if success_flag: - data['last_operation'] = { - 'state': OperationState.SUCCEEDED.value, - 'description': "binding %s succeeded at %s" % (instance_id, time.time()) - } + data['last_operation']['state'] = OperationState.SUCCEEDED.value + data['last_operation']['description'] = ( + f"binding {instance_id} succeeded at {time.time()}") else: - data['last_operation'] = { - 'state': OperationState.FAILED.value, - 'description': "binding %s failed: %s" % (instance_id, ','.join(errors)) - } + data['last_operation']['state'] = OperationState.FAILED.value + data['last_operation']['description'] = ( + f"binding {instance_id} failed: {','.join(errors)}") bind_yaml = f'{chart_path}/templates/bind.yaml' if os.path.exists(bind_yaml): os.remove(bind_yaml) @@ -181,22 +196,43 @@ def bind(instance_id: str, @app.task(serializer='pickle') def unbind(instance_id): - with new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision"): + with ( + new_instance_lock(instance_id), + run_instance_hooks(instance_id, "deprovision") as (status, output) + ): backup_instance(instance_id) + data = load_binding_meta(instance_id) + if status != 0: + data['last_operation']['state'] = OperationState.FAILED.value + data['last_operation']['description'] = f"unbind {instance_id} failed: {output}" + save_binding_meta(instance_id, data) + return binding_file = get_binding_file(instance_id) if os.path.exists(binding_file): os.remove(binding_file) + data['last_operation']['state'] = OperationState.SUCCEEDED.value + data['last_operation']['description'] = f"unbind {instance_id} succeeded at {time.time()}" + save_binding_meta(instance_id, data) @app.task(serializer='pickle') def deprovision(instance_id: str): - with new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision"): + with ( + new_instance_lock(instance_id), + run_instance_hooks(instance_id, "deprovision") as (status, output) + ): backup_instance(instance_id) data = load_instance_meta(instance_id) + if status != 0: + data["last_operation"]["operation"] = "deprovision" + data["last_operation"]["state"] = OperationState.FAILED.value + data["last_operation"]["description"] = f"deprovision {instance_id} failed: {output}" + save_instance_meta(instance_id, data) + return data["last_operation"]["operation"] = "deprovision" data["last_operation"]["state"] = OperationState.IN_PROGRESS.value data["last_operation"]["description"] = ( - "deprovision %s in progress at %s" % (instance_id, time.time())) + f"deprovision {instance_id} in progress at {time.time()}") save_instance_meta(instance_id, data) args = [ "uninstall", data["details"]["context"]["instance_name"], @@ -206,8 +242,9 @@ def deprovision(instance_id: str): status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value - data["last_operation"]["description"] = "deprovision error:\n%s" % output + data["last_operation"]["description"] = f"deprovision {instance_id} failed: {output}" else: data["last_operation"]["state"] = OperationState.SUCCEEDED.value - data["last_operation"]["description"] = "deprovision succeeded at %s" % time.time() + data["last_operation"]["description"] = ( + f"deprovision {instance_id} succeeded at {time.time()}") save_instance_meta(instance_id, data) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index 6114f4a..ff19967 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -62,8 +62,9 @@ def run_instance_hooks(instance_id, stage): status, output = subprocess.getstatusoutput(pre_script_file) result.append({"script": pre_script_file, "status": status, "output": output}) else: - logger.debug(f"skip running {pre_script_file}") - yield + status, output = 0, f"skip running {pre_script_file}" + logger.debug(output) + yield status, output finally: if os.path.exists(pre_script_file): status, output = subprocess.getstatusoutput(post_script_file) From 1b56efaccc03b87566d0732f8a54ad3bd90045c0 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 6 Aug 2024 10:04:48 +0800 Subject: [PATCH 54/75] chore(helmbroker): bump new version --- .woodpecker/test-linux.yml | 3 ++- Makefile | 2 +- rootfs/Dockerfile | 6 +++--- rootfs/Dockerfile.test | 10 ++++++---- rootfs/bin/test-unit | 2 +- rootfs/bin/upload-coverage | 6 +++++- rootfs/dev_requirements.txt | 9 +++------ rootfs/helmbroker/tasks.py | 6 +++--- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml index d47ccc5..16a01b5 100644 --- a/.woodpecker/test-linux.yml +++ b/.woodpecker/test-linux.yml @@ -11,10 +11,11 @@ steps: - name: test-linux image: bash commands: - - make test + - make test upload-coverage secrets: - codename - dev_registry + - codecov_token when: event: - push diff --git a/Makefile b/Makefile index 26df599..fed80c8 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,6 @@ test-integration: upload-coverage: $(eval CI_ENV := $(shell curl -s https://codecov.io/env | bash)) - podman run --rm ${CI_ENV} -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs ${IMAGE}.test /tmp/test/rootfs/bin/upload-coverage + podman run --rm ${CI_ENV} -v ${CURDIR}:/tmp/test -w /tmp/test/rootfs -e CODECOV_TOKEN=${CODECOV_TOKEN} ${IMAGE}.test /tmp/test/rootfs/bin/upload-coverage .PHONY: check-kubectl check-podman build podman-build podman-build-test deploy clean commit-hook full-clean test test-style test-unit test-functional test-integration upload-coverage diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 806f7c7..e97938a 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.11" \ - HELM_VERSION="3.12.1" \ - KUBECTL_VERSION="1.27.3" + PYTHON_VERSION="3.12" \ + HELM_VERSION="3.15.3" \ + KUBECTL_VERSION="1.30.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 8cbb6e6..b5c1d34 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.11" \ - HELM_VERSION="3.12.1" \ - KUBECTL_VERSION="1.27.3" + PYTHON_VERSION="3.12" \ + HELM_VERSION="3.15.3" \ + KUBECTL_VERSION="1.30.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} @@ -15,7 +15,9 @@ COPY . ${DRYCC_HOME_DIR} WORKDIR ${DRYCC_HOME_DIR} RUN buildDeps='musl-dev'; \ - install-packages ${buildDeps} openssl ca-certificates \ + install-packages ${buildDeps} git openssl ca-certificates \ + && curl -SsL https://cli.codecov.io/latest/$([ $(dpkg --print-architecture) == "arm64" ] && echo linux-arm64 || echo linux)/codecov -o /usr/local/bin/codecov \ + && chmod +x /usr/local/bin/codecov \ && install-stack python ${PYTHON_VERSION} \ && install-stack helm ${HELM_VERSION} \ && install-stack kubectl ${KUBECTL_VERSION} && . init-stack \ diff --git a/rootfs/bin/test-unit b/rootfs/bin/test-unit index ef9755f..1d1d54e 100755 --- a/rootfs/bin/test-unit +++ b/rootfs/bin/test-unit @@ -3,4 +3,4 @@ # This script is designed to be run inside the container # -python -m unittest tests/test_*.py \ No newline at end of file +coverage run -m unittest tests/test_*.py diff --git a/rootfs/bin/upload-coverage b/rootfs/bin/upload-coverage index 53433f3..492452b 100755 --- a/rootfs/bin/upload-coverage +++ b/rootfs/bin/upload-coverage @@ -6,4 +6,8 @@ # fail hard and fast even on pipelines set -eou pipefail -codecov --required +coverage report -m > coverage.txt + +if [[ -n $CODECOV_TOKEN ]]; then + codecov upload-process --plugin=noop -t "$CODECOV_TOKEN" +fi diff --git a/rootfs/dev_requirements.txt b/rootfs/dev_requirements.txt index b46d2dc..ca19f67 100644 --- a/rootfs/dev_requirements.txt +++ b/rootfs/dev_requirements.txt @@ -1,13 +1,10 @@ # test module # test # Run "make test-unit" for the % of code exercised during tests -coverage==7.2.7 +coverage==7.6.1 # Run "make test-style" to check python syntax and style -flake8==6.0.0 - -# code coverage report at https://codecov.io/github/drycc/controller -codecov==2.1.13 +flake8==7.1.1 # mock out python-requests, mostly k8s -requests-mock==1.11.0 +requests-mock==1.12.1 diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 9cfda51..baf7aae 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -61,9 +61,9 @@ def provision(instance_id: str, details: ProvisionDetails): if addon_values_file: args.insert(9, "-f") args.insert(10, addon_values_file) - logger.debug(f"helm install parameters :{details.parameters}") + logger.debug(f"helm install parameters: {details.parameters}") args = format_params_to_helm_args(instance_id, details.parameters, args) - logger.debug(f"helm install args:{args}") + logger.debug(f"helm install args: {args}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value @@ -118,7 +118,7 @@ def update(instance_id: str, details: UpdateDetails): params = data['details']['parameters'] logger.debug(f"helm upgrade parameters: {params}") args = format_params_to_helm_args(instance_id, params, args) - logger.debug(f"helm upgrade args:{args}") + logger.debug(f"helm upgrade args: {args}") status, output = helm(instance_id, *args) if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value From 880aed0384a9c2e6fae7c058f4fed7b0dab6f9bb Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Thu, 12 Sep 2024 17:37:06 +0800 Subject: [PATCH 55/75] chore(celery): use quorum queye --- charts/helmbroker/templates/_helpers.tpl | 2 +- rootfs/helmbroker/celery.py | 30 ++++++++---------------- rootfs/requirements.txt | 4 ++-- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 39282a7..cc0c838 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -22,7 +22,7 @@ env: name: rabbitmq-creds key: password - name: "HELMBROKER_RABBITMQ_URL" - value: "amqp://$(HELMBROKER_RABBITMQ_USERNAME):$(HELMBROKER_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/drycc" + value: "amqp://$(HELMBROKER_RABBITMQ_USERNAME):$(HELMBROKER_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/helmbroker" {{- end }} {{- if (.Values.redisUrl) }} - name: HELMBROKER_REDIS_URL diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index e9699d2..67f810f 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -33,43 +33,33 @@ class Config(object): task_routes={ 'helmbroker.tasks.provision': { 'queue': 'helmbroker.high', - 'exchange': 'helmbroker.priority', - 'routing_key': 'helmbroker.priority.high', + 'exchange': 'helmbroker.priority', 'routing_key': 'helmbroker.priority.high', }, 'helmbroker.tasks.update': { 'queue': 'helmbroker.high', - 'exchange': 'helmbroker.priority', - 'routing_key': 'helmbroker.priority.high', + 'exchange': 'helmbroker.priority', 'routing_key': 'helmbroker.priority.high', }, 'helmbroker.tasks.bind': { 'queue': 'helmbroker.high', - 'exchange': 'helmbroker.priority', - 'routing_key': 'helmbroker.priority.high', + 'exchange': 'helmbroker.priority', 'routing_key': 'helmbroker.priority.high', }, 'helmbroker.tasks.deprovision': { 'queue': 'helmbroker.middle', - 'exchange': 'helmbroker.priority', - 'routing_key': 'helmbroker.priority.middle', + 'exchange': 'helmbroker.priority', 'routing_key': 'helmbroker.priority.middle', }, }, task_queues=( Queue( - 'helmbroker.low', - exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.low', - queue_arguments={'x-max-priority': 16}, + 'helmbroker.low', exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.low', queue_arguments={'x-queue-type': 'quorum'}, ), Queue( - 'helmbroker.high', - exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.high', - queue_arguments={'x-max-priority': 64}, + 'helmbroker.high', exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.high', queue_arguments={'x-queue-type': 'quorum'}, ), Queue( - 'helmbroker.middle', - exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.middle', - queue_arguments={'x-max-priority': 32}, + 'helmbroker.middle', exchange=Exchange('helmbroker.priority', type="direct"), + routing_key='helmbroker.priority.middle', queue_arguments={'x-queue-type': 'quorum'}, ), ), ) diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index b88c2ac..c889eb3 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -2,6 +2,6 @@ PyYAML==6.0.1 gunicorn==22.0.0 openbrokerapi==4.6.0 requests==2.31.0 -celery==5.4.0 +celery==5.5.0b3 redis==5.0.8 -jsonschema==4.21.1 \ No newline at end of file +jsonschema==4.21.1 From fbcd19a66c044feb1b5730a57e06641edcb70e90 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Mon, 28 Oct 2024 17:55:21 +0800 Subject: [PATCH 56/75] chore(helmbroker): yaml load all templates --- rootfs/helmbroker/tasks.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index baf7aae..b51cf7a 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -165,10 +165,14 @@ def bind(instance_id: str, if status != 0: data["last_operation"]["state"] = OperationState.FAILED.value data["last_operation"]["description"] = f"binding {instance_id} failed: {templates}" - credential_template = yaml.load(templates.split('bind.yaml')[1], Loader=yaml.Loader) + credential_template = {} + templates = yaml.load_all(templates, Loader=yaml.SafeLoader) + credential_template = next( + (item for item in templates if isinstance(item, dict) and "credential" in item), {} + ) success_flag = True errors = [] - for _ in credential_template['credential']: + for _ in credential_template.get('credential', {}): if _.get('valueFrom'): status, val = get_cred_value(details.context["namespace"], _['valueFrom']) elif _.get('value'): From 3dd6d176b44e0031f0a26728b744ebbcd6434070 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Wed, 13 Nov 2024 08:25:57 +0800 Subject: [PATCH 57/75] chore(tasks): use reset-then-reuse-values replace reuse-values --- rootfs/helmbroker/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index b51cf7a..8f19af7 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -108,7 +108,7 @@ def update(instance_id: str, details: UpdateDetails): args = [ "upgrade", details.context["instance_name"], chart_path, "--namespace", details.context["namespace"], "--create-namespace", - "--wait", "--timeout", "25m0s", "--reuse-values", "-f", values_file, + "--wait", "--timeout", "25m0s", "--reset-then-reuse-values", "-f", values_file, "--set", f"fullnameOverride=helmbroker-{details.context['instance_name']}" ] addon_values_file = save_addon_values(details.service_id, instance_id) From 24d4d571509ac4cb748318b5d7f31cc42276392b Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Thu, 14 Nov 2024 09:16:11 +0800 Subject: [PATCH 58/75] chore(python): upgrade requirements version --- rootfs/requirements.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index c889eb3..1141fae 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -1,7 +1,7 @@ -PyYAML==6.0.1 -gunicorn==22.0.0 -openbrokerapi==4.6.0 -requests==2.31.0 -celery==5.5.0b3 -redis==5.0.8 -jsonschema==4.21.1 +PyYAML==6.0.2 +gunicorn==23.0.0 +openbrokerapi==4.7.1 +requests==2.32.2 +celery==5.5.0rc1 +redis==5.2.0 +jsonschema==4.23.0 From 82c6ca41b6d07d036d4fba699968c8ec4ee031e3 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Sun, 24 Nov 2024 20:17:33 +0800 Subject: [PATCH 59/75] feat(valkey): add valkey sentinel support --- .woodpecker/chart.yaml | 1 + charts/helmbroker/Chart.yaml | 7 +-- charts/helmbroker/templates/_helpers.tpl | 37 ++++---------- .../templates/helmbroker-deployment.yaml | 2 +- .../templates/helmbroker-secret-creds.yaml | 10 ++++ charts/helmbroker/values.yaml | 32 ++++-------- rootfs/helmbroker/celery.py | 49 +++++++++++++------ rootfs/helmbroker/config.py | 2 +- rootfs/helmbroker/database/fetch.py | 2 +- rootfs/helmbroker/database/metadata.py | 31 ++++++------ rootfs/helmbroker/utils.py | 26 +++++++--- rootfs/requirements.txt | 2 +- 12 files changed, 104 insertions(+), 97 deletions(-) create mode 100644 charts/helmbroker/templates/helmbroker-secret-creds.yaml diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index 214841c..67df5de 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -11,6 +11,7 @@ steps: - export APP_VERSION=$([ -z $CI_COMMIT_TAG ] && echo $CI_COMMIT_SHA || echo $VERSION) - export CHART_VERSION=$([ -z $CI_COMMIT_TAG ] && echo 1.0.0 || echo $VERSION) - sed -i "s/imageTag:\ \"canary\"/imageTag:\ $IMAGE_TAG/g" charts/$${CI_REPO_NAME}/values.yaml + - sed -i s#{{repository}}#oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts)#g charts/$${CI_REPO_NAME}/Chart.yaml - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index da3007f..6e22e3a 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -6,11 +6,8 @@ dependencies: - name: common repository: oci://registry.drycc.cc/charts version: ~1.1.2 - - name: redis - repository: oci://registry.drycc.cc/charts - version: x.x.x - - name: rabbitmq - repository: oci://registry.drycc.cc/charts + - name: valkey + repository: {{repository}} version: x.x.x description: Drycc Workflow helmbroker. maintainers: diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index cc0c838..4702bc6 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -7,39 +7,20 @@ env: value: {{ if .Values.username | default "" | ne "" }}{{ .Values.username }}{{ else }}{{ randAlphaNum 32 }}{{ end }} - name: HELMBROKER_PASSWORD value: {{ if .Values.password | default "" | ne "" }}{{ .Values.password }}{{ else }}{{ randAlphaNum 32 }}{{ end }} -{{- if (.Values.rabbitmqUrl) }} -- name: HELMBROKER_RABBITMQ_URL - value: {{ .Values.rabbitmqUrl }} -{{- else if eq .Values.global.rabbitmqLocation "on-cluster" }} -- name: "HELMBROKER_RABBITMQ_USERNAME" +{{- if (.Values.valkeyUrl) }} +- name: HELMBROKER_VALKEY_URL valueFrom: secretKeyRef: - name: rabbitmq-creds - key: username -- name: "HELMBROKER_RABBITMQ_PASSWORD" + name: helmbroker-creds + key: valkey-url +{{- else if eq .Values.global.valkeyLocation "on-cluster" }} +- name: VALKEY_PASSWORD valueFrom: secretKeyRef: - name: rabbitmq-creds + name: valkey-creds key: password -- name: "HELMBROKER_RABBITMQ_URL" - value: "amqp://$(HELMBROKER_RABBITMQ_USERNAME):$(HELMBROKER_RABBITMQ_PASSWORD)@drycc-rabbitmq.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:5672/helmbroker" -{{- end }} -{{- if (.Values.redisUrl) }} -- name: HELMBROKER_REDIS_URL - value: {{ .Values.redisUrl }} -{{- else if eq .Values.global.redisLocation "on-cluster" }} -- name: "HELMBROKER_REDIS_ADDRS" - valueFrom: - secretKeyRef: - name: redis-creds - key: addrs -- name: "HELMBROKER_REDIS_PASSWORD" - valueFrom: - secretKeyRef: - name: redis-creds - key: password -- name: "HELMBROKER_REDIS_URL" - value: "redis://:$(HELMBROKER_REDIS_PASSWORD)@$(HELMBROKER_REDIS_ADDRS)/0" +- name: HELMBROKER_VALKEY_URL + value: "redis://:$(VALKEY_PASSWORD)@drycc-valkey.{{.Release.Namespace}}.svc.{{.Values.global.clusterDomain}}:26379/0?master_set=drycc" {{- end }} {{- range $key, $value := .Values.environment }} - name: {{ $key }} diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 7a711ed..6ad1385 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -34,7 +34,7 @@ spec: - netcat - -v - -u - - $(HELMBROKER_REDIS_URL),$(HELMBROKER_RABBITMQ_URL) + - $(HELMBROKER_VALKEY_URL) {{- include "helmbroker.envs" . | indent 10 }} - name: drycc-helmbroker-fetch image: {{.Values.imageRegistry}}/{{.Values.imageOrg}}/helmbroker:{{.Values.imageTag}} diff --git a/charts/helmbroker/templates/helmbroker-secret-creds.yaml b/charts/helmbroker/templates/helmbroker-secret-creds.yaml new file mode 100644 index 0000000..29b1bc3 --- /dev/null +++ b/charts/helmbroker/templates/helmbroker-secret-creds.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: helmbroker-creds + labels: + heritage: drycc +data: + {{- if (.Values.valkeyUrl) }} + valkey-url: {{ .Values.valkeyUrl | b64enc }} + {{- end }} \ No newline at end of file diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index d8c5375..60915f7 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -23,8 +23,8 @@ diagnosticMode: ## config the helm-broker repositories repositories: -- name: drycc-helm-broker - url: https://github.com/drycc/addons/releases/download/latest/index.yaml + - name: drycc-helm-broker + url: https://github.com/drycc/addons/releases/download/latest/index.yaml celeryReplicas: 1 @@ -33,11 +33,8 @@ celeryReplicas: 1 username: admin password: admin -# Configuring this will no longer use the built-in redis component -redisUrl: "" - -# Configuring this will no longer use the built-in rabbitmq component -rabbitmqUrl: "" +# Configuring this will no longer use the built-in valkey component +valkeyUrl: "" # Any custom controller environment variables # can be specified as key-value pairs under environment @@ -51,7 +48,7 @@ api: key: "drycc.cc/node" type: "soft" values: - - "true" + - "true" podAffinityPreset: type: "" extraMatchLabels: @@ -66,7 +63,7 @@ celery: key: "drycc.cc/node" type: "soft" values: - - "true" + - "true" podAffinityPreset: type: "" extraMatchLabels: @@ -76,10 +73,6 @@ celery: extraMatchLabels: app: "drycc-helmbroker-celery" -# drycc redis replicas must always be set to 1 -redis: - replicas: 1 - # Default override of addon values addonValues: {} @@ -91,16 +84,11 @@ persistence: volumeName: "" global: - # Set the location of Workflow's redis instance - # Valid values are: - # - on-cluster: Run Redis within the Kubernetes cluster - # - off-cluster: Run Redis outside the Kubernetes cluster (configure in controller section) - redisLocation: "on-cluster" - # Set the location of Workflow's rabbitmq instance + # Set the location of Workflow's valkey instance # Valid values are: - # - on-cluster: Run Rabbitmq within the Kubernetes cluster - # - off-cluster: Run Rabbitmq outside the Kubernetes cluster (configure in controller section) - rabbitmqLocation: "on-cluster" + # - on-cluster: Run Valkey within the Kubernetes cluster + # - off-cluster: Run Valkey outside the Kubernetes cluster (configure in controller section) + valkeyLocation: "on-cluster" # Enable usage of RBAC authorization mode # # Valid values are: diff --git a/rootfs/helmbroker/celery.py b/rootfs/helmbroker/celery.py index 67f810f..45d4181 100644 --- a/rootfs/helmbroker/celery.py +++ b/rootfs/helmbroker/celery.py @@ -1,28 +1,32 @@ import os +from urllib.parse import urlparse, parse_qs, urlencode from kombu import Exchange, Queue from celery import Celery +from .config import VALKEY_URL class Config(object): # Celery Configuration Options - timezone = "Asia/Shanghai" enable_utc = True task_serializer = 'pickle' accept_content = frozenset([ - 'application/data', - 'application/text', - 'application/json', - 'application/x-python-serialize', + 'application/data', + 'application/text', + 'application/json', + 'application/x-python-serialize', ]) task_track_started = True task_time_limit = 30 * 60 worker_max_tasks_per_child = 200 + worker_prefetch_multiplier = 1 result_expires = 24 * 60 * 60 - broker_url = os.environ.get("HELMBROKER_RABBITMQ_URL", 'amqp://guest:guest@127.0.0.1:5672/') - broker_connection_retry_on_startup = True - task_default_queue = 'helmbroker.low' + cache_backend = 'django-cache' + task_default_queue = 'helmbroker.middle' task_default_exchange = 'helmbroker.priority' - task_default_routing_key = 'helmbroker.priority.low' + task_default_routing_key = 'helmbroker.priority.middle' + broker_transport_options = {"queue_order_strategy": "sorted"} + task_create_missing_queues = True + task_inherit_parent_priority = True broker_connection_retry_on_startup = True worker_cancel_long_running_tasks_on_connection_loss = True @@ -30,6 +34,7 @@ class Config(object): app = Celery('helmbroker') app.config_from_object(Config()) app.conf.update( + timezone=os.environ.get('TZ', 'UTC'), task_routes={ 'helmbroker.tasks.provision': { 'queue': 'helmbroker.high', @@ -51,22 +56,36 @@ class Config(object): task_queues=( Queue( 'helmbroker.low', exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.low', queue_arguments={'x-queue-type': 'quorum'}, + routing_key='helmbroker.priority.low', ), Queue( 'helmbroker.high', exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.high', queue_arguments={'x-queue-type': 'quorum'}, + routing_key='helmbroker.priority.high', ), Queue( 'helmbroker.middle', exchange=Exchange('helmbroker.priority', type="direct"), - routing_key='helmbroker.priority.middle', queue_arguments={'x-queue-type': 'quorum'}, + routing_key='helmbroker.priority.middle', ), ), ) app.autodiscover_tasks(("helmbroker.tasks",)) - - -app.config_from_object(Config()) +url = urlparse(VALKEY_URL) +query = parse_qs(url.query) +broker_transport_options = {"queue_order_strategy": "sorted", "visibility_timeout": 43200} +result_backend_transport_options = {} +if 'master_set' in query: + master_name = query.pop('master_set')[0] + password = url.netloc.split("@")[0].split(":")[1] + kwargs = {'sentinel_kwargs': {'password': password}, 'master_name': master_name} + broker_transport_options.update(kwargs) + result_backend_transport_options.update(kwargs) + VALKEY_URL = f"sentinel://{url.netloc}{url.path}?{urlencode(query)}" +app.conf.update( + broker_url=VALKEY_URL, + result_backend=VALKEY_URL, + broker_transport_options=broker_transport_options, + result_backend_transport_options=result_backend_transport_options, +) if __name__ == '__main__': app.start() diff --git a/rootfs/helmbroker/config.py b/rootfs/helmbroker/config.py index 6def522..a3e9f47 100644 --- a/rootfs/helmbroker/config.py +++ b/rootfs/helmbroker/config.py @@ -10,7 +10,7 @@ USERNAME = os.environ.get('HELMBROKER_USERNAME') PASSWORD = os.environ.get('HELMBROKER_PASSWORD') -REDIS_URL = os.environ.get("HELMBROKER_REDIS_URL", 'redis://localhost:6379/0') +VALKEY_URL = os.environ.get("HELMBROKER_VALKEY_URL", 'redis://localhost:6379/0') class Config: diff --git a/rootfs/helmbroker/database/fetch.py b/rootfs/helmbroker/database/fetch.py index 4fe252f..d2d8102 100644 --- a/rootfs/helmbroker/database/fetch.py +++ b/rootfs/helmbroker/database/fetch.py @@ -58,7 +58,7 @@ def _fetch_addon(url, dest): os.makedirs(dest, exist_ok=True) with tarfile.open(fileobj=tgz_file, mode="r:gz") as tarobj: for tarinfo in tarobj: - tarobj.extract(tarinfo.name, dest) + tarobj.extract(tarinfo.name, dest, filter='data') filename1 = os.path.join(dest, "meta.yaml") with open(filename1, "r") as f1: meta = yaml.load(stream=f1, Loader=yaml.Loader) diff --git a/rootfs/helmbroker/database/metadata.py b/rootfs/helmbroker/database/metadata.py index 6b37041..2877aaa 100644 --- a/rootfs/helmbroker/database/metadata.py +++ b/rootfs/helmbroker/database/metadata.py @@ -4,8 +4,8 @@ import logging import jsonschema -from redis import client -from ..config import ADDONS_PATH, REDIS_URL +from ..utils import get_valkey_client +from ..config import ADDONS_PATH logger = logging.getLogger(__name__) @@ -116,21 +116,20 @@ def save_instance_meta(instance_id, data): json_data = json.dumps(data, sort_keys=True, indent=2) with open(file, "w") as f: f.write(json_data) - redis = client.Redis.from_url(REDIS_URL) - redis.set(cache_key, json_data) + get_valkey_client().set(cache_key, json_data) def load_instance_meta(instance_id): cache_key = f"helmbroker:instance:{instance_id}" - redis = client.Redis.from_url(REDIS_URL) + valkey = get_valkey_client() - json_data = redis.get(cache_key) + json_data = valkey.get(cache_key) if not json_data: from .query import get_instance_file file = get_instance_file(instance_id) with open(file) as f: json_data = f.read() - redis.set(cache_key, json_data) + valkey.set(cache_key, json_data) return json.loads(json_data) @@ -144,20 +143,19 @@ def save_binding_meta(instance_id, data): json_data = json.dumps(data, sort_keys=True, indent=2) with open(file, "w") as f: f.write(json_data) - redis = client.Redis.from_url(REDIS_URL) - redis.set(cache_key, json_data) + get_valkey_client().set(cache_key, json_data) def load_binding_meta(instance_id): from .query import get_binding_file cache_key = f"helmbroker:binding:{instance_id}" - redis = client.Redis.from_url(REDIS_URL) - json_data = redis.get(cache_key) + valkey = get_valkey_client() + json_data = valkey.get(cache_key) if not json_data: file = get_binding_file(instance_id) with open(file, 'r') as f: json_data = f.read() - redis.set(cache_key, json_data) + valkey.set(cache_key, json_data) return json.loads(json_data) @@ -170,18 +168,17 @@ def save_addons_meta(data): json_data = json.dumps(data, sort_keys=True, indent=2) with open(file, "w") as f: f.write(json_data) - redis = client.Redis.from_url(REDIS_URL) - redis.set(cache_key, json_data) + get_valkey_client().set(cache_key, json_data) def load_addons_meta(): cache_key = "helmbroker:addons" - redis = client.Redis.from_url(REDIS_URL) + valkey = get_valkey_client() - json_data = redis.get(cache_key) + json_data = valkey.get(cache_key) if not json_data: file = os.path.join(ADDONS_PATH, "addons.json") with open(file, 'r') as f: json_data = f.read() - redis.set(cache_key, json_data) + valkey.set(cache_key, json_data) return json.loads(json_data) diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index ff19967..b76e1b4 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -5,13 +5,13 @@ import base64 import copy import logging - +from urllib.parse import urlparse, parse_qs from contextlib import contextmanager -from redis import client -from .config import REDIS_URL +from redis.client import Redis +from redis.sentinel import Sentinel +from .config import VALKEY_URL logger = logging.getLogger(__name__) - REGISTRY_CONFIG_SUFFIX = '.config/helm/registry.json' REPOSITORY_CACHE_SUFFIX = '.cache/helm/repository' REPOSITORY_CONFIG_SUFFIX = '.config/helm/repository' @@ -42,9 +42,23 @@ def helm(instance_id, *args, output_type="text"): return command("helm", *new_args, output_type=output_type) +def get_valkey_client(): + url = urlparse(VALKEY_URL) + query = parse_qs(url.query) + if 'master_set' in query: + user, host = url.netloc.split("@") + password = user.split(":")[1] + sentinel = Sentinel( + [host.split(":")], + sentinel_kwargs={'password': password}, + password=password, + ) + return sentinel.master_for(query['master_set'][0], socket_timeout=1) + return Redis.from_url(VALKEY_URL) + + def new_instance_lock(instance_id): - redis = client.Redis.from_url(REDIS_URL) - return redis.lock(instance_id) + return get_valkey_client().lock(instance_id) @contextmanager diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index 1141fae..df9e44a 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -2,6 +2,6 @@ PyYAML==6.0.2 gunicorn==23.0.0 openbrokerapi==4.7.1 requests==2.32.2 -celery==5.5.0rc1 +celery==5.4.0 redis==5.2.0 jsonschema==4.23.0 From 692aab2bcc76161dd9ed326d1a99ef5f2cb53009 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Wed, 27 Nov 2024 13:43:38 +0800 Subject: [PATCH 60/75] chore(helmbroker): update state before upgrade --- rootfs/helmbroker/broker.py | 13 ++++++++----- rootfs/helmbroker/tasks.py | 9 ++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 8b38269..4ba8eb8 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -1,6 +1,6 @@ import os -import shutil import logging +import time from typing import Union, List, Optional from openbrokerapi.catalog import ServicePlan @@ -18,7 +18,8 @@ from .database.query import get_instance_path, get_chart_path, get_plan_path, \ get_addon_updateable, get_addon_bindable, get_addon_allow_params, \ get_addon_archive, get_binding_file, get_instance_file -from .database.metadata import load_instance_meta, load_binding_meta, load_addons_meta +from .database.metadata import load_instance_meta, load_binding_meta, load_addons_meta, \ + save_instance_meta from .tasks import provision, bind, deprovision, update, unbind logger = logging.getLogger(__name__) @@ -94,9 +95,6 @@ def bind(self, instance_path = get_instance_path(instance_id) if os.path.exists(f'{instance_path}/bind.json'): raise ErrBindingAlreadyExists() - chart_path, plan_path = ( - get_chart_path(instance_id), get_plan_path(instance_id)) - shutil.copy(f'{plan_path}/bind.yaml', f'{chart_path}/templates') bind(instance_id, binding_id, details, async_allowed, **kwargs) data = load_binding_meta(instance_id) if data["last_operation"]["state"] == OperationState.SUCCEEDED.value: @@ -145,6 +143,11 @@ def update(self, if details.plan_id is not None: chart_path, plan_path = get_chart_path(instance_id), get_plan_path(instance_id) fetch_chart_plan(details.service_id, chart_path, details.plan_id, plan_path) + data = load_instance_meta(instance_id) + data['last_operation']["state"] = OperationState.IN_PROGRESS.value + data['last_operation']["description"] = ( + f"update {instance_id} in progress at {time.time()}") + save_instance_meta(instance_id, data) update.delay(instance_id, details) return UpdateServiceSpec(is_async=True) diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 8f19af7..750e603 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -2,6 +2,7 @@ import time import yaml import logging +import shutil from openbrokerapi.service_broker import ProvisionDetails, OperationState, \ UpdateDetails, BindDetails @@ -99,10 +100,6 @@ def update(instance_id: str, details: UpdateDetails): data["last_operation"]["description"] = f"update {instance_id} failed: {output}" save_instance_meta(instance_id, data) return - data['last_operation']["state"] = OperationState.IN_PROGRESS.value - data['last_operation']["description"] = ( - f"update {instance_id} in progress at {time.time()}") - save_instance_meta(instance_id, data) chart_path = get_chart_path(instance_id) values_file = os.path.join(get_plan_path(instance_id), "values.yaml") args = [ @@ -148,7 +145,9 @@ def bind(instance_id: str, save_binding_meta(instance_id, data) return save_binding_meta(instance_id, data) - chart_path = get_chart_path(instance_id) + chart_path, plan_path = ( + get_chart_path(instance_id), get_plan_path(instance_id)) + shutil.copy(f'{plan_path}/bind.yaml', f'{chart_path}/templates') values_file = os.path.join(get_plan_path(instance_id), "values.yaml") args = [ "template", details.context["instance_name"], chart_path, From 9f223bd98419250d2fa01f477f429d55ddb88a91 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Fri, 6 Dec 2024 16:14:57 +0800 Subject: [PATCH 61/75] chore(helmbroker): add debug config --- rootfs/helmbroker/broker.py | 8 +++++++- rootfs/helmbroker/config.py | 2 +- rootfs/helmbroker/gunicorn/logging.py | 4 ++-- rootfs/helmbroker/tasks.py | 12 +++++++++++- rootfs/helmbroker/wsgi.py | 3 ++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 4ba8eb8..c9bfa9a 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -43,6 +43,7 @@ def provision(self, details: ProvisionDetails, async_allowed: bool, **kwargs) -> ProvisionedServiceSpec: + logger.debug(f"*** provision instance {instance_id}") instance_path = get_instance_path(instance_id) if os.path.exists(instance_path): raise ErrInstanceAlreadyExists() @@ -83,6 +84,7 @@ def bind(self, async_allowed: bool, **kwargs ) -> Binding: + logger.debug(f"*** bind instance {instance_id}") is_addon_bindable = get_addon_bindable(details.service_id) if not is_addon_bindable: raise ErrBadRequest( @@ -110,7 +112,7 @@ def unbind(self, async_allowed: bool, **kwargs ) -> UnbindSpec: - logger.debug(f"unbind instance {instance_id}") + logger.debug(f"*** unbind instance {instance_id}") unbind.delay(instance_id) return UnbindSpec(is_async=True) @@ -120,6 +122,7 @@ def update(self, async_allowed: bool, **kwargs ) -> UpdateServiceSpec: + logger.debug(f"*** update instance {instance_id}") instance_path = get_instance_path(instance_id) if not os.path.exists(instance_path): raise ErrBadRequest(msg="Instance %s does not exist" % instance_id) @@ -156,6 +159,7 @@ def deprovision(self, details: DeprovisionDetails, async_allowed: bool, **kwargs) -> DeprovisionServiceSpec: + logger.debug(f"*** deprovision instance {instance_id}") if not os.path.exists(get_instance_path(instance_id)): raise ErrInstanceDoesNotExist() with new_instance_lock(instance_id): @@ -175,6 +179,7 @@ def last_operation(self, operation_data: Optional[str], **kwargs ) -> LastOperation: + logger.debug(f"*** last_operation instance {instance_id}") if os.path.exists(get_instance_file(instance_id)): data = load_instance_meta(instance_id) return LastOperation( @@ -189,6 +194,7 @@ def last_binding_operation(self, operation_data: Optional[str], **kwargs ) -> LastOperation: + logger.debug(f"*** last_binding_operation instance {instance_id}") if os.path.exists(get_binding_file(instance_id)): data = load_binding_meta(instance_id) return LastOperation( diff --git a/rootfs/helmbroker/config.py b/rootfs/helmbroker/config.py index a3e9f47..e5becfa 100644 --- a/rootfs/helmbroker/config.py +++ b/rootfs/helmbroker/config.py @@ -14,4 +14,4 @@ class Config: - DEBUG = bool(os.environ.get('HELMBROKER_DEBUG', True)) + DEBUG = bool(os.environ.get('HELMBROKER_DEBUG', False)) diff --git a/rootfs/helmbroker/gunicorn/logging.py b/rootfs/helmbroker/gunicorn/logging.py index f9e65f3..41a3f9e 100644 --- a/rootfs/helmbroker/gunicorn/logging.py +++ b/rootfs/helmbroker/gunicorn/logging.py @@ -1,12 +1,12 @@ -import os from gunicorn.glogging import Logger +from helmbroker.config import Config class Logging(Logger): def access(self, resp, req, environ, request_time): # health check endpoints are only logged in debug mode if ( - not os.environ.get('DEBUG', False) and + not Config.DEBUG and req.path in ['/readiness', '/healthz'] ): return diff --git a/rootfs/helmbroker/tasks.py b/rootfs/helmbroker/tasks.py index 750e603..69f2f30 100644 --- a/rootfs/helmbroker/tasks.py +++ b/rootfs/helmbroker/tasks.py @@ -20,10 +20,12 @@ @app.task(serializer='pickle') def provision(instance_id: str, details: ProvisionDetails): + logger.debug(f"*** task provision instance: {instance_id}, before lock") with ( new_instance_lock(instance_id), run_instance_hooks(instance_id, "provision") as (status, output) ): + logger.debug(f"*** task provision instance: {instance_id}") backup_instance(instance_id) # create instance.json data = { @@ -78,10 +80,12 @@ def provision(instance_id: str, details: ProvisionDetails): @app.task(serializer='pickle') def update(instance_id: str, details: UpdateDetails): + logger.debug(f"*** task update instance: {instance_id}, before lock") with ( new_instance_lock(instance_id), run_instance_hooks(instance_id, "update") as (status, output) ): + logger.debug(f"*** task update instance: {instance_id}") backup_instance(instance_id) data = load_instance_meta(instance_id) if details.service_id: @@ -133,10 +137,12 @@ def bind(instance_id: str, details: BindDetails, async_allowed: bool, **kwargs): + logger.debug(f"*** task bind instance: {instance_id}, before lock") with ( new_instance_lock(instance_id), run_instance_hooks(instance_id, "bind") as (status, output) ): + logger.debug(f"*** task bind instance: {instance_id}") backup_instance(instance_id) data = {"binding_id": binding_id, "credentials": {}, "last_operation": {}} if status != 0: @@ -199,10 +205,12 @@ def bind(instance_id: str, @app.task(serializer='pickle') def unbind(instance_id): + logger.debug(f"*** task unbind instance: {instance_id}, before lock") with ( new_instance_lock(instance_id), - run_instance_hooks(instance_id, "deprovision") as (status, output) + run_instance_hooks(instance_id, "unbind") as (status, output) ): + logger.debug(f"*** task unbind instance: {instance_id}") backup_instance(instance_id) data = load_binding_meta(instance_id) if status != 0: @@ -220,10 +228,12 @@ def unbind(instance_id): @app.task(serializer='pickle') def deprovision(instance_id: str): + logger.debug(f"*** task deprovision instance: {instance_id}, before lock") with ( new_instance_lock(instance_id), run_instance_hooks(instance_id, "deprovision") as (status, output) ): + logger.debug(f"*** task deprovision instance: {instance_id}") backup_instance(instance_id) data = load_instance_meta(instance_id) if status != 0: diff --git a/rootfs/helmbroker/wsgi.py b/rootfs/helmbroker/wsgi.py index ac89884..64db221 100644 --- a/rootfs/helmbroker/wsgi.py +++ b/rootfs/helmbroker/wsgi.py @@ -1,4 +1,5 @@ import os +import logging from flask import Flask, make_response from openbrokerapi import api, log_util from helmbroker.broker import HelmServiceBroker @@ -27,5 +28,5 @@ def readiness(): catalog_api = api.get_blueprint( HelmServiceBroker(), api.BrokerCredentials(USERNAME, PASSWORD), - log_util.basic_config()) + log_util.basic_config(level=logging.DEBUG if Config.DEBUG else logging.INFO)) application.register_blueprint(catalog_api) From 4d0b65494ec512d096c5256ca806757fbb6dae1a Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 23 Dec 2024 09:19:11 +0800 Subject: [PATCH 62/75] fix(wooddpecker): secsets are deprecated --- .woodpecker/build-linux.yml | 17 +++++++++++------ .woodpecker/chart.yaml | 14 +++++++++----- .woodpecker/manifest.yml | 13 ++++++++----- .woodpecker/test-linux.yml | 11 +++++++---- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index 782d2f5..3424a97 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -14,12 +14,17 @@ steps: - export VERSION=$([ -z $CI_COMMIT_TAG ] && echo latest || echo $CI_COMMIT_TAG)-$(sed 's#/#-#g' <<< $CI_SYSTEM_PLATFORM) - echo $CONTAINER_PASSWORD | podman login $DRYCC_REGISTRY --username $CONTAINER_USERNAME --password-stdin > /dev/null 2>&1 - make podman-build podman-immutable-push - secrets: - - codename - - dev_registry - - drycc_registry - - container_username - - container_password + environment: + CODENAME: + from_secret: codename + DEV_REGISTRY: + from_secret: dev_registry + DRYCC_REGISTRY: + from_secret: drycc_registry + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password when: event: - push diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index 67df5de..6ee4b57 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -15,11 +15,15 @@ steps: - helm package -u charts/$${CI_REPO_NAME} --version $CHART_VERSION --app-version $APP_VERSION - echo $CONTAINER_PASSWORD | helm registry login $DRYCC_REGISTRY -u $CONTAINER_USERNAME --password-stdin - helm push $${CI_REPO_NAME}-$CHART_VERSION.tgz oci://$DRYCC_REGISTRY/$([ -z $CI_COMMIT_TAG ] && echo charts-testing || echo charts) - secrets: - - dev_registry - - drycc_registry - - container_username - - container_password + environment: + DEV_REGISTRY: + from_secret: dev_registry + DRYCC_REGISTRY: + from_secret: drycc_registry + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password when: event: - push diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml index b4735ee..010bcdd 100644 --- a/.woodpecker/manifest.yml +++ b/.woodpecker/manifest.yml @@ -8,8 +8,9 @@ steps: commands: - sed -i "s/{{project}}/$${CI_REPO_NAME}/g" .woodpecker/manifest.tmpl - sed -i "s/registry.drycc.cc/$${DRYCC_REGISTRY}/g" .woodpecker/manifest.tmpl - secrets: - - drycc_registry + environment: + DRYCC_REGISTRY: + from_secret: drycc_registry when: event: - tag @@ -26,9 +27,11 @@ steps: -v $(pwd):$(pwd) -w $(pwd) docker.io/plugins/manifest - secrets: - - container_username - - container_password + environment: + CONTAINER_USERNAME: + from_secret: container_username + CONTAINER_PASSWORD: + from_secret: container_password when: event: - tag diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml index 16a01b5..9a06860 100644 --- a/.woodpecker/test-linux.yml +++ b/.woodpecker/test-linux.yml @@ -12,10 +12,13 @@ steps: image: bash commands: - make test upload-coverage - secrets: - - codename - - dev_registry - - codecov_token + environment: + CODENAME: + from_secret: codename + DEV_REGISTRY: + from_secret: dev_registry + CODECOV_TOKEN: + from_secret: codecov_token when: event: - push From c11d9a6fb67c25f8c7c50da084a977bf6ed04e50 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 8 Apr 2025 16:40:26 +0800 Subject: [PATCH 63/75] chore(helmbroker): bump latest version --- rootfs/Dockerfile | 6 +++--- rootfs/Dockerfile.test | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index e97938a..0c84b11 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.12" \ - HELM_VERSION="3.15.3" \ - KUBECTL_VERSION="1.30.3" + PYTHON_VERSION="3.13" \ + HELM_VERSION="3.17.2" \ + KUBECTL_VERSION="1.32.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index b5c1d34..8c8d2a3 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.12" \ - HELM_VERSION="3.15.3" \ - KUBECTL_VERSION="1.30.3" + PYTHON_VERSION="3.13" \ + HELM_VERSION="3.17.2" \ + KUBECTL_VERSION="1.32.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} From ab96f3c0295acbc074711eaa1d01d8925fe60e49 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 8 Apr 2025 16:54:18 +0800 Subject: [PATCH 64/75] chore(woodpecker): add cron event --- .woodpecker/build-linux.yml | 1 + .woodpecker/chart.yaml | 1 + .woodpecker/manifest.yml | 2 ++ .woodpecker/test-linux.yml | 1 + rootfs/Dockerfile | 2 +- rootfs/Dockerfile.test | 2 +- 6 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.woodpecker/build-linux.yml b/.woodpecker/build-linux.yml index 3424a97..ef4ecfd 100644 --- a/.woodpecker/build-linux.yml +++ b/.woodpecker/build-linux.yml @@ -29,6 +29,7 @@ steps: event: - push - tag + - cron depends_on: - test-linux \ No newline at end of file diff --git a/.woodpecker/chart.yaml b/.woodpecker/chart.yaml index 6ee4b57..d40739c 100644 --- a/.woodpecker/chart.yaml +++ b/.woodpecker/chart.yaml @@ -28,6 +28,7 @@ steps: event: - push - tag + - cron depends_on: - manifest \ No newline at end of file diff --git a/.woodpecker/manifest.yml b/.woodpecker/manifest.yml index 010bcdd..7330ea0 100644 --- a/.woodpecker/manifest.yml +++ b/.woodpecker/manifest.yml @@ -15,6 +15,7 @@ steps: event: - tag - push + - cron - name: publish-manifest image: bash @@ -36,6 +37,7 @@ steps: event: - tag - push + - cron depends_on: - build-linux diff --git a/.woodpecker/test-linux.yml b/.woodpecker/test-linux.yml index 9a06860..ab8ee69 100644 --- a/.woodpecker/test-linux.yml +++ b/.woodpecker/test-linux.yml @@ -23,3 +23,4 @@ steps: event: - push - tag + - cron diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index 0c84b11..b6dad30 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -4,7 +4,7 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.13" \ + PYTHON_VERSION="3.12" \ HELM_VERSION="3.17.2" \ KUBECTL_VERSION="1.32.3" diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 8c8d2a3..7596355 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -4,7 +4,7 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.13" \ + PYTHON_VERSION="3.12" \ HELM_VERSION="3.17.2" \ KUBECTL_VERSION="1.32.3" From e7375a7e8e9c177c6888fd87d4d6a065a0ef1c80 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Mon, 12 May 2025 12:37:06 +0800 Subject: [PATCH 65/75] chore(charts): change resources format --- charts/helmbroker/templates/_helpers.tpl | 14 -------------- .../templates/helmbroker-celery-deployment.yaml | 5 ++++- .../templates/helmbroker-deployment.yaml | 5 ++++- charts/helmbroker/values.yaml | 16 ++++++++++++++-- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 4702bc6..8cb0ee7 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -28,20 +28,6 @@ env: {{- end }} {{- end }} -{{/* Generate helmbroker deployment limits */}} -{{- define "helmbroker.limits" -}} -{{- if or (.Values.limitsCpu) (.Values.limitsMemory) }} -resources: - limits: -{{- if (.Values.limitsCpu) }} - cpu: {{.Values.limitsCpu}} -{{- end }} -{{- if (.Values.limitsMemory) }} - memory: {{.Values.limitsMemory}} -{{- end }} -{{- end }} -{{- end }} - {{/* Generate helmbroker deployment volumeMounts */}} {{- define "helmbroker.volumeMounts" }} volumeMounts: diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index 7388b2c..d2b4ab5 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -49,7 +49,10 @@ spec: - -c - celery --app helmbroker worker --queues helmbroker.low,helmbroker.middle,helmbroker.high --autoscale=32,1 --loglevel=WARNING {{- end }} - {{- include "helmbroker.limits" $ | indent 8 }} + {{- with index .Values "celery" "resources" }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} {{- include "helmbroker.envs" $ | indent 8 }} {{- include "helmbroker.volumeMounts" $ | indent 8 }} {{- include "helmbroker.volumes" . | indent 6 }} diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 6ad1385..6659e7d 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -71,7 +71,10 @@ spec: ports: - containerPort: 8000 name: http - {{- include "helmbroker.limits" . | indent 8 }} + {{- with index .Values "api" "resources" }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} {{- include "helmbroker.envs" . | indent 8 }} {{- include "helmbroker.volumeMounts" . | indent 8 }} {{- include "helmbroker.volumes" . | indent 6 }} diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 60915f7..3af3f08 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -3,8 +3,6 @@ imageTag: "canary" imageRegistry: "registry.drycc.cc" imagePullPolicy: "Always" replicas: 1 -# limitsCpu: "100m" -# limitsMemory: "50Mi" ## Enable diagnostic mode ## @@ -44,6 +42,13 @@ environment: # HELMBROKER_CONFIG_ROOT: /etc/helmbroker api: + resources: {} + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi nodeAffinityPreset: key: "drycc.cc/node" type: "soft" @@ -59,6 +64,13 @@ api: app: "drycc-helmbroker" celery: + resources: {} + # limits: + # cpu: 200m + # memory: 50Mi + # requests: + # cpu: 100m + # memory: 30Mi nodeAffinityPreset: key: "drycc.cc/node" type: "soft" From 5f8fd3c19705f1734f0c0b0c9a5849dca1008f70 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 15 Jul 2025 23:17:54 +0800 Subject: [PATCH 66/75] chore(workflow): remove cluster domain --- charts/helmbroker/Chart.yaml | 1 + charts/helmbroker/templates/_helpers.tpl | 4 ++-- .../helmbroker-celery-deployment.yaml | 4 ++-- charts/helmbroker/values.yaml | 19 ++----------------- rootfs/helmbroker/wsgi.py | 3 +-- 5 files changed, 8 insertions(+), 23 deletions(-) diff --git a/charts/helmbroker/Chart.yaml b/charts/helmbroker/Chart.yaml index 6e22e3a..6e8d2aa 100644 --- a/charts/helmbroker/Chart.yaml +++ b/charts/helmbroker/Chart.yaml @@ -9,6 +9,7 @@ dependencies: - name: valkey repository: {{repository}} version: x.x.x + condition: valkey.enabled description: Drycc Workflow helmbroker. maintainers: - name: Drycc Team diff --git a/charts/helmbroker/templates/_helpers.tpl b/charts/helmbroker/templates/_helpers.tpl index 8cb0ee7..3e9e40f 100644 --- a/charts/helmbroker/templates/_helpers.tpl +++ b/charts/helmbroker/templates/_helpers.tpl @@ -13,14 +13,14 @@ env: secretKeyRef: name: helmbroker-creds key: valkey-url -{{- else if eq .Values.global.valkeyLocation "on-cluster" }} +{{- else if .Values.valkey.enabled }} - name: VALKEY_PASSWORD valueFrom: secretKeyRef: name: valkey-creds key: password - name: HELMBROKER_VALKEY_URL - value: "redis://:$(VALKEY_PASSWORD)@drycc-valkey.{{.Release.Namespace}}.svc.{{.Values.global.clusterDomain}}:26379/0?master_set=drycc" + value: "redis://:$(VALKEY_PASSWORD)@drycc-valkey:26379/0?master_set=drycc" {{- end }} {{- range $key, $value := .Values.environment }} - name: {{ $key }} diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index d2b4ab5..d188ba5 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -33,8 +33,8 @@ spec: args: - netcat - -v - - -a - - $(DRYCC_HELMBROKER_SERVICE_HOST):$(DRYCC_HELMBROKER_SERVICE_PORT) + - -u + - http://drycc-helmbroker {{- include "helmbroker.envs" . | indent 10 }} containers: - name: drycc-helmbroker-celery diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 3af3f08..31fee4e 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -95,20 +95,5 @@ persistence: storageClass: "" volumeName: "" -global: - # Set the location of Workflow's valkey instance - # Valid values are: - # - on-cluster: Run Valkey within the Kubernetes cluster - # - off-cluster: Run Valkey outside the Kubernetes cluster (configure in controller section) - valkeyLocation: "on-cluster" - # Enable usage of RBAC authorization mode - # - # Valid values are: - # - true: all RBAC-related manifests will be installed (in case your cluster supports RBAC) - # - false: no RBAC-related manifests will be installed - rbac: true - # A domain name consists of one or more parts. - # Periods (.) are used to separate these parts. - # Each part must be 1 to 63 characters in length and can contain lowercase letters, digits, and hyphens (-). - # It must start and end with a lowercase letter or digit. - clusterDomain: "cluster.local" +valkey: + enabled: true diff --git a/rootfs/helmbroker/wsgi.py b/rootfs/helmbroker/wsgi.py index 64db221..206c711 100644 --- a/rootfs/helmbroker/wsgi.py +++ b/rootfs/helmbroker/wsgi.py @@ -18,8 +18,7 @@ def readiness(): if "KUBECONFIG" in os.environ: return "OK" elif "KUBERNETES_SERVICE_PORT" in os.environ and \ - ("KUBERNETES_SERVICE_HOST" in os.environ or - "KUBERNETES_CLUSTER_DOMAIN" in os.environ): + "KUBERNETES_SERVICE_HOST" in os.environ: return "OK" return make_response("kubernetes not available", 500) From bf76ea5c889ae36c5cd065e391b240f9a167753b Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 2 Sep 2025 09:39:02 +0800 Subject: [PATCH 67/75] chore(charts): celery process isolation --- .../templates/helmbroker-celery-deployment.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index d188ba5..e3a3d67 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -37,7 +37,8 @@ spec: - http://drycc-helmbroker {{- include "helmbroker.envs" . | indent 10 }} containers: - - name: drycc-helmbroker-celery + {{- range $key := (list "low" "middle" "high") }} + - name: drycc-helmbroker-celery-{{$key}} image: {{$.Values.imageRegistry}}/{{$.Values.imageOrg}}/helmbroker:{{$.Values.imageTag}} imagePullPolicy: {{$.Values.imagePullPolicy}} {{- if $.Values.diagnosticMode.enabled }} @@ -47,12 +48,13 @@ spec: args: - /bin/bash - -c - - celery --app helmbroker worker --queues helmbroker.low,helmbroker.middle,helmbroker.high --autoscale=32,1 --loglevel=WARNING + - celery --app helmbroker worker -n {{uuidv4}}@%h --queues helmbroker.{{$key}} --autoscale=32,1 --loglevel=WARNING {{- end }} - {{- with index .Values "celery" "resources" }} + {{- with index $.Values "celery" "resources" }} resources: {{- toYaml . | nindent 10 }} {{- end }} {{- include "helmbroker.envs" $ | indent 8 }} {{- include "helmbroker.volumeMounts" $ | indent 8 }} + {{- end }} {{- include "helmbroker.volumes" . | indent 6 }} From 1bc5efb22043f6bb9d5276df778e65eda99384b6 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Tue, 2 Sep 2025 17:11:27 +0800 Subject: [PATCH 68/75] chore(charts): change replicas location --- .../helmbroker/templates/helmbroker-celery-deployment.yaml | 2 +- charts/helmbroker/templates/helmbroker-deployment.yaml | 2 +- charts/helmbroker/values.yaml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index e3a3d67..b584e72 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -7,7 +7,7 @@ metadata: annotations: component.drycc.cc/version: {{ .Values.imageTag }} spec: - replicas: {{ .Values.celeryReplicas }} + replicas: {{ .Values.celery.replicas }} strategy: rollingUpdate: maxSurge: 1 diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index 6659e7d..fe9a2ed 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -7,7 +7,7 @@ metadata: annotations: component.drycc.cc/version: {{ .Values.imageTag }} spec: - replicas: {{ .Values.replicas }} + replicas: {{ .Values.api.replicas }} strategy: rollingUpdate: maxSurge: 1 diff --git a/charts/helmbroker/values.yaml b/charts/helmbroker/values.yaml index 31fee4e..65b9f54 100644 --- a/charts/helmbroker/values.yaml +++ b/charts/helmbroker/values.yaml @@ -2,7 +2,7 @@ imageOrg: "drycc-addons" imageTag: "canary" imageRegistry: "registry.drycc.cc" imagePullPolicy: "Always" -replicas: 1 + ## Enable diagnostic mode ## @@ -24,8 +24,6 @@ repositories: - name: drycc-helm-broker url: https://github.com/drycc/addons/releases/download/latest/index.yaml -celeryReplicas: 1 - # broker_credentials: # Optional Usernames and passwords that will be required to communicate with service broker username: admin @@ -42,6 +40,7 @@ environment: # HELMBROKER_CONFIG_ROOT: /etc/helmbroker api: + replicas: 1 resources: {} # limits: # cpu: 200m @@ -64,6 +63,7 @@ api: app: "drycc-helmbroker" celery: + replicas: 1 resources: {} # limits: # cpu: 200m From c806f29bc4a96e9595e7a2ce1928ae650edd8350 Mon Sep 17 00:00:00 2001 From: lijianguo Date: Tue, 2 Sep 2025 11:03:30 +0800 Subject: [PATCH 69/75] chore(helmbroker): support validate plan schema --- rootfs/helmbroker/broker.py | 10 +++++- rootfs/helmbroker/database/query.py | 4 +++ rootfs/helmbroker/utils.py | 50 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index c9bfa9a..19dda4b 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -13,7 +13,7 @@ UpdateDetails, UpdateServiceSpec, DeprovisionDetails, \ DeprovisionServiceSpec, LastOperation, OperationState -from .utils import verify_parameters, new_instance_lock +from .utils import verify_parameters, new_instance_lock, verify_parameters_by_plan from .database.fetch import fetch_chart_plan from .database.query import get_instance_path, get_chart_path, get_plan_path, \ get_addon_updateable, get_addon_bindable, get_addon_allow_params, \ @@ -64,6 +64,10 @@ def provision(self, os.makedirs(instance_path, exist_ok=True) chart_path, plan_path = get_chart_path(instance_id), get_plan_path(instance_id) fetch_chart_plan(details.service_id, chart_path, details.plan_id, plan_path) + # verify instance-schema + msg = verify_parameters_by_plan(instance_id, details.parameters) + if msg: + raise ErrBadRequest(msg) provision.delay(instance_id, details) return ProvisionedServiceSpec(state=ProvisionState.IS_ASYNC) @@ -146,6 +150,10 @@ def update(self, if details.plan_id is not None: chart_path, plan_path = get_chart_path(instance_id), get_plan_path(instance_id) fetch_chart_plan(details.service_id, chart_path, details.plan_id, plan_path) + # verify instance-schema + msg = verify_parameters_by_plan(instance_id, details.parameters) + if msg: + raise ErrBadRequest(msg) data = load_instance_meta(instance_id) data['last_operation']["state"] = OperationState.IN_PROGRESS.value data['last_operation']["description"] = ( diff --git a/rootfs/helmbroker/database/query.py b/rootfs/helmbroker/database/query.py index b7aa331..741cfc3 100644 --- a/rootfs/helmbroker/database/query.py +++ b/rootfs/helmbroker/database/query.py @@ -22,6 +22,10 @@ def get_plan_path(instance_id): return os.path.join(get_instance_path(instance_id), "plan") +def get_plan_schema_path(instance_id): + return os.path.join(get_instance_path(instance_id), "plan", "instance-schema.json") + + def get_hooks_path(instance_id): return os.path.join(get_plan_path(instance_id), "hooks") diff --git a/rootfs/helmbroker/utils.py b/rootfs/helmbroker/utils.py index b76e1b4..e749e85 100644 --- a/rootfs/helmbroker/utils.py +++ b/rootfs/helmbroker/utils.py @@ -5,6 +5,7 @@ import base64 import copy import logging +import jsonschema from urllib.parse import urlparse, parse_qs from contextlib import contextmanager from redis.client import Redis @@ -168,3 +169,52 @@ def _verify_required_parameters(allow_parameters, parameters): if error: error_parameters.add(allow_parameter["name"]) return error_parameters + + +def verify_parameters_by_plan(instance_id, parameters): + """verify parameters allowed or not""" + if not parameters: + return "" + # read schema file + from .database.query import get_plan_schema_path + schema_file = get_plan_schema_path(instance_id) + try: + with open(schema_file, 'r') as f: + schema = json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + return "" + if not schema: + return "" + # get parameters + if "rawValues" in parameters: + params = yaml.safe_load(base64.b64decode(parameters["rawValues"])) + else: + params = _convert_to_nested_dict(parameters) + # validate schema + try: + jsonschema.validate(params, schema) + except jsonschema.ValidationError as e: + return f"could not validate: {e.message}" + return "" + + +def _convert_to_nested_dict(assignments): + """ + {"a.b.c": "1Gi", "a.b.d": "2Gi"} + -> + {'a': {'b': {'c': '1Gi', 'd': '2Gi'}}} + """ + def set_nested_value(d, keys, value): + if len(keys) == 1: + d[keys[0]] = value + else: + if keys[0] not in d: + d[keys[0]] = {} + set_nested_value(d[keys[0]], keys[1:], value) + result = {} + if isinstance(assignments, dict): + # dict format: {"a.b.c": "1Gi"} + for key_path, value in assignments.items(): + keys = key_path.split('.') + set_nested_value(result, keys, value) + return result From 76b27bdb65a6907120ab93274aa4b3ae0d8dee00 Mon Sep 17 00:00:00 2001 From: jianxiaoguo Date: Fri, 12 Sep 2025 10:36:16 +0800 Subject: [PATCH 70/75] chore(helmbroker): verify instance_name length --- rootfs/helmbroker/broker.py | 5 +++++ rootfs/helmbroker/config.py | 1 + 2 files changed, 6 insertions(+) diff --git a/rootfs/helmbroker/broker.py b/rootfs/helmbroker/broker.py index 19dda4b..c920789 100644 --- a/rootfs/helmbroker/broker.py +++ b/rootfs/helmbroker/broker.py @@ -21,6 +21,7 @@ from .database.metadata import load_instance_meta, load_binding_meta, load_addons_meta, \ save_instance_meta from .tasks import provision, bind, deprovision, update, unbind +from .config import INSTANCE_NAME_LENS logger = logging.getLogger(__name__) @@ -44,6 +45,10 @@ def provision(self, async_allowed: bool, **kwargs) -> ProvisionedServiceSpec: logger.debug(f"*** provision instance {instance_id}") + # verify instance_name length + if len(details.context["instance_name"]) > INSTANCE_NAME_LENS: + raise ErrBadRequest( + msg=f"The length of the instance name cannot exceed {INSTANCE_NAME_LENS}.") instance_path = get_instance_path(instance_id) if os.path.exists(instance_path): raise ErrInstanceAlreadyExists() diff --git a/rootfs/helmbroker/config.py b/rootfs/helmbroker/config.py index e5becfa..c83ad94 100644 --- a/rootfs/helmbroker/config.py +++ b/rootfs/helmbroker/config.py @@ -11,6 +11,7 @@ PASSWORD = os.environ.get('HELMBROKER_PASSWORD') VALKEY_URL = os.environ.get("HELMBROKER_VALKEY_URL", 'redis://localhost:6379/0') +INSTANCE_NAME_LENS = int(os.environ.get("INSTANCE_NAME_LENS", '32')) class Config: From 449ca4c81bdac84469b5b7bc30844d46670722ca Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Sat, 13 Sep 2025 22:58:42 +0800 Subject: [PATCH 71/75] chore(helmbroker): bump new version --- rootfs/Dockerfile | 6 +++--- rootfs/Dockerfile.test | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index b6dad30..bd73c78 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.12" \ - HELM_VERSION="3.17.2" \ - KUBECTL_VERSION="1.32.3" + PYTHON_VERSION="3.13" \ + HELM_VERSION="3.19.0" \ + KUBECTL_VERSION="1.34.1" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 7596355..4c6eed0 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -4,9 +4,9 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.12" \ - HELM_VERSION="3.17.2" \ - KUBECTL_VERSION="1.32.3" + PYTHON_VERSION="3.13" \ + HELM_VERSION="3.19.0" \ + KUBECTL_VERSION="1.34.1" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} From ebe21a9ed7b0145ce519eed0ac10b7b69e6a623b Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Thu, 18 Sep 2025 14:31:16 +0800 Subject: [PATCH 72/75] chore(helmbroker): upgrade python requirements.txt --- rootfs/requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rootfs/requirements.txt b/rootfs/requirements.txt index df9e44a..7810104 100644 --- a/rootfs/requirements.txt +++ b/rootfs/requirements.txt @@ -1,7 +1,7 @@ PyYAML==6.0.2 gunicorn==23.0.0 openbrokerapi==4.7.1 -requests==2.32.2 -celery==5.4.0 -redis==5.2.0 -jsonschema==4.23.0 +requests==2.32.5 +celery==5.5.3 +redis==6.4.0 +jsonschema==4.25.1 From 233e0584a4f2571cc212a87ed892c9d3c6d9990f Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Sat, 15 Nov 2025 20:54:47 +0800 Subject: [PATCH 73/75] chore(python): bump python version to 3.14 --- rootfs/Dockerfile | 2 +- rootfs/Dockerfile.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index bd73c78..d2661c6 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -4,7 +4,7 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.13" \ + PYTHON_VERSION="3.14" \ HELM_VERSION="3.19.0" \ KUBECTL_VERSION="1.34.1" diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 4c6eed0..4aa8db9 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -4,7 +4,7 @@ FROM registry.drycc.cc/drycc/base:${CODENAME} ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ - PYTHON_VERSION="3.13" \ + PYTHON_VERSION="3.14" \ HELM_VERSION="3.19.0" \ KUBECTL_VERSION="1.34.1" From 2d338f217f0c7051936255b35eb9cd4302ac4101 Mon Sep 17 00:00:00 2001 From: duanhongyi Date: Sun, 5 Apr 2026 22:52:17 +0800 Subject: [PATCH 74/75] chore(helm): bump helm to 4.1.3 --- rootfs/Dockerfile | 4 ++-- rootfs/Dockerfile.test | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index d2661c6..85d3134 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -5,8 +5,8 @@ ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ PYTHON_VERSION="3.14" \ - HELM_VERSION="3.19.0" \ - KUBECTL_VERSION="1.34.1" + HELM_VERSION="4.1.3" \ + KUBECTL_VERSION="1.35.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} diff --git a/rootfs/Dockerfile.test b/rootfs/Dockerfile.test index 4aa8db9..fe48d53 100644 --- a/rootfs/Dockerfile.test +++ b/rootfs/Dockerfile.test @@ -5,8 +5,8 @@ ENV DRYCC_UID=1001 \ DRYCC_GID=1001 \ DRYCC_HOME_DIR=/workspace \ PYTHON_VERSION="3.14" \ - HELM_VERSION="3.19.0" \ - KUBECTL_VERSION="1.34.1" + HELM_VERSION="4.1.3" \ + KUBECTL_VERSION="1.35.3" RUN groupadd drycc --gid ${DRYCC_GID} \ && useradd drycc -u ${DRYCC_UID} -g ${DRYCC_GID} -s /bin/bash -m -d ${DRYCC_HOME_DIR} From f53626d5a47806df18fdd78f66c8e34006377fbc Mon Sep 17 00:00:00 2001 From: jianxiaoguo Date: Fri, 8 May 2026 11:49:40 +0800 Subject: [PATCH 75/75] chore(charts): config securityContext --- charts/helmbroker/templates/helmbroker-celery-deployment.yaml | 4 ++++ charts/helmbroker/templates/helmbroker-deployment.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml index b584e72..1b39b78 100644 --- a/charts/helmbroker/templates/helmbroker-celery-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-celery-deployment.yaml @@ -58,3 +58,7 @@ spec: {{- include "helmbroker.volumeMounts" $ | indent 8 }} {{- end }} {{- include "helmbroker.volumes" . | indent 6 }} + securityContext: + fsGroup: 1001 + runAsGroup: 1001 + runAsUser: 1001 diff --git a/charts/helmbroker/templates/helmbroker-deployment.yaml b/charts/helmbroker/templates/helmbroker-deployment.yaml index fe9a2ed..45cc4cb 100644 --- a/charts/helmbroker/templates/helmbroker-deployment.yaml +++ b/charts/helmbroker/templates/helmbroker-deployment.yaml @@ -78,3 +78,7 @@ spec: {{- include "helmbroker.envs" . | indent 8 }} {{- include "helmbroker.volumeMounts" . | indent 8 }} {{- include "helmbroker.volumes" . | indent 6 }} + securityContext: + fsGroup: 1001 + runAsGroup: 1001 + runAsUser: 1001