From d00009379bc8d2cd1b3b4700d0d970392c2f2878 Mon Sep 17 00:00:00 2001
From: duanhongyi <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@users.noreply.github.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <zhang.eamon@hotmail.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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 <lijianguo1991@outlook.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <duanhongyi@doopai.com>
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 <lijianguo1991@outlook.com>
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
