diff --git a/.github/workflows/check_helm.yaml b/.github/workflows/check_helm.yaml index 67fb9f428..271ff0a6e 100644 --- a/.github/workflows/check_helm.yaml +++ b/.github/workflows/check_helm.yaml @@ -32,6 +32,34 @@ jobs: - name: Local Helm Test run: | set -xe + export CHO_RELEASE=$(cat release) + IMAGES=( + "altinity/clickhouse-operator:${CHO_RELEASE}" + "altinity/metrics-exporter:${CHO_RELEASE}" + ) + need_build=false + echo "Checking images in registry…" + for img in "${IMAGES[@]}"; do + if docker manifest inspect "$img" >/dev/null 2>&1; then + echo "✔ Image exists: $img" + else + echo "✖ Image missing: $img" + need_build=true + fi + done + + if [[ "$need_build" = true ]]; then + export GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}') + + docker build -f dockerfile/operator/Dockerfile --build-arg GO_VERSION=${GO_VERSION} -t altinity/clickhouse-operator:${CHO_RELEASE} --pull . + docker build -f dockerfile/metrics-exporter/Dockerfile --build-arg GO_VERSION=${GO_VERSION} -t altinity/metrics-exporter:${CHO_RELEASE} --pull . + + docker image save altinity/clickhouse-operator:${CHO_RELEASE} -o operator.tar + docker image save altinity/metrics-exporter:${CHO_RELEASE} -o metrics-exporter.tar + + minikube image load operator.tar + minikube image load metrics-exporter.tar + fi minikube kubectl create ns test ./dev/generate_helm_chart.sh helm install -n test test-operator ./deploy/helm/clickhouse-operator/ @@ -48,6 +76,6 @@ jobs: exit 1 fi kubectl apply -n test -f ./docs/chi-examples/01-simple-layout-01-1shard-1repl.yaml - kubectl wait -n test --for=create chi/simple-01 --timeout=60s - kubectl wait -n test --for=jsonpath='{.status.status}'=Completed chi/simple-01 --timeout=1m - kubectl wait -n test --for=condition=Ready pod/chi-simple-01-simple-0-0-0 \ No newline at end of file + kubectl wait -n test --for=create chi/simple-01 --timeout=1m + kubectl wait -n test --for=jsonpath='{.status.status}'=Completed chi/simple-01 --timeout=3m + kubectl wait -n test --for=condition=Ready pod/chi-simple-01-simple-0-0-0 --timeout=3m \ No newline at end of file diff --git a/.github/workflows/release_chart.yaml b/.github/workflows/release_chart.yaml index 5a02d30f0..3f3b333af 100644 --- a/.github/workflows/release_chart.yaml +++ b/.github/workflows/release_chart.yaml @@ -1,195 +1,195 @@ -name: release_chart - -on: - release: - types: - - published - - edited - -jobs: - release_chart: - name: Release Chart - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install chart-releaser - run: | - wget https://github.com/helm/chart-releaser/releases/download/v1.4.1/chart-releaser_1.4.1_linux_amd64.tar.gz - tar -zxf chart-releaser_1.4.1_linux_amd64.tar.gz cr - sudo install cr /usr/local/bin/ - rm -f cr chart-releaser_1.4.1_linux_amd64.tar.gz - - - name: Install Helm - run: | - curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - - - name: Login to GitHub Container Registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin - - - name: Package Chart - run: cr package deploy/helm/clickhouse-operator - - - name: Get Release Assets - id: get_assets - run: | - CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) - ASSET_NAME=$(basename ${CHART_PATH}) - ASSET_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets" | \ - jq -r ".[] | select(.name == \"$ASSET_NAME\") | .id") - - echo "Asset ID is $ASSET_ID" - echo "asset_id=$ASSET_ID" >> $GITHUB_OUTPUT - - - name: Delete Existing Release Artifacts - if: steps.get_assets.outputs.asset_id != '' - run: | - curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets/${{ steps.get_assets.outputs.asset_id }}" - - - name: Upload Release Artifacts - run: | - CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) - curl -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "Content-Type: application/gzip" \ - -T "${CHART_PATH}" \ - "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${{ github.event.release.id }}/assets?name=$(basename ${CHART_PATH})" - - name: Validate Helm Repository Configuration - run: | - if [ -z "${{ secrets.HELM_GITHUB_TOKEN }}" ]; then - echo "ERROR: HELM_GITHUB_TOKEN secret is not set or is empty" - echo "Please add HELM_GITHUB_TOKEN to repository secrets with write access to the helm repository" - exit 1 - fi - - if [ -z "${{ vars.HELM_GITHUB_REPOSITORY }}" ]; then - echo "ERROR: HELM_GITHUB_REPOSITORY variable is not set or is empty" - echo "Please add HELM_GITHUB_REPOSITORY to repository variables (Settings -> Secrets and variables -> Actions -> Variables)" - exit 1 - fi - - echo "Configuration validated:" - echo " HELM_GITHUB_REPOSITORY: ${{ vars.HELM_GITHUB_REPOSITORY }}" - echo " HELM_GITHUB_TOKEN: [SET]" - - - name: Push Helm Chart to OCI Registry - run: | - CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) - helm push "${CHART_PATH}" oci://ghcr.io/altinity/clickhouse-operator-helm-chart - - - name: Upload Release Artifacts to Helm Repo - run: | - cr upload \ - --git-repo=${{ vars.HELM_GITHUB_REPOSITORY }} \ - --owner=${GITHUB_REPOSITORY_OWNER} \ - --release-name-template=${{ github.event.release.name }} \ - --token=${{ secrets.HELM_GITHUB_TOKEN }} \ - --package-path=.cr-release-packages \ - --skip-existing - - name: Configure Git - run: | - git config user.name "$GITHUB_ACTOR" - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Release Chart to Operator Repo - run: | - git remote add httpsorigin "https://github.com/${GITHUB_REPOSITORY}.git" - git fetch httpsorigin - cr index \ - --git-repo=${GITHUB_REPOSITORY#*/} \ - --owner=${GITHUB_REPOSITORY_OWNER} \ - --release-name-template=${{ github.event.release.name }} \ - --token=${{ secrets.GITHUB_TOKEN }} \ - --index-path=index.yaml \ - --remote=httpsorigin \ - --push - - name: Release Chart to Helm Repo - run: | - # Validate configuration before attempting to push - if [ -z "${{ vars.HELM_GITHUB_REPOSITORY }}" ]; then - echo "ERROR: HELM_GITHUB_REPOSITORY variable is not set or is empty" - echo "This step requires HELM_GITHUB_REPOSITORY to be set in repository variables" - echo "Go to: Settings -> Secrets and variables -> Actions -> Variables" - exit 1 - fi - - if [ -z "${{ secrets.HELM_GITHUB_TOKEN }}" ]; then - echo "ERROR: HELM_GITHUB_TOKEN secret is not set or is empty" - echo "This step requires HELM_GITHUB_TOKEN with write access to: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" - echo "Go to: Settings -> Secrets and variables -> Actions -> Secrets" - exit 1 - fi - - echo "Attempting to push to helm repository: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" - - # Test token authentication - echo "Testing token authentication..." - TOKEN_USER=$(curl -sS -H "Authorization: token ${{ secrets.HELM_GITHUB_TOKEN }}" https://api.github.com/user | jq -r '.login') - echo "Token authenticated as user: ${TOKEN_USER}" - - # Save current directory - WORK_DIR=$(pwd) - - # Create a temporary directory for helm repo operations - TEMP_DIR=$(mktemp -d) - cd "$TEMP_DIR" - - # Clone the helm repository WITHOUT token in URL to avoid masking issues - echo "Cloning helm repository to temporary directory..." - git clone https://github.com/${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}.git helm-repo || { - echo "ERROR: Failed to clone helm repository" - echo "Please verify:" - echo " 1. Repository exists: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" - exit 1 - } - - cd helm-repo - - # Configure git credentials for push - git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - git config user.name "$GITHUB_ACTOR" - - # Set up authentication using git credential helper - git config credential.helper "store --file=.git/credentials" - echo "https://x-access-token:${{ secrets.HELM_GITHUB_TOKEN }}@github.com" > .git/credentials - - # Now use cr index from within the helm repo to avoid history conflicts - echo "Generating index.yaml within helm repository context..." - - # Copy the package to a local directory within helm repo - mkdir -p .cr-release-packages - cp "$WORK_DIR"/.cr-release-packages/*.tgz .cr-release-packages/ || { - echo "ERROR: No chart packages found in .cr-release-packages" - exit 1 - } - - # Generate index with cr (this will handle the gh-pages branch automatically) - cr index \ - --git-repo=${{ vars.HELM_GITHUB_REPOSITORY }} \ - --owner=${GITHUB_REPOSITORY_OWNER} \ - --release-name-template=${{ github.event.release.name }} \ - --token=${{ secrets.HELM_GITHUB_TOKEN }} \ - --package-path=.cr-release-packages \ - --index-path=index.yaml \ - --push || { - echo "ERROR: Failed to generate or push index to helm repository" - echo "Debug: Current directory is $(pwd)" - echo "Debug: Git remotes:" - git remote -v - echo "Debug: Git status:" - git status - exit 1 - } - - echo "Successfully updated helm repository index" - - # Cleanup - cd / - rm -rf "$TEMP_DIR" +name: release_chart + +on: + release: + types: + - published + - edited + +jobs: + release_chart: + name: Release Chart + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install chart-releaser + run: | + wget https://github.com/helm/chart-releaser/releases/download/v1.4.1/chart-releaser_1.4.1_linux_amd64.tar.gz + tar -zxf chart-releaser_1.4.1_linux_amd64.tar.gz cr + sudo install cr /usr/local/bin/ + rm -f cr chart-releaser_1.4.1_linux_amd64.tar.gz + + - name: Install Helm + run: | + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + + - name: Login to GitHub Container Registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Package Chart + run: cr package deploy/helm/clickhouse-operator + + - name: Get Release Assets + id: get_assets + run: | + CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) + ASSET_NAME=$(basename ${CHART_PATH}) + ASSET_ID=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets" | \ + jq -r ".[] | select(.name == \"$ASSET_NAME\") | .id") + + echo "Asset ID is $ASSET_ID" + echo "asset_id=$ASSET_ID" >> $GITHUB_OUTPUT + + - name: Delete Existing Release Artifacts + if: steps.get_assets.outputs.asset_id != '' + run: | + curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + "https://api.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets/${{ steps.get_assets.outputs.asset_id }}" + + - name: Upload Release Artifacts + run: | + CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) + curl -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/gzip" \ + -T "${CHART_PATH}" \ + "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${{ github.event.release.id }}/assets?name=$(basename ${CHART_PATH})" + - name: Validate Helm Repository Configuration + run: | + if [ -z "${{ secrets.HELM_GITHUB_TOKEN }}" ]; then + echo "ERROR: HELM_GITHUB_TOKEN secret is not set or is empty" + echo "Please add HELM_GITHUB_TOKEN to repository secrets with write access to the helm repository" + exit 1 + fi + + if [ -z "${{ vars.HELM_GITHUB_REPOSITORY }}" ]; then + echo "ERROR: HELM_GITHUB_REPOSITORY variable is not set or is empty" + echo "Please add HELM_GITHUB_REPOSITORY to repository variables (Settings -> Secrets and variables -> Actions -> Variables)" + exit 1 + fi + + echo "Configuration validated:" + echo " HELM_GITHUB_REPOSITORY: ${{ vars.HELM_GITHUB_REPOSITORY }}" + echo " HELM_GITHUB_TOKEN: [SET]" + + - name: Push Helm Chart to OCI Registry + run: | + CHART_PATH=$(ls .cr-release-packages/altinity-clickhouse-operator-*.tgz) + helm push "${CHART_PATH}" oci://ghcr.io/altinity/clickhouse-operator-helm-chart + + - name: Upload Release Artifacts to Helm Repo + run: | + cr upload \ + --git-repo=${{ vars.HELM_GITHUB_REPOSITORY }} \ + --owner=${GITHUB_REPOSITORY_OWNER} \ + --release-name-template=${{ github.event.release.name }} \ + --token=${{ secrets.HELM_GITHUB_TOKEN }} \ + --package-path=.cr-release-packages \ + --skip-existing + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + - name: Release Chart to Operator Repo + run: | + git remote add httpsorigin "https://github.com/${GITHUB_REPOSITORY}.git" + git fetch httpsorigin + cr index \ + --git-repo=${GITHUB_REPOSITORY#*/} \ + --owner=${GITHUB_REPOSITORY_OWNER} \ + --release-name-template=${{ github.event.release.name }} \ + --token=${{ secrets.GITHUB_TOKEN }} \ + --index-path=index.yaml \ + --remote=httpsorigin \ + --push + - name: Release Chart to Helm Repo + run: | + # Validate configuration before attempting to push + if [ -z "${{ vars.HELM_GITHUB_REPOSITORY }}" ]; then + echo "ERROR: HELM_GITHUB_REPOSITORY variable is not set or is empty" + echo "This step requires HELM_GITHUB_REPOSITORY to be set in repository variables" + echo "Go to: Settings -> Secrets and variables -> Actions -> Variables" + exit 1 + fi + + if [ -z "${{ secrets.HELM_GITHUB_TOKEN }}" ]; then + echo "ERROR: HELM_GITHUB_TOKEN secret is not set or is empty" + echo "This step requires HELM_GITHUB_TOKEN with write access to: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" + echo "Go to: Settings -> Secrets and variables -> Actions -> Secrets" + exit 1 + fi + + echo "Attempting to push to helm repository: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" + + # Test token authentication + echo "Testing token authentication..." + TOKEN_USER=$(curl -sS -H "Authorization: token ${{ secrets.HELM_GITHUB_TOKEN }}" https://api.github.com/user | jq -r '.login') + echo "Token authenticated as user: ${TOKEN_USER}" + + # Save current directory + WORK_DIR=$(pwd) + + # Create a temporary directory for helm repo operations + TEMP_DIR=$(mktemp -d) + cd "$TEMP_DIR" + + # Clone the helm repository WITHOUT token in URL to avoid masking issues + echo "Cloning helm repository to temporary directory..." + git clone https://github.com/${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}.git helm-repo || { + echo "ERROR: Failed to clone helm repository" + echo "Please verify:" + echo " 1. Repository exists: ${GITHUB_REPOSITORY_OWNER}/${{ vars.HELM_GITHUB_REPOSITORY }}" + exit 1 + } + + cd helm-repo + + # Configure git credentials for push + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + git config user.name "$GITHUB_ACTOR" + + # Set up authentication using git credential helper + git config credential.helper "store --file=.git/credentials" + echo "https://x-access-token:${{ secrets.HELM_GITHUB_TOKEN }}@github.com" > .git/credentials + + # Now use cr index from within the helm repo to avoid history conflicts + echo "Generating index.yaml within helm repository context..." + + # Copy the package to a local directory within helm repo + mkdir -p .cr-release-packages + cp "$WORK_DIR"/.cr-release-packages/*.tgz .cr-release-packages/ || { + echo "ERROR: No chart packages found in .cr-release-packages" + exit 1 + } + + # Generate index with cr (this will handle the gh-pages branch automatically) + cr index \ + --git-repo=${{ vars.HELM_GITHUB_REPOSITORY }} \ + --owner=${GITHUB_REPOSITORY_OWNER} \ + --release-name-template=${{ github.event.release.name }} \ + --token=${{ secrets.HELM_GITHUB_TOKEN }} \ + --package-path=.cr-release-packages \ + --index-path=index.yaml \ + --push || { + echo "ERROR: Failed to generate or push index to helm repository" + echo "Debug: Current directory is $(pwd)" + echo "Debug: Git remotes:" + git remote -v + echo "Debug: Git status:" + git status + exit 1 + } + + echo "Successfully updated helm repository index" + + # Cleanup + cd / + rm -rf "$TEMP_DIR" diff --git a/deploy/helm/clickhouse-operator/Chart.yaml b/deploy/helm/clickhouse-operator/Chart.yaml index 82647ddfb..d5d319523 100644 --- a/deploy/helm/clickhouse-operator/Chart.yaml +++ b/deploy/helm/clickhouse-operator/Chart.yaml @@ -5,7 +5,11 @@ description: |- The ClickHouse Operator creates, configures and manages ClickHouse clusters running on Kubernetes. - For upgrade please install CRDs separately: + ## CRD Management + + CRDs are automatically installed and updated during `helm install` and `helm upgrade` using pre-install/pre-upgrade hooks (enabled by default). + + To disable automatic CRD updates, set `crdHook.enabled: false` in values.yaml. When disabled, CRDs must be installed manually: ```bash kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallations.clickhouse.altinity.com.yaml kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallationtemplates.clickhouse.altinity.com.yaml diff --git a/deploy/helm/clickhouse-operator/README.md b/deploy/helm/clickhouse-operator/README.md index b9d0815ed..7f912b16f 100644 --- a/deploy/helm/clickhouse-operator/README.md +++ b/deploy/helm/clickhouse-operator/README.md @@ -6,7 +6,11 @@ Helm chart to deploy [altinity-clickhouse-operator](https://github.com/Altinity/ The ClickHouse Operator creates, configures and manages ClickHouse clusters running on Kubernetes. -For upgrade please install CRDs separately: +## CRD Management + +CRDs are automatically installed and updated during `helm install` and `helm upgrade` using pre-install/pre-upgrade hooks (enabled by default). + +To disable automatic CRD updates, set `crdHook.enabled: false` in values.yaml. When disabled, CRDs must be installed manually: ```bash kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallations.clickhouse.altinity.com.yaml kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallationtemplates.clickhouse.altinity.com.yaml @@ -22,6 +26,45 @@ For upgrade please install CRDs separately: | ---- | ------ | --- | | altinity | | | +## CRD Management + +This chart includes automatic CRD installation and update functionality using Helm hooks. CRDs are automatically applied during `helm install` and `helm upgrade` operations. + +### How It Works + +- **Automatic Updates**: CRDs are installed/updated via pre-install and pre-upgrade hooks (enabled by default) +- **Backward Compatible**: CRDs remain in the `crds/` directory for standard Helm 3 behavior +- **Server-Side Apply**: Uses kubectl with `--server-side` flag for better conflict resolution +- **Configurable**: Can be disabled via `crdHook.enabled: false` in values.yaml + +### Manual CRD Management + +If you prefer to manage CRDs manually or have disabled the automatic hooks, you can apply CRDs using kubectl: + +```bash +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallations.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallationtemplates.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseoperatorconfigurations.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhousekeeperinstallations.clickhouse-keeper.altinity.com.yaml +``` + +### Troubleshooting + +**Hook Job Fails with Permission Denied** +- Ensure the cluster has RBAC enabled and the ServiceAccount has proper permissions to manage CRDs +- The hook requires cluster-level permissions for `apiextensions.k8s.io/customresourcedefinitions` + +**CRDs Not Updating** +- Check if `crdHook.enabled` is set to `true` in your values +- Verify the hook Job ran successfully: `kubectl get jobs -n | grep crd-install` +- Check Job logs: `kubectl logs -n job/-crd-install` + +**Disabling Automatic CRD Updates** +```yaml +crdHook: + enabled: false +``` + ## Values | Key | Type | Default | Description | @@ -31,6 +74,14 @@ For upgrade please install CRDs separately: | commonAnnotations | object | `{}` | set of annotations that will be applied to all the resources for the operator | | commonLabels | object | `{}` | set of labels that will be applied to all the resources for the operator | | configs | object | check the `values.yaml` file for the config content (auto-generated from latest operator release) | clickhouse operator configs | +| crdHook.affinity | object | `{}` | affinity for CRD installation job | +| crdHook.enabled | bool | `true` | enable automatic CRD installation/update via pre-install/pre-upgrade hooks when disabled, CRDs must be installed manually using kubectl apply | +| crdHook.image.pullPolicy | string | `"IfNotPresent"` | image pull policy for CRD installation job | +| crdHook.image.repository | string | `"bitnami/kubectl"` | image repository for CRD installation job | +| crdHook.image.tag | string | `"latest"` | image tag for CRD installation job | +| crdHook.nodeSelector | object | `{}` | node selector for CRD installation job | +| crdHook.resources | object | `{}` | resource limits and requests for CRD installation job | +| crdHook.tolerations | list | `[]` | tolerations for CRD installation job | | dashboards.additionalLabels | object | `{"grafana_dashboard":""}` | labels to add to a secret with dashboards | | dashboards.annotations | object | `{}` | annotations to add to a secret with dashboards | | dashboards.enabled | bool | `false` | provision grafana dashboards as configMaps (can be synced by grafana dashboards sidecar https://github.com/grafana/helm-charts/blob/grafana-8.3.4/charts/grafana/values.yaml#L778 ) | diff --git a/deploy/helm/clickhouse-operator/README.md.gotmpl b/deploy/helm/clickhouse-operator/README.md.gotmpl index 8dff37845..a7658d97e 100644 --- a/deploy/helm/clickhouse-operator/README.md.gotmpl +++ b/deploy/helm/clickhouse-operator/README.md.gotmpl @@ -13,5 +13,44 @@ {{ template "chart.requirementsSection" . }} +## CRD Management + +This chart includes automatic CRD installation and update functionality using Helm hooks. CRDs are automatically applied during `helm install` and `helm upgrade` operations. + +### How It Works + +- **Automatic Updates**: CRDs are installed/updated via pre-install and pre-upgrade hooks (enabled by default) +- **Backward Compatible**: CRDs remain in the `crds/` directory for standard Helm 3 behavior +- **Server-Side Apply**: Uses kubectl with `--server-side` flag for better conflict resolution +- **Configurable**: Can be disabled via `crdHook.enabled: false` in values.yaml + +### Manual CRD Management + +If you prefer to manage CRDs manually or have disabled the automatic hooks, you can apply CRDs using kubectl: + +```bash +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallations.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseinstallationtemplates.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhouseoperatorconfigurations.clickhouse.altinity.com.yaml +kubectl apply -f https://github.com/Altinity/clickhouse-operator/raw/master/deploy/helm/clickhouse-operator/crds/CustomResourceDefinition-clickhousekeeperinstallations.clickhouse-keeper.altinity.com.yaml +``` + +### Troubleshooting + +**Hook Job Fails with Permission Denied** +- Ensure the cluster has RBAC enabled and the ServiceAccount has proper permissions to manage CRDs +- The hook requires cluster-level permissions for `apiextensions.k8s.io/customresourcedefinitions` + +**CRDs Not Updating** +- Check if `crdHook.enabled` is set to `true` in your values +- Verify the hook Job ran successfully: `kubectl get jobs -n | grep crd-install` +- Check Job logs: `kubectl logs -n job/-crd-install` + +**Disabling Automatic CRD Updates** +```yaml +crdHook: + enabled: false +``` + {{ template "chart.valuesSection" . }} diff --git a/deploy/helm/clickhouse-operator/templates/generated/ClusterRoleBinding-clickhouse-operator-kube-system.yaml b/deploy/helm/clickhouse-operator/templates/generated/ClusterRoleBinding-clickhouse-operator-kube-system.yaml index 1f696a543..6b6131dbb 100644 --- a/deploy/helm/clickhouse-operator/templates/generated/ClusterRoleBinding-clickhouse-operator-kube-system.yaml +++ b/deploy/helm/clickhouse-operator/templates/generated/ClusterRoleBinding-clickhouse-operator-kube-system.yaml @@ -20,7 +20,6 @@ subjects: - kind: ServiceAccount name: {{ include "altinity-clickhouse-operator.serviceAccountName" . }} namespace: {{ include "altinity-clickhouse-operator.namespace" . }} - # Template Parameters: # # NAMESPACE=kube-system diff --git a/deploy/helm/clickhouse-operator/templates/generated/ServiceAccount-clickhouse-operator.yaml b/deploy/helm/clickhouse-operator/templates/generated/ServiceAccount-clickhouse-operator.yaml index bcc55c20d..fd06949a6 100644 --- a/deploy/helm/clickhouse-operator/templates/generated/ServiceAccount-clickhouse-operator.yaml +++ b/deploy/helm/clickhouse-operator/templates/generated/ServiceAccount-clickhouse-operator.yaml @@ -13,7 +13,6 @@ metadata: namespace: {{ include "altinity-clickhouse-operator.namespace" . }} labels: {{ include "altinity-clickhouse-operator.labels" . | nindent 4 }} annotations: {{ include "altinity-clickhouse-operator.annotations" . | nindent 4 }}{{ if .Values.serviceAccount.annotations }}{{ toYaml .Values.serviceAccount.annotations | nindent 4 }}{{ end }} - # Template Parameters: # # NAMESPACE=kube-system diff --git a/deploy/helm/clickhouse-operator/templates/hooks/crd-install-configmap.yaml b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-configmap.yaml new file mode 100644 index 000000000..1946b6e51 --- /dev/null +++ b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-configmap.yaml @@ -0,0 +1,23 @@ +{{- if .Values.crdHook.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crds + namespace: {{ include "altinity-clickhouse-operator.namespace" . }} + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 4 }} + app.kubernetes.io/component: crd-install-hook + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-7" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +data: + clickhouseinstallations.yaml: | +{{ .Files.Get "crds/CustomResourceDefinition-clickhouseinstallations.clickhouse.altinity.com.yaml" | indent 4 }} + clickhouseinstallationtemplates.yaml: | +{{ .Files.Get "crds/CustomResourceDefinition-clickhouseinstallationtemplates.clickhouse.altinity.com.yaml" | indent 4 }} + clickhousekeeperinstallations.yaml: | +{{ .Files.Get "crds/CustomResourceDefinition-clickhousekeeperinstallations.clickhouse-keeper.altinity.com.yaml" | indent 4 }} + clickhouseoperatorconfigurations.yaml: | +{{ .Files.Get "crds/CustomResourceDefinition-clickhouseoperatorconfigurations.clickhouse.altinity.com.yaml" | indent 4 }} +{{- end }} diff --git a/deploy/helm/clickhouse-operator/templates/hooks/crd-install-job.yaml b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-job.yaml new file mode 100644 index 000000000..9b9b4e005 --- /dev/null +++ b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-job.yaml @@ -0,0 +1,63 @@ +{{- if .Values.crdHook.enabled }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + namespace: {{ include "altinity-clickhouse-operator.namespace" . }} + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 4 }} + app.kubernetes.io/component: crd-install-hook + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +spec: + template: + metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 8 }} + app.kubernetes.io/component: crd-install-hook + spec: + serviceAccountName: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + restartPolicy: OnFailure + {{- with .Values.crdHook.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.crdHook.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.crdHook.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: crd-install + image: "{{ .Values.crdHook.image.repository }}:{{ .Values.crdHook.image.tag }}" + imagePullPolicy: {{ .Values.crdHook.image.pullPolicy | default "IfNotPresent" }} + command: + - /bin/sh + - -c + - | + set -e + echo "Installing/Updating ClickHouse Operator CRDs..." + for crd_file in /crds/*.yaml; do + echo "Applying $(basename $crd_file)..." + kubectl apply --server-side=true --force-conflicts -f "$crd_file" + done + echo "CRD installation completed successfully" + volumeMounts: + - name: crds + mountPath: /crds + readOnly: true + {{- with .Values.crdHook.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + volumes: + - name: crds + configMap: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crds +{{- end }} diff --git a/deploy/helm/clickhouse-operator/templates/hooks/crd-install-rbac.yaml b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-rbac.yaml new file mode 100644 index 000000000..bc776c990 --- /dev/null +++ b/deploy/helm/clickhouse-operator/templates/hooks/crd-install-rbac.yaml @@ -0,0 +1,58 @@ +{{- if .Values.crdHook.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + namespace: {{ include "altinity-clickhouse-operator.namespace" . }} + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 4 }} + app.kubernetes.io/component: crd-install-hook + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-6" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 4 }} + app.kubernetes.io/component: crd-install-hook + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-6" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +rules: +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - create + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + labels: + {{- include "altinity-clickhouse-operator.labels" . | nindent 4 }} + app.kubernetes.io/component: crd-install-hook + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "-6" + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install +subjects: +- kind: ServiceAccount + name: {{ include "altinity-clickhouse-operator.fullname" . }}-crd-install + namespace: {{ include "altinity-clickhouse-operator.namespace" . }} +{{- end }} diff --git a/deploy/helm/clickhouse-operator/values.schema.json b/deploy/helm/clickhouse-operator/values.schema.json index 598c3636b..383ebdb71 100644 --- a/deploy/helm/clickhouse-operator/values.schema.json +++ b/deploy/helm/clickhouse-operator/values.schema.json @@ -1,895 +1,895 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "additionalResources": { - "description": "list of additional resources to create (processed via `tpl` function), useful for create ClickHouse clusters together with clickhouse-operator. check `kubectl explain chi` for details", - "type": "array" - }, - "affinity": { - "description": "affinity for scheduler pod assignment, check `kubectl explain pod.spec.affinity` for details", - "type": "object" - }, - "commonAnnotations": { - "description": "set of annotations that will be applied to all the resources for the operator", - "type": "object" - }, - "commonLabels": { - "description": "set of labels that will be applied to all the resources for the operator", - "type": "object" - }, - "configs": { - "description": "clickhouse operator configs", - "type": "object", - "properties": { - "confdFiles": { - "type": ["string", "null"] - }, - "configdFiles": { - "type": "object", - "properties": { - "01-clickhouse-01-listen.xml": { - "type": "string" - }, - "01-clickhouse-02-logger.xml": { - "type": "string" - }, - "01-clickhouse-03-query_log.xml": { - "type": "string" - }, - "01-clickhouse-04-part_log.xml": { - "type": "string" - }, - "01-clickhouse-05-trace_log.xml": { - "type": "string" - } - } - }, - "files": { - "type": "object", - "properties": { - "config.yaml": { - "type": "object", - "properties": { - "annotation": { - "type": "object", - "properties": { - "exclude": { - "type": "array" - }, - "include": { - "type": "array" - } - } - }, - "clickhouse": { - "type": "object", - "properties": { - "access": { - "type": "object", - "properties": { - "password": { - "type": "string" - }, - "port": { - "type": "integer" - }, - "rootCA": { - "type": "string" - }, - "scheme": { - "type": "string" - }, - "secret": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - } - } - }, - "timeouts": { - "type": "object", - "properties": { - "connect": { - "type": "integer" - }, - "query": { - "type": "integer" - } - } - }, - "username": { - "type": "string" - } - } - }, - "addons": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "spec": { - "type": "object", - "properties": { - "configuration": { - "type": "object", - "properties": { - "files": { - "type": ["object", "null"] - }, - "profiles": { - "type": ["object", "null"], - "properties": { - "clickhouse_operator/format_display_secrets_in_show_and_select": { - "type": "integer" - } - } - }, - "quotas": { - "type": ["object", "null"] - }, - "settings": { - "type": ["object", "null"], - "properties": { - "display_secrets_in_show_and_select": { - "type": "integer" - } - } - }, - "users": { - "type": ["object", "null"], - "properties": { - "{clickhouseOperatorUser}/access_management": { - "type": "integer" - }, - "{clickhouseOperatorUser}/named_collection_control": { - "type": "integer" - }, - "{clickhouseOperatorUser}/show_named_collections": { - "type": "integer" - }, - "{clickhouseOperatorUser}/show_named_collections_secrets": { - "type": "integer" - } - } - } - } - } - } - }, - "version": { - "type": "string" - } - } - } - } - } - }, - "configuration": { - "type": "object", - "properties": { - "file": { - "type": "object", - "properties": { - "path": { - "type": "object", - "properties": { - "common": { - "type": "string" - }, - "host": { - "type": "string" - }, - "user": { - "type": "string" - } - } - } - } - }, - "network": { - "type": "object", - "properties": { - "hostRegexpTemplate": { - "type": "string" - } - } - }, - "user": { - "type": "object", - "properties": { - "default": { - "type": "object", - "properties": { - "networksIP": { - "type": "array", - "items": { - "type": "string" - } - }, - "password": { - "type": "string" - }, - "profile": { - "type": "string" - }, - "quota": { - "type": "string" - } - } - } - } - } - } - }, - "configurationRestartPolicy": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "rules": { - "type": "array", - "items": { - "type": "object", - "properties": { - "files/*.xml": { - "type": "string" - }, - "files/config.d/*.xml": { - "type": "string" - }, - "files/config.d/*dict*.xml": { - "type": "string" - }, - "files/config.d/*no_restart*": { - "type": "string" - }, - "profiles/default/background_*_pool_size": { - "type": "string" - }, - "profiles/default/max_*_for_server": { - "type": "string" - }, - "settings/*": { - "type": "string" - }, - "settings/access_control_path": { - "type": "string" - }, - "settings/dictionaries_config": { - "type": "string" - }, - "settings/display_secrets_in_show_and_select": { - "type": "string" - }, - "settings/logger": { - "type": "string" - }, - "settings/logger/*": { - "type": "string" - }, - "settings/macros/*": { - "type": "string" - }, - "settings/max_*_to_drop": { - "type": "string" - }, - "settings/max_concurrent_queries": { - "type": "string" - }, - "settings/max_server_memory_*": { - "type": "string" - }, - "settings/models_config": { - "type": "string" - }, - "settings/remote_servers/*": { - "type": "string" - }, - "settings/user_defined_executable_functions_config": { - "type": "string" - }, - "settings/user_directories/*": { - "type": "string" - }, - "zookeeper/*": { - "type": "string" - } - } - } - }, - "version": { - "type": "string" - } - } - } - } - } - }, - "metrics": { - "type": "object", - "properties": { - "timeouts": { - "type": "object", - "properties": { - "collect": { - "type": "integer" - } - } - } - } - } - } - }, - "keeper": { - "type": "object", - "properties": { - "configuration": { - "type": "object", - "properties": { - "file": { - "type": "object", - "properties": { - "path": { - "type": "object", - "properties": { - "common": { - "type": "string" - }, - "host": { - "type": "string" - }, - "user": { - "type": "string" - } - } - } - } - } - } - } - } - }, - "label": { - "type": "object", - "properties": { - "appendScope": { - "type": "string" - }, - "exclude": { - "type": "array" - }, - "include": { - "type": "array" - } - } - }, - "logger": { - "type": "object", - "properties": { - "alsologtostderr": { - "type": "string" - }, - "log_backtrace_at": { - "type": "string" - }, - "logtostderr": { - "type": "string" - }, - "stderrthreshold": { - "type": "string" - }, - "v": { - "type": "string" - }, - "vmodule": { - "type": "string" - } - } - }, - "metrics": { - "type": "object", - "properties": { - "labels": { - "type": "object", - "properties": { - "exclude": { - "type": "array" - } - } - } - } - }, - "pod": { - "type": "object", - "properties": { - "terminationGracePeriod": { - "type": "integer" - } - } - }, - "reconcile": { - "type": "object", - "properties": { - "host": { - "type": "object", - "properties": { - "wait": { - "type": "object", - "properties": { - "exclude": { - "type": "boolean" - }, - "include": { - "type": "boolean" - }, - "probes": { - "type": "object", - "properties": { - "readiness": { - "type": "boolean" - }, - "startup": { - "type": "boolean" - } - } - }, - "queries": { - "type": "boolean" - }, - "replicas": { - "type": "object", - "properties": { - "all": { - "type": "boolean" - }, - "delay": { - "type": "integer" - }, - "new": { - "type": "boolean" - } - } - } - } - } - } - }, - "runtime": { - "type": "object", - "properties": { - "reconcileCHIsThreadsNumber": { - "type": "integer" - }, - "reconcileShardsMaxConcurrencyPercent": { - "type": "integer" - }, - "reconcileShardsThreadsNumber": { - "type": "integer" - } - } - }, - "statefulSet": { - "type": "object", - "properties": { - "create": { - "type": "object", - "properties": { - "onFailure": { - "type": "string" - } - } - }, - "update": { - "type": "object", - "properties": { - "onFailure": { - "type": "string" - }, - "pollInterval": { - "type": "integer" - }, - "timeout": { - "type": "integer" - } - } - } - } - } - } - }, - "statefulSet": { - "type": "object", - "properties": { - "revisionHistoryLimit": { - "type": "integer" - } - } - }, - "status": { - "type": "object", - "properties": { - "fields": { - "type": "object", - "properties": { - "action": { - "type": "boolean" - }, - "actions": { - "type": "boolean" - }, - "error": { - "type": "boolean" - }, - "errors": { - "type": "boolean" - } - } - } - } - }, - "template": { - "type": "object", - "properties": { - "chi": { - "type": "object", - "properties": { - "path": { - "type": "string" - }, - "policy": { - "type": "string" - } - } - }, - "chk": { - "type": "object", - "properties": { - "path": { - "type": "string" - }, - "policy": { - "type": "string" - } - } - } - } - }, - "watch": { - "type": "object", - "properties": { - "namespaces": { - "type": "array" - } - } - } - } - } - } - }, - "keeperConfdFiles": { - "type": ["string", "null"] - }, - "keeperConfigdFiles": { - "type": "object", - "properties": { - "01-keeper-01-default-config.xml": { - "type": "string" - }, - "01-keeper-02-readiness.xml": { - "type": "string" - }, - "01-keeper-03-enable-reconfig.xml": { - "type": "string" - } - } - }, - "keeperTemplatesdFiles": { - "type": ["object", "null"], - "properties": { - "readme": { - "type": ["string", "null"] - } - } - }, - "keeperUsersdFiles": { - "type": ["string", "null"] - }, - "templatesdFiles": { - "type": ["object", "null"], - "properties": { - "001-templates.json.example": { - "type": ["string", "null"] - }, - "default-pod-template.yaml.example": { - "type": ["string", "null"] - }, - "default-storage-template.yaml.example": { - "type": ["string", "null"] - }, - "readme": { - "type": ["string", "null"] - } - } - }, - "usersdFiles": { - "type": ["object", "null"], - "properties": { - "01-clickhouse-operator-profile.xml": { - "type": ["string", "null"] - }, - "02-clickhouse-default-profile.xml": { - "type": ["string", "null"] - } - } - } - } - }, - "dashboards": { - "type": "object", - "properties": { - "additionalLabels": { - "description": "labels to add to a secret with dashboards", - "type": "object", - "properties": { - "grafana_dashboard": { - "type": "string" - } - } - }, - "annotations": { - "description": "annotations to add to a secret with dashboards", - "type": "object" - }, - "enabled": { - "description": "provision grafana dashboards as configMaps (can be synced by grafana dashboards sidecar https://github.com/grafana/helm-charts/blob/grafana-8.3.4/charts/grafana/values.yaml#L778 )", - "type": "boolean" - }, - "grafana_folder": { - "type": "string" - } - } - }, - "deployment": { - "type": "object", - "properties": { - "strategy": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } - } - } - }, - "fullnameOverride": { - "description": "full name of the chart.", - "type": "string" - }, - "imagePullSecrets": { - "description": "image pull secret for private images in clickhouse-operator pod possible value format `[{\"name\":\"your-secret-name\"}]`, check `kubectl explain pod.spec.imagePullSecrets` for details", - "type": "array" - }, - "metrics": { - "type": "object", - "properties": { - "containerSecurityContext": { - "type": "object" - }, - "enabled": { - "type": "boolean" - }, - "env": { - "description": "additional environment variables for the deployment of metrics-exporter containers possible format value `[{\"name\": \"SAMPLE\", \"value\": \"text\"}]`", - "type": "array" - }, - "image": { - "type": "object", - "properties": { - "pullPolicy": { - "description": "image pull policy", - "type": "string" - }, - "repository": { - "description": "image repository", - "type": "string" - }, - "tag": { - "description": "image tag (chart's appVersion value will be used if not set)", - "type": "string" - } - } - }, - "resources": { - "description": "custom resource configuration", - "type": "object" - } - } - }, - "nameOverride": { - "description": "override name of the chart", - "type": "string" - }, - "namespaceOverride": { - "type": "string" - }, - "nodeSelector": { - "description": "node for scheduler pod assignment, check `kubectl explain pod.spec.nodeSelector` for details", - "type": "object" - }, - "operator": { - "type": "object", - "properties": { - "containerSecurityContext": { - "type": "object" - }, - "env": { - "description": "additional environment variables for the clickhouse-operator container in deployment possible format value `[{\"name\": \"SAMPLE\", \"value\": \"text\"}]`", - "type": "array" - }, - "image": { - "type": "object", - "properties": { - "pullPolicy": { - "description": "image pull policy", - "type": "string" - }, - "repository": { - "description": "image repository", - "type": "string" - }, - "tag": { - "description": "image tag (chart's appVersion value will be used if not set)", - "type": "string" - } - } - }, - "priorityClassName": { - "description": "priority class name for the clickhouse-operator deployment, check `kubectl explain pod.spec.priorityClassName` for details", - "type": "string" - }, - "resources": { - "description": "custom resource configuration, check `kubectl explain pod.spec.containers.resources` for details", - "type": "object" - } - } - }, - "podAnnotations": { - "description": "annotations to add to the clickhouse-operator pod, check `kubectl explain pod.spec.annotations` for details", - "type": "object", - "properties": { - "clickhouse-operator-metrics/port": { - "type": "string" - }, - "clickhouse-operator-metrics/scrape": { - "type": "string" - }, - "prometheus.io/port": { - "type": "string" - }, - "prometheus.io/scrape": { - "type": "string" - } - } - }, - "podLabels": { - "description": "labels to add to the clickhouse-operator pod", - "type": "object" - }, - "podSecurityContext": { - "type": "object" - }, - "rbac": { - "type": "object", - "properties": { - "create": { - "description": "specifies whether rbac resources should be created", - "type": "boolean" - }, - "namespaceScoped": { - "description": "specifies whether to create roles and rolebindings at the cluster level or namespace level", - "type": "boolean" - } - } - }, - "secret": { - "type": "object", - "properties": { - "create": { - "description": "create a secret with operator credentials", - "type": "boolean" - }, - "password": { - "description": "operator credentials password", - "type": "string" - }, - "username": { - "description": "operator credentials username", - "type": "string" - } - } - }, - "serviceAccount": { - "type": ["object", "null"], - "properties": { - "annotations": { - "description": "annotations to add to the service account", - "type": "object" - }, - "create": { - "description": "specifies whether a service account should be created", - "type": "boolean" - }, - "name": { - "description": "the name of the service account to use; if not set and create is true, a name is generated using the fullname template", - "type": ["string","null"] - } - } - }, - "serviceMonitor": { - "type": "object", - "properties": { - "additionalLabels": { - "description": "additional labels for service monitor", - "type": "object" - }, - "clickhouseMetrics": { - "type": "object", - "properties": { - "interval": { - "type": "string" - }, - "metricRelabelings": { - "type": "array" - }, - "relabelings": { - "type": "array" - }, - "scrapeTimeout": { - "type": "string" - } - } - }, - "enabled": { - "description": "ServiceMonitor Custom resource is created for a [prometheus-operator](https://github.com/prometheus-operator/prometheus-operator) In serviceMonitor will be created two endpoints ch-metrics on port 8888 and op-metrics # 9999. Ypu can specify interval, scrapeTimeout, relabelings, metricRelabelings for each endpoint below", - "type": "boolean" - }, - "operatorMetrics": { - "type": "object", - "properties": { - "interval": { - "type": "string" - }, - "metricRelabelings": { - "type": "array" - }, - "relabelings": { - "type": "array" - }, - "scrapeTimeout": { - "type": "string" - } - } - } - } - }, - "tolerations": { - "description": "tolerations for scheduler pod assignment, check `kubectl explain pod.spec.tolerations` for details", - "type": "array" - }, - "topologySpreadConstraints": { - "type": "array" - } - } -} +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "additionalResources": { + "description": "list of additional resources to create (processed via `tpl` function), useful for create ClickHouse clusters together with clickhouse-operator. check `kubectl explain chi` for details", + "type": "array" + }, + "affinity": { + "description": "affinity for scheduler pod assignment, check `kubectl explain pod.spec.affinity` for details", + "type": "object" + }, + "commonAnnotations": { + "description": "set of annotations that will be applied to all the resources for the operator", + "type": "object" + }, + "commonLabels": { + "description": "set of labels that will be applied to all the resources for the operator", + "type": "object" + }, + "configs": { + "description": "clickhouse operator configs", + "type": "object", + "properties": { + "confdFiles": { + "type": ["string", "null"] + }, + "configdFiles": { + "type": "object", + "properties": { + "01-clickhouse-01-listen.xml": { + "type": "string" + }, + "01-clickhouse-02-logger.xml": { + "type": "string" + }, + "01-clickhouse-03-query_log.xml": { + "type": "string" + }, + "01-clickhouse-04-part_log.xml": { + "type": "string" + }, + "01-clickhouse-05-trace_log.xml": { + "type": "string" + } + } + }, + "files": { + "type": "object", + "properties": { + "config.yaml": { + "type": "object", + "properties": { + "annotation": { + "type": "object", + "properties": { + "exclude": { + "type": "array" + }, + "include": { + "type": "array" + } + } + }, + "clickhouse": { + "type": "object", + "properties": { + "access": { + "type": "object", + "properties": { + "password": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "rootCA": { + "type": "string" + }, + "scheme": { + "type": "string" + }, + "secret": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + } + } + }, + "timeouts": { + "type": "object", + "properties": { + "connect": { + "type": "integer" + }, + "query": { + "type": "integer" + } + } + }, + "username": { + "type": "string" + } + } + }, + "addons": { + "type": "object", + "properties": { + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "spec": { + "type": "object", + "properties": { + "configuration": { + "type": "object", + "properties": { + "files": { + "type": ["object", "null"] + }, + "profiles": { + "type": ["object", "null"], + "properties": { + "clickhouse_operator/format_display_secrets_in_show_and_select": { + "type": "integer" + } + } + }, + "quotas": { + "type": ["object", "null"] + }, + "settings": { + "type": ["object", "null"], + "properties": { + "display_secrets_in_show_and_select": { + "type": "integer" + } + } + }, + "users": { + "type": ["object", "null"], + "properties": { + "{clickhouseOperatorUser}/access_management": { + "type": "integer" + }, + "{clickhouseOperatorUser}/named_collection_control": { + "type": "integer" + }, + "{clickhouseOperatorUser}/show_named_collections": { + "type": "integer" + }, + "{clickhouseOperatorUser}/show_named_collections_secrets": { + "type": "integer" + } + } + } + } + } + } + }, + "version": { + "type": "string" + } + } + } + } + } + }, + "configuration": { + "type": "object", + "properties": { + "file": { + "type": "object", + "properties": { + "path": { + "type": "object", + "properties": { + "common": { + "type": "string" + }, + "host": { + "type": "string" + }, + "user": { + "type": "string" + } + } + } + } + }, + "network": { + "type": "object", + "properties": { + "hostRegexpTemplate": { + "type": "string" + } + } + }, + "user": { + "type": "object", + "properties": { + "default": { + "type": "object", + "properties": { + "networksIP": { + "type": "array", + "items": { + "type": "string" + } + }, + "password": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "quota": { + "type": "string" + } + } + } + } + } + } + }, + "configurationRestartPolicy": { + "type": "object", + "properties": { + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "files/*.xml": { + "type": "string" + }, + "files/config.d/*.xml": { + "type": "string" + }, + "files/config.d/*dict*.xml": { + "type": "string" + }, + "files/config.d/*no_restart*": { + "type": "string" + }, + "profiles/default/background_*_pool_size": { + "type": "string" + }, + "profiles/default/max_*_for_server": { + "type": "string" + }, + "settings/*": { + "type": "string" + }, + "settings/access_control_path": { + "type": "string" + }, + "settings/dictionaries_config": { + "type": "string" + }, + "settings/display_secrets_in_show_and_select": { + "type": "string" + }, + "settings/logger": { + "type": "string" + }, + "settings/logger/*": { + "type": "string" + }, + "settings/macros/*": { + "type": "string" + }, + "settings/max_*_to_drop": { + "type": "string" + }, + "settings/max_concurrent_queries": { + "type": "string" + }, + "settings/max_server_memory_*": { + "type": "string" + }, + "settings/models_config": { + "type": "string" + }, + "settings/remote_servers/*": { + "type": "string" + }, + "settings/user_defined_executable_functions_config": { + "type": "string" + }, + "settings/user_directories/*": { + "type": "string" + }, + "zookeeper/*": { + "type": "string" + } + } + } + }, + "version": { + "type": "string" + } + } + } + } + } + }, + "metrics": { + "type": "object", + "properties": { + "timeouts": { + "type": "object", + "properties": { + "collect": { + "type": "integer" + } + } + } + } + } + } + }, + "keeper": { + "type": "object", + "properties": { + "configuration": { + "type": "object", + "properties": { + "file": { + "type": "object", + "properties": { + "path": { + "type": "object", + "properties": { + "common": { + "type": "string" + }, + "host": { + "type": "string" + }, + "user": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "label": { + "type": "object", + "properties": { + "appendScope": { + "type": "string" + }, + "exclude": { + "type": "array" + }, + "include": { + "type": "array" + } + } + }, + "logger": { + "type": "object", + "properties": { + "alsologtostderr": { + "type": "string" + }, + "log_backtrace_at": { + "type": "string" + }, + "logtostderr": { + "type": "string" + }, + "stderrthreshold": { + "type": "string" + }, + "v": { + "type": "string" + }, + "vmodule": { + "type": "string" + } + } + }, + "metrics": { + "type": "object", + "properties": { + "labels": { + "type": "object", + "properties": { + "exclude": { + "type": "array" + } + } + } + } + }, + "pod": { + "type": "object", + "properties": { + "terminationGracePeriod": { + "type": "integer" + } + } + }, + "reconcile": { + "type": "object", + "properties": { + "host": { + "type": "object", + "properties": { + "wait": { + "type": "object", + "properties": { + "exclude": { + "type": "boolean" + }, + "include": { + "type": "boolean" + }, + "probes": { + "type": "object", + "properties": { + "readiness": { + "type": "boolean" + }, + "startup": { + "type": "boolean" + } + } + }, + "queries": { + "type": "boolean" + }, + "replicas": { + "type": "object", + "properties": { + "all": { + "type": "boolean" + }, + "delay": { + "type": "integer" + }, + "new": { + "type": "boolean" + } + } + } + } + } + } + }, + "runtime": { + "type": "object", + "properties": { + "reconcileCHIsThreadsNumber": { + "type": "integer" + }, + "reconcileShardsMaxConcurrencyPercent": { + "type": "integer" + }, + "reconcileShardsThreadsNumber": { + "type": "integer" + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "create": { + "type": "object", + "properties": { + "onFailure": { + "type": "string" + } + } + }, + "update": { + "type": "object", + "properties": { + "onFailure": { + "type": "string" + }, + "pollInterval": { + "type": "integer" + }, + "timeout": { + "type": "integer" + } + } + } + } + } + } + }, + "statefulSet": { + "type": "object", + "properties": { + "revisionHistoryLimit": { + "type": "integer" + } + } + }, + "status": { + "type": "object", + "properties": { + "fields": { + "type": "object", + "properties": { + "action": { + "type": "boolean" + }, + "actions": { + "type": "boolean" + }, + "error": { + "type": "boolean" + }, + "errors": { + "type": "boolean" + } + } + } + } + }, + "template": { + "type": "object", + "properties": { + "chi": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "policy": { + "type": "string" + } + } + }, + "chk": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "policy": { + "type": "string" + } + } + } + } + }, + "watch": { + "type": "object", + "properties": { + "namespaces": { + "type": "array" + } + } + } + } + } + } + }, + "keeperConfdFiles": { + "type": ["string", "null"] + }, + "keeperConfigdFiles": { + "type": "object", + "properties": { + "01-keeper-01-default-config.xml": { + "type": "string" + }, + "01-keeper-02-readiness.xml": { + "type": "string" + }, + "01-keeper-03-enable-reconfig.xml": { + "type": "string" + } + } + }, + "keeperTemplatesdFiles": { + "type": ["object", "null"], + "properties": { + "readme": { + "type": ["string", "null"] + } + } + }, + "keeperUsersdFiles": { + "type": ["string", "null"] + }, + "templatesdFiles": { + "type": ["object", "null"], + "properties": { + "001-templates.json.example": { + "type": ["string", "null"] + }, + "default-pod-template.yaml.example": { + "type": ["string", "null"] + }, + "default-storage-template.yaml.example": { + "type": ["string", "null"] + }, + "readme": { + "type": ["string", "null"] + } + } + }, + "usersdFiles": { + "type": ["object", "null"], + "properties": { + "01-clickhouse-operator-profile.xml": { + "type": ["string", "null"] + }, + "02-clickhouse-default-profile.xml": { + "type": ["string", "null"] + } + } + } + } + }, + "dashboards": { + "type": "object", + "properties": { + "additionalLabels": { + "description": "labels to add to a secret with dashboards", + "type": "object", + "properties": { + "grafana_dashboard": { + "type": "string" + } + } + }, + "annotations": { + "description": "annotations to add to a secret with dashboards", + "type": "object" + }, + "enabled": { + "description": "provision grafana dashboards as configMaps (can be synced by grafana dashboards sidecar https://github.com/grafana/helm-charts/blob/grafana-8.3.4/charts/grafana/values.yaml#L778 )", + "type": "boolean" + }, + "grafana_folder": { + "type": "string" + } + } + }, + "deployment": { + "type": "object", + "properties": { + "strategy": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + } + } + }, + "fullnameOverride": { + "description": "full name of the chart.", + "type": "string" + }, + "imagePullSecrets": { + "description": "image pull secret for private images in clickhouse-operator pod possible value format `[{\"name\":\"your-secret-name\"}]`, check `kubectl explain pod.spec.imagePullSecrets` for details", + "type": "array" + }, + "metrics": { + "type": "object", + "properties": { + "containerSecurityContext": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "env": { + "description": "additional environment variables for the deployment of metrics-exporter containers possible format value `[{\"name\": \"SAMPLE\", \"value\": \"text\"}]`", + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "description": "image pull policy", + "type": "string" + }, + "repository": { + "description": "image repository", + "type": "string" + }, + "tag": { + "description": "image tag (chart's appVersion value will be used if not set)", + "type": "string" + } + } + }, + "resources": { + "description": "custom resource configuration", + "type": "object" + } + } + }, + "nameOverride": { + "description": "override name of the chart", + "type": "string" + }, + "namespaceOverride": { + "type": "string" + }, + "nodeSelector": { + "description": "node for scheduler pod assignment, check `kubectl explain pod.spec.nodeSelector` for details", + "type": "object" + }, + "operator": { + "type": "object", + "properties": { + "containerSecurityContext": { + "type": "object" + }, + "env": { + "description": "additional environment variables for the clickhouse-operator container in deployment possible format value `[{\"name\": \"SAMPLE\", \"value\": \"text\"}]`", + "type": "array" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "description": "image pull policy", + "type": "string" + }, + "repository": { + "description": "image repository", + "type": "string" + }, + "tag": { + "description": "image tag (chart's appVersion value will be used if not set)", + "type": "string" + } + } + }, + "priorityClassName": { + "description": "priority class name for the clickhouse-operator deployment, check `kubectl explain pod.spec.priorityClassName` for details", + "type": "string" + }, + "resources": { + "description": "custom resource configuration, check `kubectl explain pod.spec.containers.resources` for details", + "type": "object" + } + } + }, + "podAnnotations": { + "description": "annotations to add to the clickhouse-operator pod, check `kubectl explain pod.spec.annotations` for details", + "type": "object", + "properties": { + "clickhouse-operator-metrics/port": { + "type": "string" + }, + "clickhouse-operator-metrics/scrape": { + "type": "string" + }, + "prometheus.io/port": { + "type": "string" + }, + "prometheus.io/scrape": { + "type": "string" + } + } + }, + "podLabels": { + "description": "labels to add to the clickhouse-operator pod", + "type": "object" + }, + "podSecurityContext": { + "type": "object" + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "description": "specifies whether rbac resources should be created", + "type": "boolean" + }, + "namespaceScoped": { + "description": "specifies whether to create roles and rolebindings at the cluster level or namespace level", + "type": "boolean" + } + } + }, + "secret": { + "type": "object", + "properties": { + "create": { + "description": "create a secret with operator credentials", + "type": "boolean" + }, + "password": { + "description": "operator credentials password", + "type": "string" + }, + "username": { + "description": "operator credentials username", + "type": "string" + } + } + }, + "serviceAccount": { + "type": ["object", "null"], + "properties": { + "annotations": { + "description": "annotations to add to the service account", + "type": "object" + }, + "create": { + "description": "specifies whether a service account should be created", + "type": "boolean" + }, + "name": { + "description": "the name of the service account to use; if not set and create is true, a name is generated using the fullname template", + "type": ["string","null"] + } + } + }, + "serviceMonitor": { + "type": "object", + "properties": { + "additionalLabels": { + "description": "additional labels for service monitor", + "type": "object" + }, + "clickhouseMetrics": { + "type": "object", + "properties": { + "interval": { + "type": "string" + }, + "metricRelabelings": { + "type": "array" + }, + "relabelings": { + "type": "array" + }, + "scrapeTimeout": { + "type": "string" + } + } + }, + "enabled": { + "description": "ServiceMonitor Custom resource is created for a [prometheus-operator](https://github.com/prometheus-operator/prometheus-operator) In serviceMonitor will be created two endpoints ch-metrics on port 8888 and op-metrics # 9999. Ypu can specify interval, scrapeTimeout, relabelings, metricRelabelings for each endpoint below", + "type": "boolean" + }, + "operatorMetrics": { + "type": "object", + "properties": { + "interval": { + "type": "string" + }, + "metricRelabelings": { + "type": "array" + }, + "relabelings": { + "type": "array" + }, + "scrapeTimeout": { + "type": "string" + } + } + } + } + }, + "tolerations": { + "description": "tolerations for scheduler pod assignment, check `kubectl explain pod.spec.tolerations` for details", + "type": "array" + }, + "topologySpreadConstraints": { + "type": "array" + } + } +} diff --git a/deploy/helm/clickhouse-operator/values.yaml b/deploy/helm/clickhouse-operator/values.yaml index 2c9a6707c..199e4cd98 100644 --- a/deploy/helm/clickhouse-operator/values.yaml +++ b/deploy/helm/clickhouse-operator/values.yaml @@ -7,6 +7,31 @@ deployment: # look details in `kubectl explain deployment.spec.strategy` strategy: type: Recreate +crdHook: + # crdHook.enabled -- enable automatic CRD installation/update via pre-install/pre-upgrade hooks + # when disabled, CRDs must be installed manually using kubectl apply + enabled: true + image: + # crdHook.image.repository -- image repository for CRD installation job + repository: bitnami/kubectl + # crdHook.image.tag -- image tag for CRD installation job + tag: "latest" + # crdHook.image.pullPolicy -- image pull policy for CRD installation job + pullPolicy: IfNotPresent + # crdHook.resources -- resource limits and requests for CRD installation job + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + # crdHook.nodeSelector -- node selector for CRD installation job + nodeSelector: {} + # crdHook.tolerations -- tolerations for CRD installation job + tolerations: [] + # crdHook.affinity -- affinity for CRD installation job + affinity: {} operator: image: # operator.image.repository -- image repository diff --git a/go.mod b/go.mod index 2de661b1d..8ea31c133 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,7 @@ require ( go.opentelemetry.io/otel/sdk v1.24.0 go.opentelemetry.io/otel/sdk/metric v1.24.0 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e - golang.org/x/sync v0.12.0 + golang.org/x/sync v0.18.0 gopkg.in/d4l3k/messagediff.v1 v1.2.1 gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/controller-runtime v0.15.1 @@ -88,14 +88,14 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.38.0 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.39.0 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index cfaad41cb..5b2438e71 100644 --- a/go.sum +++ b/go.sum @@ -544,8 +544,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -583,8 +583,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -603,8 +603,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -653,20 +653,20 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -726,8 +726,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=