Compare commits
44 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
a6fab4dc75 | |
|
|
a45d681a08 | |
|
|
1c8e3d087d | |
|
|
d3908e6a3c | |
|
|
daebbfb0ad | |
|
|
8034acff6e | |
|
|
55adae872e | |
|
|
4fdc4affae | |
|
|
fc54ff76d2 | |
|
|
377ca5c1a2 | |
|
|
160753c87f | |
|
|
ff60d0b565 | |
|
|
391c677058 | |
|
|
310cdead2e | |
|
|
c2d783e872 | |
|
|
98d9cf4b28 | |
|
|
6673ebad84 | |
|
|
1b8f2871f6 | |
|
|
e34ea571fc | |
|
|
70205ac9ce | |
|
|
3a5c57e144 | |
|
|
e4267a4317 | |
|
|
d94a7ada2b | |
|
|
c31ecf5061 | |
|
|
a30957409d | |
|
|
c354768e60 | |
|
|
4de4c46f87 | |
|
|
d646b3cbd4 | |
|
|
3f5d4110f6 | |
|
|
c443baa103 | |
|
|
9c1b393b35 | |
|
|
3728b6f647 | |
|
|
2ad21b3df0 | |
|
|
e3de97fcbd | |
|
|
1ecffc87e4 | |
|
|
f708d06200 | |
|
|
55030e4eee | |
|
|
27d6fb08c6 | |
|
|
6fc2278f5f | |
|
|
2116c93cc4 | |
|
|
fc900dda54 | |
|
|
074de257f8 | |
|
|
ce6197a514 | |
|
|
31b3bd4e62 |
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache: false
|
||||
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: Build
|
||||
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
run: make check test
|
||||
- name: Archive built binaries
|
||||
run: tar -cvf built-binaries.tar helmfile diff-yamls dyff
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: built-binaries-${{ github.run_id }}
|
||||
path: built-binaries.tar
|
||||
|
|
@ -54,12 +54,12 @@ jobs:
|
|||
# Helm maintains the latest minor version only and therefore each Helmfile version supports 2 Helm minor versions.
|
||||
# That's why we cover only 2 Helm minor versions in this matrix.
|
||||
# See https://github.com/helmfile/helmfile/pull/286#issuecomment-1250161182 for more context.
|
||||
- helm-version: v3.17.4
|
||||
- helm-version: v3.18.6
|
||||
kustomize-version: v5.2.1
|
||||
plugin-secrets-version: 4.6.5
|
||||
plugin-diff-version: 3.11.0
|
||||
extra-helmfile-flags: ''
|
||||
- helm-version: v3.17.4
|
||||
- helm-version: v3.18.6
|
||||
kustomize-version: v5.4.3
|
||||
# We assume that the helm-secrets plugin is supposed to
|
||||
# work with the two most recent helm minor versions.
|
||||
|
|
@ -69,30 +69,30 @@ jobs:
|
|||
plugin-secrets-version: 4.6.5
|
||||
plugin-diff-version: 3.12.5
|
||||
extra-helmfile-flags: ''
|
||||
- helm-version: v3.18.6
|
||||
- helm-version: v3.19.0
|
||||
kustomize-version: v5.2.1
|
||||
plugin-secrets-version: 4.6.5
|
||||
plugin-diff-version: 3.11.0
|
||||
extra-helmfile-flags: ''
|
||||
- helm-version: v3.18.6
|
||||
- helm-version: v3.19.0
|
||||
kustomize-version: v5.4.3
|
||||
plugin-secrets-version: 4.6.5
|
||||
plugin-diff-version: 3.12.5
|
||||
extra-helmfile-flags: ''
|
||||
# In case you need to test some optional helmfile features,
|
||||
# enable it via extra-helmfile-flags below.
|
||||
- helm-version: v3.18.6
|
||||
- helm-version: v3.19.0
|
||||
kustomize-version: v5.4.3
|
||||
plugin-secrets-version: 4.6.5
|
||||
plugin-diff-version: 3.12.5
|
||||
extra-helmfile-flags: '--enable-live-output'
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- uses: actions/download-artifact@v5
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: built-binaries-${{ github.run_id }}
|
||||
- name: install semver
|
||||
|
|
@ -109,6 +109,8 @@ jobs:
|
|||
run: make -C .github/workflows helm vault sops kustomize
|
||||
- name: Start minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
kubernetes-version: v1.33.1
|
||||
- name: Execute integration tests
|
||||
run: make integration
|
||||
env:
|
||||
|
|
@ -125,7 +127,7 @@ jobs:
|
|||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v5
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: built-binaries-${{ github.run_id }}
|
||||
- name: Extract tar to get built binaries
|
||||
|
|
|
|||
|
|
@ -25,11 +25,22 @@ jobs:
|
|||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: check disk usage
|
||||
run: df -h
|
||||
- name: cleanup disk
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf /usr/local/share/boost
|
||||
sudo rm -fr /usr/local/lib/android
|
||||
sudo rm -fr /opt/hostedtoolcache/CodeQL
|
||||
sudo docker image prune --all --force
|
||||
sudo docker builder prune -a
|
||||
- name: check disk usage
|
||||
run: df -h
|
||||
- uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
version: latest
|
||||
|
|
|
|||
12
Dockerfile
12
Dockerfile
|
|
@ -12,11 +12,11 @@ RUN make static-${TARGETOS}-${TARGETARCH}
|
|||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
FROM alpine:3.19
|
||||
FROM alpine:3.22
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/helmfile/helmfile
|
||||
|
||||
RUN apk add --no-cache ca-certificates git bash curl jq openssh-client gnupg
|
||||
RUN apk add --no-cache ca-certificates git bash curl jq yq openssh-client gnupg
|
||||
|
||||
ARG TARGETARCH TARGETOS TARGETPLATFORM
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ ENV HELM_CONFIG_HOME="${HELM_CONFIG_HOME}"
|
|||
ARG HELM_DATA_HOME="${HOME}/.local/share/helm"
|
||||
ENV HELM_DATA_HOME="${HELM_DATA_HOME}"
|
||||
|
||||
ARG HELM_VERSION="v3.18.6"
|
||||
ARG HELM_VERSION="v3.19.0"
|
||||
ENV HELM_VERSION="${HELM_VERSION}"
|
||||
ARG HELM_LOCATION="https://get.helm.sh"
|
||||
ARG HELM_FILENAME="helm-${HELM_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
|
||||
|
|
@ -38,8 +38,8 @@ RUN set -x && \
|
|||
curl --retry 5 --retry-connrefused -LO "${HELM_LOCATION}/${HELM_FILENAME}" && \
|
||||
echo Verifying ${HELM_FILENAME}... && \
|
||||
case ${TARGETPLATFORM} in \
|
||||
"linux/amd64") HELM_SHA256="3f43c0aa57243852dd542493a0f54f1396c0bc8ec7296bbb2c01e802010819ce" ;; \
|
||||
"linux/arm64") HELM_SHA256="5b8e00b6709caab466cbbb0bc29ee09059b8dc9417991dd04b497530e49b1737" ;; \
|
||||
"linux/amd64") HELM_SHA256="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
|
||||
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
|
||||
esac && \
|
||||
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
|
||||
echo Extracting ${HELM_FILENAME}... && \
|
||||
|
|
@ -93,7 +93,7 @@ RUN set -x && \
|
|||
[ "$(age --version)" = "${AGE_VERSION}" ] && \
|
||||
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
|
||||
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.12.5 && \
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.13.1 && \
|
||||
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.6.5 && \
|
||||
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.16.3 && \
|
||||
helm plugin install https://github.com/aslafy-z/helm-git.git --version v1.3.0 && \
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ RUN apt update -qq && \
|
|||
|
||||
ARG TARGETARCH TARGETOS TARGETPLATFORM
|
||||
|
||||
RUN wget https://github.com/mikefarah/yq/releases/latest/download/yq_${TARGETOS}_${TARGETARCH} -O /usr/local/bin/yq &&\
|
||||
chmod +x /usr/local/bin/yq
|
||||
|
||||
# Set Helm home variables so that also non-root users can use plugins etc.
|
||||
ARG HOME="/helm"
|
||||
ENV HOME="${HOME}"
|
||||
|
|
@ -35,7 +38,7 @@ ENV HELM_CONFIG_HOME="${HELM_CONFIG_HOME}"
|
|||
ARG HELM_DATA_HOME="${HOME}/.local/share/helm"
|
||||
ENV HELM_DATA_HOME="${HELM_DATA_HOME}"
|
||||
|
||||
ARG HELM_VERSION="v3.18.6"
|
||||
ARG HELM_VERSION="v3.19.0"
|
||||
ENV HELM_VERSION="${HELM_VERSION}"
|
||||
ARG HELM_LOCATION="https://get.helm.sh"
|
||||
ARG HELM_FILENAME="helm-${HELM_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
|
||||
|
|
@ -43,8 +46,8 @@ RUN set -x && \
|
|||
curl --retry 5 --retry-connrefused -LO "${HELM_LOCATION}/${HELM_FILENAME}" && \
|
||||
echo Verifying ${HELM_FILENAME}... && \
|
||||
case ${TARGETPLATFORM} in \
|
||||
"linux/amd64") HELM_SHA256="3f43c0aa57243852dd542493a0f54f1396c0bc8ec7296bbb2c01e802010819ce" ;; \
|
||||
"linux/arm64") HELM_SHA256="5b8e00b6709caab466cbbb0bc29ee09059b8dc9417991dd04b497530e49b1737" ;; \
|
||||
"linux/amd64") HELM_SHA256="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
|
||||
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
|
||||
esac && \
|
||||
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
|
||||
echo Extracting ${HELM_FILENAME}... && \
|
||||
|
|
@ -99,7 +102,7 @@ RUN set -x && \
|
|||
[ "$(age --version)" = "${AGE_VERSION}" ] && \
|
||||
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
|
||||
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.12.5 && \
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.13.1 && \
|
||||
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.6.5 && \
|
||||
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.16.3 && \
|
||||
helm plugin install https://github.com/aslafy-z/helm-git.git --version v1.3.0 && \
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ RUN make static-${TARGETOS}-${TARGETARCH}
|
|||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
FROM ubuntu:24.10
|
||||
FROM ubuntu:24.04
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/helmfile/helmfile
|
||||
|
||||
|
|
@ -25,6 +25,9 @@ RUN apt update -qq && \
|
|||
|
||||
ARG TARGETARCH TARGETOS TARGETPLATFORM
|
||||
|
||||
RUN wget https://github.com/mikefarah/yq/releases/latest/download/yq_${TARGETOS}_${TARGETARCH} -O /usr/local/bin/yq &&\
|
||||
chmod +x /usr/local/bin/yq
|
||||
|
||||
# Set Helm home variables so that also non-root users can use plugins etc.
|
||||
ARG HOME="/helm"
|
||||
ENV HOME="${HOME}"
|
||||
|
|
@ -35,7 +38,7 @@ ENV HELM_CONFIG_HOME="${HELM_CONFIG_HOME}"
|
|||
ARG HELM_DATA_HOME="${HOME}/.local/share/helm"
|
||||
ENV HELM_DATA_HOME="${HELM_DATA_HOME}"
|
||||
|
||||
ARG HELM_VERSION="v3.18.6"
|
||||
ARG HELM_VERSION="v3.19.0"
|
||||
ENV HELM_VERSION="${HELM_VERSION}"
|
||||
ARG HELM_LOCATION="https://get.helm.sh"
|
||||
ARG HELM_FILENAME="helm-${HELM_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
|
||||
|
|
@ -43,8 +46,8 @@ RUN set -x && \
|
|||
curl --retry 5 --retry-connrefused -LO "${HELM_LOCATION}/${HELM_FILENAME}" && \
|
||||
echo Verifying ${HELM_FILENAME}... && \
|
||||
case ${TARGETPLATFORM} in \
|
||||
"linux/amd64") HELM_SHA256="3f43c0aa57243852dd542493a0f54f1396c0bc8ec7296bbb2c01e802010819ce" ;; \
|
||||
"linux/arm64") HELM_SHA256="5b8e00b6709caab466cbbb0bc29ee09059b8dc9417991dd04b497530e49b1737" ;; \
|
||||
"linux/amd64") HELM_SHA256="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
|
||||
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
|
||||
esac && \
|
||||
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
|
||||
echo Extracting ${HELM_FILENAME}... && \
|
||||
|
|
@ -99,7 +102,7 @@ RUN set -x && \
|
|||
[ "$(age --version)" = "${AGE_VERSION}" ] && \
|
||||
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
|
||||
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.12.5 && \
|
||||
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.13.1 && \
|
||||
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.6.5 && \
|
||||
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.16.3 && \
|
||||
helm plugin install https://github.com/aslafy-z/helm-git.git --version v1.3.0 && \
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
[](https://slack.sweetops.com)
|
||||
[](https://helmfile.readthedocs.io/en/latest/)
|
||||
[](https://gurubase.io/g/helmfile)
|
||||
[](https://zread.ai/helmfile/helmfile)
|
||||
|
||||
声明式Helm Chart管理工具
|
||||
<br />
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
[](https://slack.sweetops.com)
|
||||
[](https://helmfile.readthedocs.io/en/latest/)
|
||||
[](https://gurubase.io/g/helmfile)
|
||||
[](https://zread.ai/helmfile/helmfile)
|
||||
|
||||
Deploy Kubernetes Helm Charts
|
||||
<br />
|
||||
|
|
@ -33,7 +34,9 @@ Helmfile is a declarative spec for deploying helm charts. It lets you...
|
|||
* Apply CI/CD to configuration changes.
|
||||
* Periodically sync to avoid skew in environments.
|
||||
|
||||
To avoid upgrades for each iteration of `helm`, the `helmfile` executable delegates to `helm` - as a result, `helm` must be installed.
|
||||
To avoid upgrades for each iteration of `helm`, the `helmfile` executable delegates to `helm` - as a result, the following must be installed
|
||||
- [helm](https://helm.sh/docs/intro/install/)
|
||||
- [helm-diff](https://github.com/databus23/helm-diff)
|
||||
|
||||
## Highlights
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
|
|||
// Set the global options for the root command.
|
||||
setGlobalOptionsForRootCmd(flags, globalConfig)
|
||||
|
||||
flags.ParseErrorsWhitelist.UnknownFlags = true
|
||||
flags.ParseErrorsAllowlist.UnknownFlags = true
|
||||
|
||||
globalImpl := config.NewGlobalImpl(globalConfig)
|
||||
|
||||
|
|
|
|||
|
|
@ -194,8 +194,9 @@ helmDefaults:
|
|||
skipSchemaValidation: false
|
||||
# wait for k8s resources via --wait. (default false)
|
||||
wait: true
|
||||
# if set and --wait enabled, will retry any failed check on resource state subject to the specified number of retries (default 0)
|
||||
waitRetries: 3
|
||||
# DEPRECATED: waitRetries is no longer supported as the --wait-retries flag was removed from Helm.
|
||||
# This configuration is ignored and preserved only for backward compatibility.
|
||||
# waitRetries: 3
|
||||
# if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout (default false, Implemented in Helm3.5)
|
||||
waitForJobs: true
|
||||
# time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks, and waits on pod/pvc/svc/deployment readiness) (default 300)
|
||||
|
|
@ -318,7 +319,8 @@ releases:
|
|||
# --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint', starts with helm 3.16.0 (default false)
|
||||
skipSchemaValidation: false
|
||||
wait: true
|
||||
waitRetries: 3
|
||||
# DEPRECATED: waitRetries is no longer supported - see documentation above
|
||||
# waitRetries: 3
|
||||
waitForJobs: true
|
||||
timeout: 60
|
||||
recreatePods: true
|
||||
|
|
@ -326,6 +328,9 @@ releases:
|
|||
reuseValues: false
|
||||
# set `false` to uninstall this release on sync. (default true)
|
||||
installed: true
|
||||
# Defines the strategy to use when updating. Possible value is:
|
||||
# - "reinstallIfForbidden": Performs an uninstall before the update only if the update is forbidden (e.g., due to permission issues or conflicts).
|
||||
updateStrategy: ""
|
||||
# restores previous state in case of failed release (default false)
|
||||
atomic: true
|
||||
# when true, cleans up any new resources created during a failed release (default false)
|
||||
|
|
|
|||
203
go.mod
203
go.mod
|
|
@ -6,37 +6,39 @@ require (
|
|||
dario.cat/mergo v1.0.2
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.15
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.88.7
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/gosuri/uitable v0.0.4
|
||||
github.com/hashicorp/go-getter v1.7.9
|
||||
github.com/hashicorp/go-getter v1.8.2
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/helmfile/chartify v0.24.7
|
||||
github.com/helmfile/vals v0.42.1
|
||||
github.com/helmfile/chartify v0.25.0
|
||||
github.com/helmfile/vals v0.42.4
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.9
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tatsushid/go-prettytable v0.0.0-20141013043238-ed2d14c29939
|
||||
github.com/tj/assert v0.0.3
|
||||
github.com/variantdev/dag v1.1.0
|
||||
github.com/zclconf/go-cty v1.16.4
|
||||
github.com/zclconf/go-cty v1.17.0
|
||||
github.com/zclconf/go-cty-yaml v1.1.0
|
||||
go.szostok.io/version v1.2.0
|
||||
go.uber.org/zap v1.27.0
|
||||
go.yaml.in/yaml/v2 v2.4.2
|
||||
go.yaml.in/yaml/v2 v2.4.3
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/sync v0.16.0
|
||||
golang.org/x/term v0.34.0
|
||||
helm.sh/helm/v3 v3.18.6
|
||||
k8s.io/apimachinery v0.34.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/term v0.36.0
|
||||
helm.sh/helm/v3 v3.19.0
|
||||
k8s.io/apimachinery v0.34.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.121.6 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/storage v1.56.1 // indirect
|
||||
cloud.google.com/go/storage v1.57.0 // indirect
|
||||
filippo.io/age v1.2.1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||
|
|
@ -46,12 +48,11 @@ require (
|
|||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/a8m/envsubst v1.4.3 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.7
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fujiwara/tfstate-lookup v1.7.0 // indirect
|
||||
github.com/fujiwara/tfstate-lookup v1.7.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
|
|
@ -62,18 +63,16 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-slug v0.16.4 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
||||
github.com/hashicorp/go-tfe v1.84.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
|
||||
github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e // indirect
|
||||
github.com/hashicorp/vault/api v1.20.0 // indirect
|
||||
github.com/hashicorp/vault/api v1.22.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/itchyny/gojq v0.12.16 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
|
|
@ -92,15 +91,15 @@ require (
|
|||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.28.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/api v0.248.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/oauth2 v0.31.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
google.golang.org/api v0.252.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.74.2 // indirect
|
||||
google.golang.org/protobuf v1.36.7 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
|
|
@ -109,10 +108,10 @@ require (
|
|||
require (
|
||||
al.essio.dev/pkg/shellescape v1.6.0 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.5 // indirect
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.8.0 // indirect
|
||||
cloud.google.com/go/kms v1.22.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/kms v1.23.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.7 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
cloud.google.com/go/secretmanager v1.15.0 // indirect
|
||||
|
|
@ -120,23 +119,23 @@ require (
|
|||
github.com/1Password/connect-sdk-go v1.5.3 // indirect
|
||||
github.com/1password/onepassword-sdk-go v0.3.1 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect
|
||||
github.com/DopplerHQ/cli v0.5.11-0.20230908185655-7aef4713e1a4 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.2.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/antchfx/jsonquery v1.3.6 // indirect
|
||||
github.com/antchfx/xpath v1.3.5 // indirect
|
||||
|
|
@ -144,28 +143,26 @@ require (
|
|||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.38.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.64.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
|
||||
github.com/aws/smithy-go v1.23.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.19.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.45.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.65.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.29.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.38.9 // indirect
|
||||
github.com/aws/smithy-go v1.23.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
|
|
@ -173,12 +170,12 @@ require (
|
|||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
|
||||
github.com/containerd/containerd v1.7.27 // indirect
|
||||
github.com/containerd/containerd v1.7.28 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/cyberark/conjur-api-go v0.13.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyberark/conjur-api-go v0.13.7 // indirect
|
||||
github.com/danieljoos/wincred v1.2.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
|
||||
|
|
@ -191,23 +188,35 @@ require (
|
|||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e // indirect
|
||||
github.com/getsops/sops/v3 v3.10.2 // indirect
|
||||
github.com/getsops/sops/v3 v3.11.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/loads v0.22.0 // indirect
|
||||
github.com/go-openapi/runtime v0.28.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/go-openapi/analysis v0.24.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.2 // indirect
|
||||
github.com/go-openapi/loads v0.23.1 // indirect
|
||||
github.com/go-openapi/runtime v0.29.0 // indirect
|
||||
github.com/go-openapi/spec v0.22.0 // indirect
|
||||
github.com/go-openapi/strfmt v0.24.0 // indirect
|
||||
github.com/go-openapi/swag v0.24.1 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.24.0 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.24.0 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.1 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.1 // indirect
|
||||
github.com/go-openapi/validate v0.25.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-yaml v1.17.1 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
|
|
@ -221,16 +230,16 @@ require (
|
|||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hcp-sdk-go v0.155.0 // indirect
|
||||
github.com/hashicorp/hcp-sdk-go v0.162.0 // indirect
|
||||
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.6 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
|
|
@ -238,7 +247,6 @@ require (
|
|||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
|
|
@ -261,6 +269,7 @@ require (
|
|||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rubenv/sql-migrate v1.8.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect
|
||||
|
|
@ -270,48 +279,48 @@ require (
|
|||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
|
||||
github.com/urfave/cli v1.22.16 // indirect
|
||||
github.com/urfave/cli v1.22.17 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.19.0 // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.15.0 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.29.0 // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.22.0 // indirect
|
||||
github.com/zalando/go-keyring v0.2.6 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/gookit/color.v1 v1.1.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.34.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.3 // indirect
|
||||
k8s.io/cli-runtime v0.33.3 // indirect
|
||||
k8s.io/client-go v0.34.0 // indirect
|
||||
k8s.io/component-base v0.33.3 // indirect
|
||||
k8s.io/api v0.34.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.34.0 // indirect
|
||||
k8s.io/cli-runtime v0.34.0 // indirect
|
||||
k8s.io/client-go v0.34.1 // indirect
|
||||
k8s.io/component-base v0.34.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/kubectl v0.33.3 // indirect
|
||||
k8s.io/kubectl v0.34.0 // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
oras.land/oras-go/v2 v2.6.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.19.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.20.1 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ func (a *App) Diff(c DiffConfigProvider) error {
|
|||
}
|
||||
|
||||
if c.DetailedExitcode() && (len(allDiffDetectedErrs) > 0 || affectedAny) {
|
||||
// We take the first release error w/ exit status 2 (although all the defered errs should have exit status 2)
|
||||
// We take the first release error w/ exit status 2 (although all the deferred errs should have exit status 2)
|
||||
// to just let helmfile itself to exit with 2
|
||||
// See https://github.com/roboll/helmfile/issues/749
|
||||
code := 2
|
||||
|
|
@ -680,7 +680,7 @@ func (a *App) within(dir string, do func() error) error {
|
|||
|
||||
prev, err := a.fs.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed getting current working direcotyr: %v", err)
|
||||
return fmt.Errorf("failed getting current working directory: %v", err)
|
||||
}
|
||||
|
||||
absDir, err := a.fs.Abs(dir)
|
||||
|
|
@ -784,7 +784,7 @@ func createHelmKey(bin, kubectx string) helmKey {
|
|||
//
|
||||
// This is currently used for running all the helm commands for reconciling releases. But this may change in the future
|
||||
// once we enable each release to have its own helm binary/version.
|
||||
func (a *App) getHelm(st *state.HelmState) helmexec.Interface {
|
||||
func (a *App) getHelm(st *state.HelmState) (helmexec.Interface, error) {
|
||||
a.helmsMutex.Lock()
|
||||
defer a.helmsMutex.Unlock()
|
||||
|
||||
|
|
@ -799,14 +799,18 @@ func (a *App) getHelm(st *state.HelmState) helmexec.Interface {
|
|||
key := createHelmKey(bin, kubectx)
|
||||
|
||||
if _, ok := a.helms[key]; !ok {
|
||||
a.helms[key] = helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, kubeconfig, kubectx, &helmexec.ShellRunner{
|
||||
exec, err := helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, kubeconfig, kubectx, &helmexec.ShellRunner{
|
||||
Logger: a.Logger,
|
||||
Ctx: a.ctx,
|
||||
StripArgsValuesOnExitError: a.StripArgsValuesOnExitError,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.helms[key] = exec
|
||||
}
|
||||
|
||||
return a.helms[key]
|
||||
return a.helms[key], nil
|
||||
}
|
||||
|
||||
func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*state.HelmState) (bool, []error)) error {
|
||||
|
|
@ -958,7 +962,10 @@ var (
|
|||
func (a *App) ForEachState(do func(*Run) (bool, []error), includeTransitiveNeeds bool, o ...LoadOption) error {
|
||||
ctx := NewContext()
|
||||
err := a.visitStatesWithSelectorsAndRemoteSupport(a.FileOrDir, func(st *state.HelmState) (bool, []error) {
|
||||
helm := a.getHelm(st)
|
||||
helm, err := a.getHelm(st)
|
||||
if err != nil {
|
||||
return false, []error{err}
|
||||
}
|
||||
|
||||
run, err := NewRun(st, helm, ctx)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -415,4 +415,28 @@ releases:
|
|||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("show diff on changed selected release with reinstall", func(t *testing.T) {
|
||||
check(t, testcase{
|
||||
helmfile: `
|
||||
releases:
|
||||
- name: a
|
||||
chart: incubator/raw
|
||||
namespace: default
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: b
|
||||
chart: incubator/raw
|
||||
namespace: default
|
||||
`,
|
||||
selectors: []string{"name=a"},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^a$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
diffed: []exectest.Release{
|
||||
{Name: "a", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values"}},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,97 @@ releases:
|
|||
}
|
||||
}
|
||||
|
||||
func TestUpdateStrategyParamValidation(t *testing.T) {
|
||||
cases := []struct {
|
||||
files map[string]string
|
||||
updateStrategy string
|
||||
isValid bool
|
||||
}{
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`},
|
||||
"reinstallIfForbidden",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`},
|
||||
"reinstallIfForbidden",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy:
|
||||
`},
|
||||
"",
|
||||
true},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: foo
|
||||
`},
|
||||
"foo",
|
||||
false},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstal
|
||||
`},
|
||||
"reinstal",
|
||||
false},
|
||||
{map[string]string{
|
||||
"/path/to/helmfile.yaml": `releases:
|
||||
- name: zipkin
|
||||
chart: stable/zipkin
|
||||
updateStrategy: reinstall1
|
||||
`},
|
||||
"reinstall1",
|
||||
false},
|
||||
}
|
||||
|
||||
for idx, c := range cases {
|
||||
fs := testhelper.NewTestFs(c.files)
|
||||
app := &App{
|
||||
OverrideHelmBinary: DefaultHelmBinary,
|
||||
OverrideKubeContext: "default",
|
||||
Logger: newAppTestLogger(),
|
||||
Namespace: "",
|
||||
Env: "default",
|
||||
FileOrDir: "helmfile.yaml",
|
||||
}
|
||||
|
||||
expectNoCallsToHelm(app)
|
||||
|
||||
app = injectFs(app, fs)
|
||||
|
||||
err := app.ForEachState(
|
||||
Noop,
|
||||
false,
|
||||
SetFilter(true),
|
||||
)
|
||||
|
||||
if c.isValid && err != nil {
|
||||
t.Errorf("[case: %d] Unexpected error for valid case: %v", idx, err)
|
||||
} else if !c.isValid {
|
||||
var invalidUpdateStrategy state.InvalidUpdateStrategyError
|
||||
invalidUpdateStrategy.UpdateStrategy = c.updateStrategy
|
||||
if err == nil {
|
||||
t.Errorf("[case: %d] Expected error for invalid case", idx)
|
||||
} else if !strings.Contains(err.Error(), invalidUpdateStrategy.Error()) {
|
||||
t.Errorf("[case: %d] Unexpected error returned for invalid case\ngot: %v\nexpected underlying error: %s", idx, err, invalidUpdateStrategy.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVisitDesiredStatesWithReleasesFiltered_Issue1008_MissingNonDefaultEnvInBase(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"/path/to/base.yaml": `
|
||||
|
|
@ -2492,9 +2583,12 @@ func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string
|
|||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func MockExecer(logger *zap.SugaredLogger, kubeContext string) helmexec.Interface {
|
||||
execer := helmexec.New("helm", helmexec.HelmExecOptions{}, logger, "", kubeContext, &mockRunner{})
|
||||
return execer
|
||||
func MockExecer(logger *zap.SugaredLogger, kubeContext string) (helmexec.Interface, error) {
|
||||
execer, err := helmexec.New("helm", helmexec.HelmExecOptions{}, logger, "", kubeContext, &mockRunner{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return execer, nil
|
||||
}
|
||||
|
||||
// mocking helmexec.Interface
|
||||
|
|
@ -3073,6 +3167,97 @@ baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 defau
|
|||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade with reinstallIfForbidden
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-reinstallIfForbidden",
|
||||
loc: location(),
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "foo", Chart: "stable/mychart1", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^foo$", Flags: listFlags("", "default")}: ``,
|
||||
{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 default
|
||||
`,
|
||||
{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
upgraded: []exectest.Release{
|
||||
{Name: "baz", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "bar", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "foo", Flags: []string{"--kube-context", "default"}},
|
||||
},
|
||||
deleted: []exectest.Release{},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// install with upgrade and --skip-diff-on-install with reinstallIfForbidden
|
||||
//
|
||||
{
|
||||
name: "install-with-upgrade-with-skip-diff-on-install-with-reinstallIfForbidden",
|
||||
loc: location(),
|
||||
skipDiffOnInstall: true,
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `
|
||||
releases:
|
||||
- name: baz
|
||||
chart: stable/mychart3
|
||||
disableValidationOnInstall: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
- name: foo
|
||||
chart: stable/mychart1
|
||||
disableValidationOnInstall: true
|
||||
needs:
|
||||
- bar
|
||||
- name: bar
|
||||
chart: stable/mychart2
|
||||
disableValidation: true
|
||||
updateStrategy: reinstallIfForbidden
|
||||
`,
|
||||
},
|
||||
diffs: map[exectest.DiffKey]error{
|
||||
{Name: "baz", Chart: "stable/mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
{Name: "bar", Chart: "stable/mychart2", Flags: "--disable-validation --kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
|
||||
},
|
||||
lists: map[exectest.ListKey]string{
|
||||
{Filter: "^foo$", Flags: listFlags("", "default")}: ``,
|
||||
{Filter: "^bar$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 default
|
||||
`,
|
||||
{Filter: "^baz$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
|
||||
baz 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart3-3.1.0 3.1.0 default
|
||||
`,
|
||||
},
|
||||
upgraded: []exectest.Release{
|
||||
{Name: "baz", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "bar", Flags: []string{"--kube-context", "default"}},
|
||||
{Name: "foo", Flags: []string{"--kube-context", "default"}},
|
||||
},
|
||||
concurrency: 1,
|
||||
},
|
||||
//
|
||||
// upgrades
|
||||
//
|
||||
{
|
||||
|
|
@ -3769,7 +3954,7 @@ releases:
|
|||
}
|
||||
for flagIdx := range wantDeletes[relIdx].Flags {
|
||||
if wantDeletes[relIdx].Flags[flagIdx] != helm.Deleted[relIdx].Flags[flagIdx] {
|
||||
t.Errorf("releaes[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
|
||||
t.Errorf("releases[%d].flags[%d]: got %v, want %v", relIdx, flagIdx, helm.Deleted[relIdx].Flags[flagIdx], wantDeletes[relIdx].Flags[flagIdx])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"dario.cat/mergo"
|
||||
"github.com/helmfile/vals"
|
||||
|
|
@ -34,7 +35,7 @@ type desiredStateLoader struct {
|
|||
chart string
|
||||
fs *filesystem.FileSystem
|
||||
|
||||
getHelm func(*state.HelmState) helmexec.Interface
|
||||
getHelm func(*state.HelmState) (helmexec.Interface, error)
|
||||
|
||||
remote *remote.Remote
|
||||
logger *zap.SugaredLogger
|
||||
|
|
@ -285,6 +286,18 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba
|
|||
}
|
||||
}
|
||||
|
||||
// Validate updateStrategy value if set in the releases
|
||||
for i := range finalState.Releases {
|
||||
if finalState.Releases[i].UpdateStrategy != "" {
|
||||
if !slices.Contains(state.ValidUpdateStrategyValues, finalState.Releases[i].UpdateStrategy) {
|
||||
return nil, &state.StateLoadError{
|
||||
Msg: fmt.Sprintf("failed to read %s", finalState.FilePath),
|
||||
Cause: &state.InvalidUpdateStrategyError{UpdateStrategy: finalState.Releases[i].UpdateStrategy},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalState.OrginReleases = finalState.Releases
|
||||
return finalState, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
HelmRequiredVersion = "v3.17.4"
|
||||
HelmDiffRecommendedVersion = "v3.12.5"
|
||||
HelmRecommendedVersion = "v3.18.6"
|
||||
HelmRequiredVersion = "v3.18.6"
|
||||
HelmDiffRecommendedVersion = "v3.13.1"
|
||||
HelmRecommendedVersion = "v3.19.0"
|
||||
HelmSecretsRecommendedVersion = "v4.6.5"
|
||||
HelmGitRecommendedVersion = "v1.3.0"
|
||||
HelmS3RecommendedVersion = "v0.16.3"
|
||||
|
|
@ -163,7 +163,10 @@ func (h *HelmfileInit) WhetherContinue(ask string) error {
|
|||
|
||||
func (h *HelmfileInit) CheckHelmPlugins() error {
|
||||
settings := cli.New()
|
||||
helm := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", "", h.runner)
|
||||
helm, err := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", "", h.runner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range helmPlugins {
|
||||
pluginVersion, err := helmexec.GetPluginVersion(p.name, settings.PluginsDirectory)
|
||||
if err != nil {
|
||||
|
|
|
|||
11
pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
vendored
Normal file
11
pkg/app/testdata/app_diff_test/show_diff_on_changed_selected_release_with_reinstall
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
1 release(s) matching name=a found in helmfile.yaml
|
||||
|
||||
processing 1 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default/default/a
|
||||
|
||||
processing releases in group 1/1: default/default/a
|
||||
changing working directory back to "/path/to"
|
||||
35
pkg/app/testdata/testapply/install-with-upgrade-with-reinstallifforbidden/log
vendored
Normal file
35
pkg/app/testdata/testapply/install-with-upgrade-with-reinstallifforbidden/log
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) UPDATED
|
||||
baz (stable/mychart3) UPDATED
|
||||
foo (stable/mychart1) UPDATED
|
||||
|
||||
invoking preapply hooks for 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//foo
|
||||
2 default//baz, default//bar
|
||||
|
||||
invoking preapply hooks for releases in group 1/2: default//foo
|
||||
invoking preapply hooks for releases in group 2/2: default//baz, default//bar
|
||||
processing 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//baz, default//bar
|
||||
2 default//foo
|
||||
|
||||
processing releases in group 1/2: default//baz, default//bar
|
||||
update strategy - sync success
|
||||
update strategy - sync success
|
||||
processing releases in group 2/2: default//foo
|
||||
getting deployed release version failed: Failed to get the version for: mychart1
|
||||
|
||||
UPDATED RELEASES:
|
||||
NAME NAMESPACE CHART VERSION DURATION
|
||||
baz stable/mychart3 3.1.0 0s
|
||||
bar stable/mychart2 3.1.0 0s
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
processing file "helmfile.yaml" in directory "."
|
||||
changing working directory to "/path/to"
|
||||
merged environment: &{default map[] map[]}
|
||||
3 release(s) found in helmfile.yaml
|
||||
|
||||
Affected releases are:
|
||||
bar (stable/mychart2) UPDATED
|
||||
baz (stable/mychart3) UPDATED
|
||||
foo (stable/mychart1) UPDATED
|
||||
|
||||
invoking preapply hooks for 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//foo
|
||||
2 default//baz, default//bar
|
||||
|
||||
invoking preapply hooks for releases in group 1/2: default//foo
|
||||
invoking preapply hooks for releases in group 2/2: default//baz, default//bar
|
||||
processing 2 groups of releases in this order:
|
||||
GROUP RELEASES
|
||||
1 default//baz, default//bar
|
||||
2 default//foo
|
||||
|
||||
processing releases in group 1/2: default//baz, default//bar
|
||||
update strategy - sync success
|
||||
update strategy - sync success
|
||||
processing releases in group 2/2: default//foo
|
||||
getting deployed release version failed: Failed to get the version for: mychart1
|
||||
|
||||
UPDATED RELEASES:
|
||||
NAME NAMESPACE CHART VERSION DURATION
|
||||
baz stable/mychart3 3.1.0 0s
|
||||
bar stable/mychart2 3.1.0 0s
|
||||
foo stable/mychart1 0s
|
||||
|
||||
changing working directory back to "/path/to"
|
||||
|
|
@ -57,6 +57,7 @@ type Release struct {
|
|||
|
||||
type Affected struct {
|
||||
Upgraded []*Release
|
||||
Reinstalled []*Release
|
||||
Deleted []*Release
|
||||
Failed []*Release
|
||||
}
|
||||
|
|
@ -107,7 +108,24 @@ func (helm *Helm) RegistryLogin(name, username, password, caFile, certFile, keyF
|
|||
return nil
|
||||
}
|
||||
func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart, namespace string, flags ...string) error {
|
||||
if strings.Contains(name, "error") {
|
||||
if strings.Contains(name, "forbidden") {
|
||||
releaseExists := false
|
||||
for _, release := range helm.Releases {
|
||||
if release.Name == name {
|
||||
releaseExists = true
|
||||
}
|
||||
}
|
||||
releaseDeleted := false
|
||||
for _, release := range helm.Deleted {
|
||||
if release.Name == name {
|
||||
releaseDeleted = true
|
||||
}
|
||||
}
|
||||
// Only fail if the release is present in the helm.Releases to simulate a forbidden update if it exists
|
||||
if releaseExists && !releaseDeleted {
|
||||
return fmt.Errorf("cannot patch %q with kind StatefulSet: StatefulSet.apps %q is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'ordinals', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden", name, name)
|
||||
}
|
||||
} else if strings.Contains(name, "error") {
|
||||
return errors.New("error")
|
||||
}
|
||||
helm.sync(helm.ReleasesMutex, func() {
|
||||
|
|
|
|||
|
|
@ -122,11 +122,10 @@ func redactedURL(chart string) string {
|
|||
}
|
||||
|
||||
// New for running helm commands
|
||||
func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger, kubeconfig string, kubeContext string, runner Runner) *execer {
|
||||
// TODO: proper error handling
|
||||
func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger, kubeconfig string, kubeContext string, runner Runner) (*execer, error) {
|
||||
version, err := GetHelmVersion(helmBinary, runner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if version.Prerelease() != "" {
|
||||
|
|
@ -143,7 +142,7 @@ func New(helmBinary string, options HelmExecOptions, logger *zap.SugaredLogger,
|
|||
kubeContext: kubeContext,
|
||||
runner: runner,
|
||||
decryptedSecrets: make(map[string]*decryptedSecret),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (helm *execer) SetExtraArgs(args ...string) {
|
||||
|
|
|
|||
|
|
@ -36,16 +36,22 @@ func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string
|
|||
return mock.output, mock.err
|
||||
}
|
||||
|
||||
func MockExecer(logger *zap.SugaredLogger, kubeconfig, kubeContext string) *execer {
|
||||
execer := New("helm", HelmExecOptions{}, logger, kubeconfig, kubeContext, &mockRunner{})
|
||||
return execer
|
||||
func MockExecer(logger *zap.SugaredLogger, kubeconfig, kubeContext string) (*execer, error) {
|
||||
execer, err := New("helm", HelmExecOptions{}, logger, kubeconfig, kubeContext, &mockRunner{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return execer, nil
|
||||
}
|
||||
|
||||
// Test methods
|
||||
|
||||
func TestNewHelmExec(t *testing.T) {
|
||||
buffer := bytes.NewBufferString("something")
|
||||
helm := MockExecer(NewLogger(buffer, "debug"), "config", "dev")
|
||||
helm, err := MockExecer(NewLogger(buffer, "debug"), "config", "dev")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if helm.kubeContext != "dev" {
|
||||
t.Error("helmexec.New() - kubeContext")
|
||||
}
|
||||
|
|
@ -58,7 +64,10 @@ func TestNewHelmExec(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_SetExtraArgs(t *testing.T) {
|
||||
helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
helm.SetExtraArgs()
|
||||
if len(helm.extra) != 0 {
|
||||
t.Error("helmexec.SetExtraArgs() - passing no arguments should not change extra field")
|
||||
|
|
@ -74,7 +83,10 @@ func Test_SetExtraArgs(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_SetHelmBinary(t *testing.T) {
|
||||
helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if helm.helmBinary != "helm" {
|
||||
t.Error("helmexec.command - default command is not helm")
|
||||
}
|
||||
|
|
@ -85,7 +97,10 @@ func Test_SetHelmBinary(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_SetEnableLiveOutput(t *testing.T) {
|
||||
helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if helm.options.EnableLiveOutput {
|
||||
t.Error("helmexec.options.EnableLiveOutput should not be enabled by default")
|
||||
}
|
||||
|
|
@ -96,7 +111,10 @@ func Test_SetEnableLiveOutput(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_SetDisableForceUpdate(t *testing.T) {
|
||||
helm := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
helm, err := MockExecer(NewLogger(os.Stdout, "info"), "config", "dev")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if helm.options.DisableForceUpdate {
|
||||
t.Error("helmexec.options.ForceUpdate should not be enabled by default")
|
||||
}
|
||||
|
|
@ -155,11 +173,14 @@ exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.e
|
|||
func Test_AddRepo(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Test case with certfile and keyfile
|
||||
buffer.Reset()
|
||||
err := helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "", false, false)
|
||||
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "", false, false)
|
||||
expected := `Adding repo myRepo https://repo.example.com/
|
||||
exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem
|
||||
`
|
||||
|
|
@ -292,8 +313,11 @@ exec: helm --kubeconfig config --kube-context dev repo add myRepo https://repo.e
|
|||
func Test_UpdateRepo(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.UpdateRepo()
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.UpdateRepo()
|
||||
expected := `Updating repo
|
||||
exec: helm --kubeconfig config --kube-context dev repo update
|
||||
`
|
||||
|
|
@ -346,8 +370,11 @@ exec: helm --kubeconfig config --kube-context dev registry login repo.example.co
|
|||
func Test_SyncRelease(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.SyncRelease(HelmContext{}, "release", "chart", "default", "--timeout 10", "--wait", "--wait-for-jobs")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.SyncRelease(HelmContext{}, "release", "chart", "default", "--timeout 10", "--wait", "--wait-for-jobs")
|
||||
expected := `Upgrading release=release, chart=chart, namespace=default
|
||||
exec: helm --kubeconfig config --kube-context dev upgrade --install release chart --timeout 10 --wait --wait-for-jobs --history-max 0
|
||||
`
|
||||
|
|
@ -386,8 +413,11 @@ exec: helm --kubeconfig config --kube-context dev upgrade --install release http
|
|||
func Test_UpdateDeps(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.UpdateDeps("./chart/foo")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.UpdateDeps("./chart/foo")
|
||||
expected := `Updating dependency ./chart/foo
|
||||
exec: helm --kubeconfig config --kube-context dev dependency update ./chart/foo
|
||||
`
|
||||
|
|
@ -416,8 +446,11 @@ func Test_BuildDeps(t *testing.T) {
|
|||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a")}
|
||||
helm := New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner)
|
||||
err := helm.BuildDeps("foo", "./chart/foo", []string{"--skip-refresh"}...)
|
||||
helm, err := New("helm", HelmExecOptions{}, logger, "config", "dev", &helm3Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.BuildDeps("foo", "./chart/foo", []string{"--skip-refresh"}...)
|
||||
expected := `Building dependency release=foo, chart=./chart/foo
|
||||
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo --skip-refresh
|
||||
v3.2.4+ge29ce2a
|
||||
|
|
@ -458,7 +491,10 @@ v3.2.4+ge29ce2a
|
|||
|
||||
buffer.Reset()
|
||||
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94")}
|
||||
helm = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm2Runner)
|
||||
helm, err = New("helm", HelmExecOptions{}, logger, "config", "dev", &helm2Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.BuildDeps("foo", "./chart/foo")
|
||||
expected = `Building dependency release=foo, chart=./chart/foo
|
||||
exec: helm --kubeconfig config --kube-context dev dependency build ./chart/foo
|
||||
|
|
@ -484,14 +520,17 @@ func Test_DecryptSecret(t *testing.T) {
|
|||
}()
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tmpFilePath := "path/to/temp/file"
|
||||
helm.writeTempFile = func(content []byte) (string, error) {
|
||||
return tmpFilePath, nil
|
||||
}
|
||||
|
||||
_, err := helm.DecryptSecret(HelmContext{}, "secretName")
|
||||
_, err = helm.DecryptSecret(HelmContext{}, "secretName")
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
|
|
@ -533,7 +572,10 @@ func Test_DecryptSecretWithGotmpl(t *testing.T) {
|
|||
}()
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
tmpFilePath := "path/to/temp/file"
|
||||
helm.writeTempFile = func(content []byte) (string, error) {
|
||||
|
|
@ -541,7 +583,7 @@ func Test_DecryptSecretWithGotmpl(t *testing.T) {
|
|||
}
|
||||
|
||||
secretName := "secretName.yaml.gotmpl"
|
||||
_, err := helm.DecryptSecret(HelmContext{}, secretName)
|
||||
_, err = helm.DecryptSecret(HelmContext{}, secretName)
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
|
|
@ -566,8 +608,11 @@ Decrypted %s/secretName.yaml.gotmpl into %s
|
|||
func Test_DiffRelease(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--timeout 10", "--wait", "--wait-for-jobs")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.DiffRelease(HelmContext{}, "release", "chart", "default", false, "--timeout 10", "--wait", "--wait-for-jobs")
|
||||
expected := `Comparing release=release, chart=chart, namespace=default
|
||||
|
||||
exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unreleased release chart --timeout 10 --wait --wait-for-jobs
|
||||
|
|
@ -609,8 +654,11 @@ exec: helm --kubeconfig config --kube-context dev diff upgrade --allow-unrelease
|
|||
func Test_DeleteRelease(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.DeleteRelease(HelmContext{}, "release")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.DeleteRelease(HelmContext{}, "release")
|
||||
expected := `Deleting release
|
||||
exec: helm --kubeconfig config --kube-context dev delete release
|
||||
`
|
||||
|
|
@ -624,8 +672,11 @@ exec: helm --kubeconfig config --kube-context dev delete release
|
|||
func Test_DeleteRelease_Flags(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.DeleteRelease(HelmContext{}, "release", "--purge")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.DeleteRelease(HelmContext{}, "release", "--purge")
|
||||
expected := `Deleting release
|
||||
exec: helm --kubeconfig config --kube-context dev delete release --purge
|
||||
`
|
||||
|
|
@ -640,8 +691,11 @@ exec: helm --kubeconfig config --kube-context dev delete release --purge
|
|||
func Test_TestRelease(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.TestRelease(HelmContext{}, "release")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.TestRelease(HelmContext{}, "release")
|
||||
expected := `Testing release
|
||||
exec: helm --kubeconfig config --kube-context dev test release
|
||||
`
|
||||
|
|
@ -655,8 +709,11 @@ exec: helm --kubeconfig config --kube-context dev test release
|
|||
func Test_TestRelease_Flags(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.TestRelease(HelmContext{}, "release", "--cleanup", "--timeout", "60")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.TestRelease(HelmContext{}, "release", "--cleanup", "--timeout", "60")
|
||||
expected := `Testing release
|
||||
exec: helm --kubeconfig config --kube-context dev test release --cleanup --timeout 60
|
||||
`
|
||||
|
|
@ -671,8 +728,11 @@ exec: helm --kubeconfig config --kube-context dev test release --cleanup --timeo
|
|||
func Test_ReleaseStatus(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.ReleaseStatus(HelmContext{}, "myRelease")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.ReleaseStatus(HelmContext{}, "myRelease")
|
||||
expected := `Getting status myRelease
|
||||
exec: helm --kubeconfig config --kube-context dev status myRelease
|
||||
`
|
||||
|
|
@ -687,9 +747,12 @@ exec: helm --kubeconfig config --kube-context dev status myRelease
|
|||
func Test_exec(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "", "")
|
||||
helm, err := MockExecer(logger, "", "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
env := map[string]string{}
|
||||
_, err := helm.exec([]string{"version"}, env, nil)
|
||||
_, err = helm.exec([]string{"version"}, env, nil)
|
||||
expected := `exec: helm version
|
||||
`
|
||||
if err != nil {
|
||||
|
|
@ -699,14 +762,20 @@ func Test_exec(t *testing.T) {
|
|||
t.Errorf("helmexec.exec()\nactual = %v\nexpect = %v", buffer.String(), expected)
|
||||
}
|
||||
|
||||
helm = MockExecer(logger, "config", "dev")
|
||||
helm, err = MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
ret, _ := helm.exec([]string{"diff"}, env, nil)
|
||||
if len(ret) != 0 {
|
||||
t.Error("helmexec.exec() - expected empty return value")
|
||||
}
|
||||
|
||||
buffer.Reset()
|
||||
helm = MockExecer(logger, "config", "dev")
|
||||
helm, err = MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
_, err = helm.exec([]string{"diff", "release", "chart", "--timeout 10", "--wait", "--wait-for-jobs"}, env, nil)
|
||||
expected = `exec: helm --kubeconfig config --kube-context dev diff release chart --timeout 10 --wait --wait-for-jobs
|
||||
`
|
||||
|
|
@ -741,7 +810,10 @@ func Test_exec(t *testing.T) {
|
|||
}
|
||||
|
||||
buffer.Reset()
|
||||
helm = MockExecer(logger, "", "")
|
||||
helm, err = MockExecer(logger, "", "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
helm.SetHelmBinary("overwritten")
|
||||
_, err = helm.exec([]string{"version"}, env, nil)
|
||||
expected = `exec: overwritten version
|
||||
|
|
@ -757,8 +829,11 @@ func Test_exec(t *testing.T) {
|
|||
func Test_Lint(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.Lint("release", "path/to/chart", "--values", "file.yml")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.Lint("release", "path/to/chart", "--values", "file.yml")
|
||||
expected := `Linting release=release, chart=path/to/chart
|
||||
exec: helm --kubeconfig config --kube-context dev lint path/to/chart --values file.yml
|
||||
`
|
||||
|
|
@ -773,8 +848,11 @@ exec: helm --kubeconfig config --kube-context dev lint path/to/chart --values fi
|
|||
func Test_Fetch(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.Fetch("chart", "--version", "1.2.3", "--untar", "--untardir", "/tmp/dir")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.Fetch("chart", "--version", "1.2.3", "--untar", "--untardir", "/tmp/dir")
|
||||
expected := `Fetching chart
|
||||
exec: helm --kubeconfig config --kube-context dev fetch chart --version 1.2.3 --untar --untardir /tmp/dir
|
||||
`
|
||||
|
|
@ -848,8 +926,11 @@ exec: helm --kubeconfig config --kube-context dev pull oci://repo/helm-charts --
|
|||
tt := tests[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buffer.Reset()
|
||||
helm := New(tt.helmBin, HelmExecOptions{}, logger, "config", "dev", &mockRunner{output: []byte(tt.helmVersion)})
|
||||
err := helm.ChartPull(tt.chartName, tt.chartPath, tt.chartFlags...)
|
||||
helm, err := New(tt.helmBin, HelmExecOptions{}, logger, "config", "dev", &mockRunner{output: []byte(tt.helmVersion)})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.ChartPull(tt.chartName, tt.chartPath, tt.chartFlags...)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -922,8 +1003,11 @@ func Test_LogLevels(t *testing.T) {
|
|||
for logLevel, expected := range logLevelTests {
|
||||
buffer.Reset()
|
||||
logger := NewLogger(&buffer, logLevel)
|
||||
helm := MockExecer(logger, "", "")
|
||||
err := helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", false, false)
|
||||
helm, err := MockExecer(logger, "", "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "", false, false)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -951,8 +1035,11 @@ func Test_mergeEnv(t *testing.T) {
|
|||
func Test_Template(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
logger := NewLogger(&buffer, "debug")
|
||||
helm := MockExecer(logger, "config", "dev")
|
||||
err := helm.TemplateRelease("release", "path/to/chart", "--values", "file.yml")
|
||||
helm, err := MockExecer(logger, "config", "dev")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = helm.TemplateRelease("release", "path/to/chart", "--values", "file.yml")
|
||||
expected := `Templating release=release, chart=path/to/chart
|
||||
exec: helm --kubeconfig config --kube-context dev template release path/to/chart --values file.yml
|
||||
`
|
||||
|
|
@ -978,13 +1065,19 @@ exec: helm --kubeconfig config --kube-context dev template release https://examp
|
|||
|
||||
func Test_IsHelm3(t *testing.T) {
|
||||
helm2Runner := mockRunner{output: []byte("Client: v2.16.0+ge13bc94\n")}
|
||||
helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if helm.IsHelm3() {
|
||||
t.Error("helmexec.IsHelm3() - Detected Helm 3 with Helm 2 version")
|
||||
}
|
||||
|
||||
helm3Runner := mockRunner{output: []byte("v3.0.0+ge29ce2a\n")}
|
||||
helm = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
||||
helm, err = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !helm.IsHelm3() {
|
||||
t.Error("helmexec.IsHelm3() - Failed to detect Helm 3")
|
||||
}
|
||||
|
|
@ -1012,14 +1105,20 @@ func Test_GetPluginVersion(t *testing.T) {
|
|||
|
||||
func Test_GetVersion(t *testing.T) {
|
||||
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
|
||||
helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
ver := helm.GetVersion()
|
||||
if ver.Major != 2 || ver.Minor != 16 || ver.Patch != 1 {
|
||||
t.Errorf("helmexec.GetVersion - did not detect correct Helm2 version; it was: %+v", ver)
|
||||
}
|
||||
|
||||
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a\n")}
|
||||
helm = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
||||
helm, err = New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm3Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
ver = helm.GetVersion()
|
||||
if ver.Major != 3 || ver.Minor != 2 || ver.Patch != 4 {
|
||||
t.Errorf("helmexec.GetVersion - did not detect correct Helm3 version; it was: %+v", ver)
|
||||
|
|
@ -1028,7 +1127,10 @@ func Test_GetVersion(t *testing.T) {
|
|||
|
||||
func Test_IsVersionAtLeast(t *testing.T) {
|
||||
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
|
||||
helm := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
helm, err := New("helm", HelmExecOptions{}, NewLogger(os.Stdout, "info"), "", "dev", &helm2Runner)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !helm.IsVersionAtLeast("2.1.0") {
|
||||
t.Error("helmexec.IsVersionAtLeast - 2.16.1 not atleast 2.1")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ func Output(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError bool, l
|
|||
exitStatus := waitStatus.ExitStatus()
|
||||
err = newExitError(c.Path, c.Args, exitStatus, ee, stderr.String(), combined.String(), stripArgsValuesOnExitError)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected error: %v", err))
|
||||
err = fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/hashicorp/go-getter"
|
||||
"github.com/hashicorp/go-getter/helper/url"
|
||||
"go.uber.org/zap"
|
||||
|
|
@ -368,22 +367,22 @@ func (g *S3Getter) Get(wd, src, dst string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Create a new AWS session using the default AWS configuration
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
Config: aws.Config{
|
||||
Region: aws.String(region),
|
||||
},
|
||||
}))
|
||||
// Create a new AWS config and S3 client using AWS SDK v2
|
||||
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||
config.WithRegion(region),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create an S3 client using the session
|
||||
s3Client := s3.New(sess)
|
||||
// Create an S3 client using the config
|
||||
s3Client := s3.NewFromConfig(cfg)
|
||||
|
||||
getObjectInput := &s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
}
|
||||
resp, err := s3Client.GetObject(getObjectInput)
|
||||
resp, err := s3Client.GetObject(context.TODO(), getObjectInput)
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
|
|
@ -467,48 +466,47 @@ func (g *S3Getter) S3FileExists(path string) (string, error) {
|
|||
}
|
||||
|
||||
// Region
|
||||
g.Logger.Debugf("Creating session for determining S3 region %s", path)
|
||||
sess := session.Must(session.NewSessionWithOptions(session.Options{
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
}))
|
||||
g.Logger.Debugf("Creating config for determining S3 region %s", path)
|
||||
cfg, err := config.LoadDefaultConfig(context.TODO())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
g.Logger.Debugf("Getting bucket %s location %s", bucket, path)
|
||||
s3Client := s3.New(sess)
|
||||
s3Client := s3.NewFromConfig(cfg)
|
||||
bucketRegion := "us-east-1"
|
||||
getBucketLocationInput := &s3.GetBucketLocationInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Bucket: &bucket,
|
||||
}
|
||||
resp, err := s3Client.GetBucketLocation(getBucketLocationInput)
|
||||
resp, err := s3Client.GetBucketLocation(context.TODO(), getBucketLocationInput)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error: Failed to retrieve bucket location: %v\n", err)
|
||||
return "", fmt.Errorf("failed to retrieve bucket location: %v", err)
|
||||
}
|
||||
if resp == nil || resp.LocationConstraint == nil {
|
||||
if resp == nil || string(resp.LocationConstraint) == "" {
|
||||
g.Logger.Debugf("Bucket has no location Assuming us-east-1")
|
||||
} else {
|
||||
bucketRegion = *resp.LocationConstraint
|
||||
bucketRegion = string(resp.LocationConstraint)
|
||||
}
|
||||
g.Logger.Debugf("Got bucket location %s", bucketRegion)
|
||||
|
||||
// File existence
|
||||
g.Logger.Debugf("Creating new session with region to see if file exists")
|
||||
regionSession, err := session.NewSessionWithOptions(session.Options{
|
||||
SharedConfigState: session.SharedConfigEnable,
|
||||
Config: aws.Config{
|
||||
Region: aws.String(bucketRegion),
|
||||
},
|
||||
})
|
||||
g.Logger.Debugf("Creating new config with region to see if file exists")
|
||||
regionCfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||
config.WithRegion(bucketRegion),
|
||||
)
|
||||
if err != nil {
|
||||
g.Logger.Error(err)
|
||||
return bucketRegion, err
|
||||
}
|
||||
g.Logger.Debugf("Creating new s3 client to check if object exists")
|
||||
s3Client = s3.New(regionSession)
|
||||
s3Client = s3.NewFromConfig(regionCfg)
|
||||
headObjectInput := &s3.HeadObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &key,
|
||||
}
|
||||
|
||||
g.Logger.Debugf("Fethcing head %s", path)
|
||||
_, err = s3Client.HeadObject(headObjectInput)
|
||||
_, err = s3Client.HeadObject(context.TODO(), headObjectInput)
|
||||
return bucketRegion, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ const (
|
|||
DefaultHCLFileExtension = ".hcl"
|
||||
)
|
||||
|
||||
var ValidUpdateStrategyValues = []string{UpdateStrategyReinstallIfForbidden}
|
||||
|
||||
type StateLoadError struct {
|
||||
Msg string
|
||||
Cause error
|
||||
|
|
@ -43,6 +45,14 @@ func (e *UndefinedEnvError) Error() string {
|
|||
return fmt.Sprintf("environment \"%s\" is not defined", e.Env)
|
||||
}
|
||||
|
||||
type InvalidUpdateStrategyError struct {
|
||||
UpdateStrategy string
|
||||
}
|
||||
|
||||
func (e *InvalidUpdateStrategyError) Error() string {
|
||||
return fmt.Sprintf("updateStrategy %q is invalid, valid values are: %s or not set", e.UpdateStrategy, strings.Join(ValidUpdateStrategyValues, ", "))
|
||||
}
|
||||
|
||||
type StateCreator struct {
|
||||
logger *zap.SugaredLogger
|
||||
|
||||
|
|
@ -54,7 +64,7 @@ type StateCreator struct {
|
|||
|
||||
LoadFile func(inheritedEnv, overrodeEnv *environment.Environment, baseDir, file string, evaluateBases bool) (*HelmState, error)
|
||||
|
||||
getHelm func(*HelmState) helmexec.Interface
|
||||
getHelm func(*HelmState) (helmexec.Interface, error)
|
||||
|
||||
overrideHelmBinary string
|
||||
|
||||
|
|
@ -67,7 +77,7 @@ type StateCreator struct {
|
|||
lockFile string
|
||||
}
|
||||
|
||||
func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) helmexec.Interface, overrideHelmBinary string, overrideKustomizeBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator {
|
||||
func NewCreator(logger *zap.SugaredLogger, fs *filesystem.FileSystem, valsRuntime vals.Evaluator, getHelm func(*HelmState) (helmexec.Interface, error), overrideHelmBinary string, overrideKustomizeBinary string, remote *remote.Remote, enableLiveOutput bool, lockFile string) *StateCreator {
|
||||
return &StateCreator{
|
||||
logger: logger,
|
||||
|
||||
|
|
@ -116,11 +126,19 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
|
|||
return nil, &StateLoadError{fmt.Sprintf("failed to read %s: reading document at index %d", file, i), err}
|
||||
}
|
||||
|
||||
if err := mergo.Merge(&state, &intermediate, mergo.WithAppendSlice); err != nil {
|
||||
if err := mergo.Merge(&state, &intermediate, mergo.WithAppendSlice, mergo.WithOverride); err != nil {
|
||||
return nil, &StateLoadError{fmt.Sprintf("failed to read %s: merging document at index %d", file, i), err}
|
||||
}
|
||||
}
|
||||
|
||||
state.logger = c.logger
|
||||
state.valsRuntime = c.valsRuntime
|
||||
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
// applyDefaultsAndOverrides applies default binary paths and command-line overrides
|
||||
func (c *StateCreator) applyDefaultsAndOverrides(state *HelmState) {
|
||||
if c.overrideHelmBinary != "" && c.overrideHelmBinary != DefaultHelmBinary {
|
||||
state.DefaultHelmBinary = c.overrideHelmBinary
|
||||
} else if state.DefaultHelmBinary == "" {
|
||||
|
|
@ -134,11 +152,6 @@ func (c *StateCreator) Parse(content []byte, baseDir, file string) (*HelmState,
|
|||
// Let `helmfile --kustomize-binary ""` not break this helmfile run
|
||||
state.DefaultKustomizeBinary = DefaultKustomizeBinary
|
||||
}
|
||||
|
||||
state.logger = c.logger
|
||||
state.valsRuntime = c.valsRuntime
|
||||
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
// LoadEnvValues loads environment values files relative to the `baseDir`
|
||||
|
|
@ -181,6 +194,11 @@ func (c *StateCreator) ParseAndLoad(content []byte, baseDir, file string, envNam
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply default binaries and command-line overrides only for the main helmfile
|
||||
// after loading and merging all bases. This ensures that values from bases are
|
||||
// properly respected and that later bases/documents can override earlier ones.
|
||||
c.applyDefaultsAndOverrides(state)
|
||||
}
|
||||
|
||||
state, err = c.LoadEnvValues(state, envName, failOnMissingEnv, envValues, overrode)
|
||||
|
|
@ -216,7 +234,7 @@ func (c *StateCreator) loadBases(envValues, overrodeEnv *environment.Environment
|
|||
layers = append(layers, st)
|
||||
|
||||
for i := 1; i < len(layers); i++ {
|
||||
if err := mergo.Merge(layers[0], layers[i], mergo.WithAppendSlice); err != nil {
|
||||
if err := mergo.Merge(layers[0], layers[i], mergo.WithAppendSlice, mergo.WithOverride); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
@ -342,7 +360,10 @@ func (c *StateCreator) loadEnvValues(st *HelmState, name string, failOnMissingEn
|
|||
func (c *StateCreator) scatterGatherEnvSecretFiles(st *HelmState, envSecretFiles []string, envVals map[string]any, keepFileExtensions []string) ([]string, error) {
|
||||
var errs []error
|
||||
var decryptedFilesKeeper []string
|
||||
helm := c.getHelm(st)
|
||||
helm, err := c.getHelm(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputs := envSecretFiles
|
||||
inputsSize := len(inputs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
|
@ -525,3 +526,205 @@ releaseContext:
|
|||
t.Errorf("unexpected values: expected=%v, actual=%v", expectedValues, actualValues)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHelmBinaryInBases tests that helmBinary and kustomizeBinary settings
|
||||
// from bases are properly merged with later values overriding earlier ones
|
||||
func TestHelmBinaryInBases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
files map[string]string
|
||||
mainFile string
|
||||
expectedHelmBinary string
|
||||
expectedKustomizeBinary string
|
||||
}{
|
||||
{
|
||||
name: "helmBinary in second base should be used",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/env.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/repos.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/releases.yaml
|
||||
`,
|
||||
"/path/to/bases/env.yaml": `environments:
|
||||
default:
|
||||
values:
|
||||
- key: value1
|
||||
`,
|
||||
"/path/to/bases/repos.yaml": `repositories:
|
||||
- name: stable
|
||||
url: https://charts.helm.sh/stable
|
||||
helmBinary: /path/to/custom/helm
|
||||
`,
|
||||
"/path/to/bases/releases.yaml": `releases:
|
||||
- name: myapp
|
||||
chart: stable/nginx
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: "/path/to/custom/helm",
|
||||
expectedKustomizeBinary: DefaultKustomizeBinary,
|
||||
},
|
||||
{
|
||||
name: "helmBinary in main file after bases should override",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/env.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/repos.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/releases.yaml
|
||||
helmBinary: /path/to/main/helm
|
||||
`,
|
||||
"/path/to/bases/env.yaml": `environments:
|
||||
default:
|
||||
values:
|
||||
- key: value1
|
||||
`,
|
||||
"/path/to/bases/repos.yaml": `repositories:
|
||||
- name: stable
|
||||
url: https://charts.helm.sh/stable
|
||||
helmBinary: /path/to/base/helm
|
||||
`,
|
||||
"/path/to/bases/releases.yaml": `releases:
|
||||
- name: myapp
|
||||
chart: stable/nginx
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: "/path/to/main/helm",
|
||||
expectedKustomizeBinary: DefaultKustomizeBinary,
|
||||
},
|
||||
{
|
||||
name: "helmBinary in main file between bases should override earlier bases",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/env.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/repos.yaml
|
||||
helmBinary: /path/to/middle/helm
|
||||
---
|
||||
bases:
|
||||
- ./bases/releases.yaml
|
||||
`,
|
||||
"/path/to/bases/env.yaml": `environments:
|
||||
default:
|
||||
values:
|
||||
- key: value1
|
||||
`,
|
||||
"/path/to/bases/repos.yaml": `repositories:
|
||||
- name: stable
|
||||
url: https://charts.helm.sh/stable
|
||||
helmBinary: /path/to/base/helm
|
||||
`,
|
||||
"/path/to/bases/releases.yaml": `releases:
|
||||
- name: myapp
|
||||
chart: stable/nginx
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: "/path/to/middle/helm",
|
||||
expectedKustomizeBinary: DefaultKustomizeBinary,
|
||||
},
|
||||
{
|
||||
name: "kustomizeBinary in base should be used",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/base.yaml
|
||||
`,
|
||||
"/path/to/bases/base.yaml": `kustomizeBinary: /path/to/custom/kustomize
|
||||
releases:
|
||||
- name: myapp
|
||||
chart: mychart
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: DefaultHelmBinary,
|
||||
expectedKustomizeBinary: "/path/to/custom/kustomize",
|
||||
},
|
||||
{
|
||||
name: "both helmBinary and kustomizeBinary in different bases",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/helm.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/kustomize.yaml
|
||||
`,
|
||||
"/path/to/bases/helm.yaml": `helmBinary: /path/to/custom/helm
|
||||
`,
|
||||
"/path/to/bases/kustomize.yaml": `kustomizeBinary: /path/to/custom/kustomize
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: "/path/to/custom/helm",
|
||||
expectedKustomizeBinary: "/path/to/custom/kustomize",
|
||||
},
|
||||
{
|
||||
name: "later base overrides earlier base for helmBinary",
|
||||
files: map[string]string{
|
||||
"/path/to/helmfile.yaml": `bases:
|
||||
- ./bases/first.yaml
|
||||
---
|
||||
bases:
|
||||
- ./bases/second.yaml
|
||||
`,
|
||||
"/path/to/bases/first.yaml": `helmBinary: /path/to/first/helm
|
||||
`,
|
||||
"/path/to/bases/second.yaml": `helmBinary: /path/to/second/helm
|
||||
`,
|
||||
},
|
||||
mainFile: "/path/to/helmfile.yaml",
|
||||
expectedHelmBinary: "/path/to/second/helm",
|
||||
expectedKustomizeBinary: DefaultKustomizeBinary,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testFs := testhelper.NewTestFs(tt.files)
|
||||
if testFs.Cwd == "" {
|
||||
testFs.Cwd = "/"
|
||||
}
|
||||
|
||||
r := remote.NewRemote(logger, testFs.Cwd, testFs.ToFileSystem())
|
||||
creator := NewCreator(logger, testFs.ToFileSystem(), nil, nil, "", "", r, false, "")
|
||||
|
||||
// Set up LoadFile for recursive base loading
|
||||
creator.LoadFile = func(inheritedEnv, overrodeEnv *environment.Environment, baseDir, file string, evaluateBases bool) (*HelmState, error) {
|
||||
path := filepath.Join(baseDir, file)
|
||||
content, ok := tt.files[path]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("file not found: %s", path)
|
||||
}
|
||||
return creator.ParseAndLoad([]byte(content), filepath.Dir(path), path, DefaultEnv, true, evaluateBases, inheritedEnv, overrodeEnv)
|
||||
}
|
||||
|
||||
yamlContent, ok := tt.files[tt.mainFile]
|
||||
if !ok {
|
||||
t.Fatalf("no file named %q registered", tt.mainFile)
|
||||
}
|
||||
|
||||
state, err := creator.ParseAndLoad([]byte(yamlContent), filepath.Dir(tt.mainFile), tt.mainFile, DefaultEnv, true, true, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if state.DefaultHelmBinary != tt.expectedHelmBinary {
|
||||
t.Errorf("helmBinary mismatch: expected=%s, actual=%s",
|
||||
tt.expectedHelmBinary, state.DefaultHelmBinary)
|
||||
}
|
||||
|
||||
if state.DefaultKustomizeBinary != tt.expectedKustomizeBinary {
|
||||
t.Errorf("kustomizeBinary mismatch: expected=%s, actual=%s",
|
||||
tt.expectedKustomizeBinary, state.DefaultKustomizeBinary)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/helmfile/chartify"
|
||||
|
|
@ -158,31 +157,17 @@ func (st *HelmState) appendWaitForJobsFlags(flags []string, release *ReleaseSpec
|
|||
return flags
|
||||
}
|
||||
|
||||
func (st *HelmState) appendWaitFlags(flags []string, helm helmexec.Interface, release *ReleaseSpec, ops *SyncOpts) []string {
|
||||
var hasWait bool
|
||||
func (st *HelmState) appendWaitFlags(flags []string, release *ReleaseSpec, ops *SyncOpts) []string {
|
||||
switch {
|
||||
case release.Wait != nil && *release.Wait:
|
||||
hasWait = true
|
||||
flags = append(flags, "--wait")
|
||||
case ops != nil && ops.Wait:
|
||||
hasWait = true
|
||||
flags = append(flags, "--wait")
|
||||
case release.Wait == nil && st.HelmDefaults.Wait:
|
||||
hasWait = true
|
||||
flags = append(flags, "--wait")
|
||||
}
|
||||
// see https://github.com/helm/helm/releases/tag/v3.15.0
|
||||
// https://github.com/helm/helm/commit/fc74964
|
||||
if hasWait && helm.IsVersionAtLeast("3.15.0") {
|
||||
switch {
|
||||
case release.WaitRetries != nil && *release.WaitRetries > 0:
|
||||
flags = append(flags, "--wait-retries", strconv.Itoa(*release.WaitRetries))
|
||||
case ops != nil && ops.WaitRetries > 0:
|
||||
flags = append(flags, "--wait-retries", strconv.Itoa(ops.WaitRetries))
|
||||
case release.WaitRetries == nil && st.HelmDefaults.WaitRetries > 0:
|
||||
flags = append(flags, "--wait-retries", strconv.Itoa(st.HelmDefaults.WaitRetries))
|
||||
}
|
||||
}
|
||||
// Note: --wait-retries flag has been removed from Helm and is no longer supported
|
||||
// WaitRetries configuration is preserved for backward compatibility but ignored
|
||||
return flags
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name string
|
||||
release *ReleaseSpec
|
||||
syncOpts *SyncOpts
|
||||
helm helmexec.Interface
|
||||
helmSpec HelmSpec
|
||||
expected []string
|
||||
}{
|
||||
|
|
@ -85,7 +84,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "release wait",
|
||||
release: &ReleaseSpec{Wait: &[]bool{true}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
|
|
@ -93,7 +91,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "cli flags wait",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: &SyncOpts{Wait: true},
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
|
|
@ -101,7 +98,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "helm defaults wait",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{Wait: true},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
|
|
@ -109,7 +105,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "release wait false",
|
||||
release: &ReleaseSpec{Wait: &[]bool{false}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{Wait: true},
|
||||
expected: []string{},
|
||||
},
|
||||
|
|
@ -117,7 +112,6 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "cli flags wait false",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: &SyncOpts{},
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{Wait: true},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
|
|
@ -125,66 +119,58 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
name: "helm defaults wait false",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{Wait: false},
|
||||
expected: []string{},
|
||||
},
|
||||
// --wait-retries
|
||||
// --wait-retries flag has been removed from Helm
|
||||
{
|
||||
name: "release wait and retry unsupported",
|
||||
release: &ReleaseSpec{Wait: &[]bool{true}[0], WaitRetries: &[]int{1}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.11.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
{
|
||||
name: "release wait and retry supported",
|
||||
name: "release wait and retry - retries ignored",
|
||||
release: &ReleaseSpec{Wait: &[]bool{true}[0], WaitRetries: &[]int{1}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{"--wait", "--wait-retries", "1"},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
{
|
||||
name: "no wait retry",
|
||||
release: &ReleaseSpec{WaitRetries: &[]int{1}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
name: "cli flags wait and retry",
|
||||
name: "cli flags wait and retry - retries ignored",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: &SyncOpts{Wait: true, WaitRetries: 2},
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{},
|
||||
expected: []string{"--wait", "--wait-retries", "2"},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
{
|
||||
name: "helm defaults wait retry",
|
||||
name: "helm defaults wait retry - retries ignored",
|
||||
release: &ReleaseSpec{},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{Wait: true, WaitRetries: 3},
|
||||
expected: []string{"--wait", "--wait-retries", "3"},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
{
|
||||
name: "release wait default retries",
|
||||
name: "release wait default retries - retries ignored",
|
||||
release: &ReleaseSpec{Wait: &[]bool{true}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{WaitRetries: 4},
|
||||
expected: []string{"--wait", "--wait-retries", "4"},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
{
|
||||
name: "release retries default wait",
|
||||
name: "release retries default wait - retries ignored",
|
||||
release: &ReleaseSpec{WaitRetries: &[]int{5}[0]},
|
||||
syncOpts: nil,
|
||||
helm: testutil.NewVersionHelmExec("3.15.0"),
|
||||
helmSpec: HelmSpec{Wait: true},
|
||||
expected: []string{"--wait", "--wait-retries", "5"},
|
||||
expected: []string{"--wait"},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +178,7 @@ func TestAppendWaitFlags(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
st := &HelmState{}
|
||||
st.HelmDefaults = tt.helmSpec
|
||||
got := st.appendWaitFlags([]string{}, tt.helm, tt.release, tt.syncOpts)
|
||||
got := st.appendWaitFlags([]string{}, tt.release, tt.syncOpts)
|
||||
require.Equalf(t, tt.expected, got, "appendWaitFlags() = %v, want %v", got, tt.expected)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ const (
|
|||
// This is used by an interim solution to make the urfave/cli command report to the helmfile internal about that the
|
||||
// --timeout flag is missingl
|
||||
EmptyTimeout = -1
|
||||
|
||||
// Valid enum for updateStrategy values
|
||||
UpdateStrategyReinstallIfForbidden = "reinstallIfForbidden"
|
||||
)
|
||||
|
||||
// ReleaseSetSpec is release set spec
|
||||
|
|
@ -163,6 +166,7 @@ type HelmSpec struct {
|
|||
// Wait, if set to true, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful
|
||||
Wait bool `yaml:"wait"`
|
||||
// WaitRetries, if set and --wait enabled, will retry any failed check on resource state, except if HTTP status code < 500 is received, subject to the specified number of retries
|
||||
// DEPRECATED: This field is ignored as the --wait-retries flag was removed from Helm. Preserved for backward compatibility.
|
||||
WaitRetries int `yaml:"waitRetries"`
|
||||
// WaitForJobs, if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout
|
||||
WaitForJobs bool `yaml:"waitForJobs"`
|
||||
|
|
@ -264,6 +268,7 @@ type ReleaseSpec struct {
|
|||
// Wait, if set to true, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful
|
||||
Wait *bool `yaml:"wait,omitempty"`
|
||||
// WaitRetries, if set and --wait enabled, will retry any failed check on resource state, except if HTTP status code < 500 is received, subject to the specified number of retries
|
||||
// DEPRECATED: This field is ignored as the --wait-retries flag was removed from Helm. Preserved for backward compatibility.
|
||||
WaitRetries *int `yaml:"waitRetries,omitempty"`
|
||||
// WaitForJobs, if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout
|
||||
WaitForJobs *bool `yaml:"waitForJobs,omitempty"`
|
||||
|
|
@ -275,6 +280,8 @@ type ReleaseSpec struct {
|
|||
Force *bool `yaml:"force,omitempty"`
|
||||
// Installed, when set to true, `delete --purge` the release
|
||||
Installed *bool `yaml:"installed,omitempty"`
|
||||
// UpdateStrategy, when set, indicate the strategy to use to update the release
|
||||
UpdateStrategy string `yaml:"updateStrategy,omitempty"`
|
||||
// Atomic, when set to true, restore previous state in case of a failed install/upgrade attempt
|
||||
Atomic *bool `yaml:"atomic,omitempty"`
|
||||
// CleanupOnFail, when set to true, the --cleanup-on-fail helm flag is passed to the upgrade command
|
||||
|
|
@ -465,6 +472,7 @@ type SetValue struct {
|
|||
// AffectedReleases hold the list of released that where updated, deleted, or in error
|
||||
type AffectedReleases struct {
|
||||
Upgraded []*ReleaseSpec
|
||||
Reinstalled []*ReleaseSpec
|
||||
Deleted []*ReleaseSpec
|
||||
Failed []*ReleaseSpec
|
||||
DeleteFailed []*ReleaseSpec
|
||||
|
|
@ -1035,7 +1043,10 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
|
|||
}
|
||||
m.Unlock()
|
||||
}
|
||||
} else if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
} else if release.UpdateStrategy == UpdateStrategyReinstallIfForbidden {
|
||||
relErr = st.performSyncOrReinstallOfRelease(affectedReleases, helm, context, release, chart, m, flags...)
|
||||
} else {
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
|
|
@ -1051,6 +1062,7 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
|
|||
release.installedVersion = installedVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := st.triggerPostsyncEvent(release, relErr, "sync"); err != nil {
|
||||
if relErr == nil {
|
||||
|
|
@ -1094,6 +1106,77 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
|
|||
return nil
|
||||
}
|
||||
|
||||
func (st *HelmState) performSyncOrReinstallOfRelease(affectedReleases *AffectedReleases, helm helmexec.Interface, context helmexec.HelmContext, release *ReleaseSpec, chart string, m *sync.Mutex, flags ...string) *ReleaseError {
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
st.logger.Debugf("update strategy - sync failed: %s", err.Error())
|
||||
// Only fail if a different error than forbidden updates
|
||||
if !strings.Contains(err.Error(), "Forbidden: updates") {
|
||||
st.logger.Debugf("update strategy - sync failed not due to Forbidden updates")
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
return newReleaseFailedError(release, err)
|
||||
}
|
||||
} else {
|
||||
st.logger.Debugf("update strategy - sync success")
|
||||
m.Lock()
|
||||
affectedReleases.Upgraded = append(affectedReleases.Upgraded, release)
|
||||
m.Unlock()
|
||||
installedVersion, err := st.getDeployedVersion(context, helm, release)
|
||||
if err != nil { // err is not really impacting so just log it
|
||||
st.logger.Debugf("update strategy - getting deployed release version failed: %v", err)
|
||||
} else {
|
||||
release.installedVersion = installedVersion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
st.logger.Infof("Failed to sync due to forbidden updates, attempting to reinstall %q allowed by update strategy", release.Name)
|
||||
installed, err := st.isReleaseInstalled(context, helm, *release)
|
||||
if err != nil {
|
||||
return newReleaseFailedError(release, err)
|
||||
}
|
||||
if installed {
|
||||
var args []string
|
||||
if release.Namespace != "" {
|
||||
args = append(args, "--namespace", release.Namespace)
|
||||
}
|
||||
deleteWaitFlag := true
|
||||
release.DeleteWait = &deleteWaitFlag
|
||||
args = st.appendDeleteWaitFlags(args, release)
|
||||
deletionFlags := st.appendConnectionFlags(args, release)
|
||||
m.Lock()
|
||||
if _, err := st.triggerReleaseEvent("preuninstall", nil, release, "sync"); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
return newReleaseFailedError(release, err)
|
||||
} else if err := helm.DeleteRelease(context, release.Name, deletionFlags...); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
return newReleaseFailedError(release, err)
|
||||
} else if _, err := st.triggerReleaseEvent("postuninstall", nil, release, "sync"); err != nil {
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
return newReleaseFailedError(release, err)
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
if err := helm.SyncRelease(context, release.Name, chart, release.Namespace, flags...); err != nil {
|
||||
m.Lock()
|
||||
affectedReleases.Failed = append(affectedReleases.Failed, release)
|
||||
m.Unlock()
|
||||
return newReleaseFailedError(release, err)
|
||||
} else {
|
||||
m.Lock()
|
||||
affectedReleases.Reinstalled = append(affectedReleases.Reinstalled, release)
|
||||
m.Unlock()
|
||||
installedVersion, err := st.getDeployedVersion(context, helm, release)
|
||||
if err != nil { // err is not really impacting so just log it
|
||||
st.logger.Debugf("update strategy - getting deployed release version failed: %v", err)
|
||||
} else {
|
||||
release.installedVersion = installedVersion
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *HelmState) listReleases(context helmexec.HelmContext, helm helmexec.Interface, release *ReleaseSpec) (string, error) {
|
||||
flags := st.kubeConnectionFlags(release)
|
||||
if release.Namespace != "" {
|
||||
|
|
@ -1137,6 +1220,16 @@ func releasesNeedCharts(releases []ReleaseSpec) []ReleaseSpec {
|
|||
return result
|
||||
}
|
||||
|
||||
func filterReleasesForBuild(releases []ReleaseSpec) []ReleaseSpec {
|
||||
var filteredReleases []ReleaseSpec
|
||||
for _, r := range releases {
|
||||
if len(r.JSONPatches) == 0 && len(r.StrategicMergePatches) == 0 && len(r.Transformers) == 0 {
|
||||
filteredReleases = append(filteredReleases, r)
|
||||
}
|
||||
}
|
||||
return filteredReleases
|
||||
}
|
||||
|
||||
type ChartPrepareOptions struct {
|
||||
ForceDownload bool
|
||||
SkipRepos bool
|
||||
|
|
@ -1189,6 +1282,19 @@ func (st *HelmState) GetRepositoryAndNameFromChartName(chartName string) (*Repos
|
|||
return nil, chartName
|
||||
}
|
||||
|
||||
var mutexMap sync.Map
|
||||
|
||||
// retrieves or creates a sync.Mutex for a given name
|
||||
func (st *HelmState) getNamedMutex(name string) *sync.Mutex {
|
||||
mu, ok := mutexMap.Load(name)
|
||||
if ok {
|
||||
return mu.(*sync.Mutex)
|
||||
}
|
||||
newMu := &sync.Mutex{}
|
||||
actualMu, _ := mutexMap.LoadOrStore(name, newMu)
|
||||
return actualMu.(*sync.Mutex)
|
||||
}
|
||||
|
||||
type PrepareChartKey struct {
|
||||
Namespace, Name, KubeContext string
|
||||
}
|
||||
|
|
@ -1207,100 +1313,8 @@ type PrepareChartKey struct {
|
|||
// Otheriwse, if a chart is not a helm chart, it will call "chartify" to turn it into a chart.
|
||||
//
|
||||
// If exists, it will also patch resources by json patches, strategic-merge patches, and injectors.
|
||||
func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string, opts ChartPrepareOptions) (map[PrepareChartKey]string, []error) {
|
||||
if !opts.SkipResolve {
|
||||
updated, err := st.ResolveDeps()
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
*st = *updated
|
||||
}
|
||||
selected, err := st.GetSelectedReleases(opts.IncludeTransitiveNeeds)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
releases := releasesNeedCharts(selected)
|
||||
|
||||
var prepareChartInfoMutex sync.Mutex
|
||||
|
||||
prepareChartInfo := make(map[PrepareChartKey]string, len(releases))
|
||||
|
||||
errs := []error{}
|
||||
|
||||
jobQueue := make(chan *ReleaseSpec, len(releases))
|
||||
results := make(chan *chartPrepareResult, len(releases))
|
||||
|
||||
var builds []*chartPrepareResult
|
||||
|
||||
st.scatterGather(
|
||||
concurrency,
|
||||
len(releases),
|
||||
func() {
|
||||
for i := 0; i < len(releases); i++ {
|
||||
jobQueue <- &releases[i]
|
||||
}
|
||||
close(jobQueue)
|
||||
},
|
||||
func(workerIndex int) {
|
||||
for release := range jobQueue {
|
||||
if st.OverrideChart != "" {
|
||||
release.Chart = st.OverrideChart
|
||||
}
|
||||
// Call user-defined `prepare` hooks to create/modify local charts to be used by
|
||||
// the later process.
|
||||
//
|
||||
// If it wasn't called here, Helmfile can end up an issue like
|
||||
// https://github.com/roboll/helmfile/issues/1328
|
||||
if _, err := st.triggerPrepareEvent(release, helmfileCommand); err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
chartName := release.Chart
|
||||
|
||||
chartPath, err := st.downloadChartWithGoGetter(release)
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||
return
|
||||
}
|
||||
chartFetchedByGoGetter := chartPath != chartName
|
||||
|
||||
if !chartFetchedByGoGetter {
|
||||
ociChartPath, err := st.getOCIChart(release, dir, helm, opts.OutputDirTemplate)
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ociChartPath != nil {
|
||||
chartPath = *ociChartPath
|
||||
}
|
||||
}
|
||||
|
||||
isLocal := st.fs.DirectoryExistsAt(normalizeChart(st.basePath, chartName))
|
||||
|
||||
chartification, clean, err := st.PrepareChartify(helm, release, chartPath, workerIndex)
|
||||
|
||||
if !opts.SkipCleanup {
|
||||
// nolint: staticcheck
|
||||
defer clean()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
var buildDeps bool
|
||||
|
||||
skipDepsGlobal := opts.SkipDeps
|
||||
skipDepsRelease := release.SkipDeps != nil && *release.SkipDeps
|
||||
skipDepsDefault := release.SkipDeps == nil && st.HelmDefaults.SkipDeps
|
||||
skipDeps := (!isLocal && !chartFetchedByGoGetter) || skipDepsGlobal || skipDepsRelease || skipDepsDefault
|
||||
|
||||
if chartification != nil && helmfileCommand != "pull" {
|
||||
// processChartification handles the chartification process
|
||||
func (st *HelmState) processChartification(chartification *Chartify, release *ReleaseSpec, chartPath string, opts ChartPrepareOptions, skipDeps bool) (string, bool, error) {
|
||||
c := chartify.New(
|
||||
chartify.HelmBin(st.DefaultHelmBinary),
|
||||
chartify.KustomizeBin(st.DefaultKustomizeBinary),
|
||||
|
|
@ -1339,53 +1353,135 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
|||
|
||||
out, err := c.Chartify(release.Name, chartPath, chartify.WithChartifyOpts(chartifyOpts))
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
} else {
|
||||
chartPath = out
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
chartPath = out
|
||||
// Skip `helm dep build` and `helm dep up` altogether when the chart is from remote or the dep is
|
||||
// explicitly skipped.
|
||||
buildDeps = !skipDeps
|
||||
} else if normalizedChart := normalizeChart(st.basePath, chartPath); st.fs.DirectoryExistsAt(normalizedChart) {
|
||||
// At this point, we are sure that chartPath is a local directory containing either:
|
||||
// - A remote chart fetched by go-getter or
|
||||
// - A local chart
|
||||
//
|
||||
// The chart may have Chart.yaml(and requirements.yaml for Helm 2), and optionally Chart.lock/requirements.lock,
|
||||
// but no `charts/` directory populated at all, or a subet of chart dependencies are missing in the directory.
|
||||
//
|
||||
// In such situation, Helm fails with an error like:
|
||||
// Error: found in Chart.yaml, but missing in charts/ directory: cert-manager, prometheus, postgresql, gitlab-runner, grafana, redis
|
||||
//
|
||||
// (See also https://github.com/roboll/helmfile/issues/1401#issuecomment-670854495)
|
||||
//
|
||||
// To avoid it, we need to call a `helm dep build` command on the chart.
|
||||
// But the command may consistently fail when an outdated Chart.lock exists.
|
||||
//
|
||||
// (I've mentioned about such case in https://github.com/roboll/helmfile/pull/1400.)
|
||||
//
|
||||
// Trying to run `helm dep build` on the chart regardless of if it's from local or remote is
|
||||
// problematic, as usually the user would have no way to fix the remote chart on their own.
|
||||
//
|
||||
// Given that, we always run `helm dep build` on the chart here, but tolerate any error caused by it
|
||||
// for a remote chart, so that the user can notice/fix the issue in a local chart while
|
||||
// a broken remote chart won't completely block their job.
|
||||
chartPath = normalizedChart
|
||||
buildDeps := !skipDeps
|
||||
return chartPath, buildDeps, nil
|
||||
}
|
||||
|
||||
// processLocalChart handles local chart processing
|
||||
func (st *HelmState) processLocalChart(normalizedChart, dir string, release *ReleaseSpec, helmfileCommand string, opts ChartPrepareOptions, isLocal bool) (string, error) {
|
||||
chartPath := normalizedChart
|
||||
if helmfileCommand == "pull" && isLocal {
|
||||
chartAbsPath := strings.TrimSuffix(filepath.Clean(normalizedChart), "/")
|
||||
var err error
|
||||
chartPath, err = generateChartPath(filepath.Base(chartAbsPath), dir, release, opts.OutputDirTemplate)
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
if err := st.fs.CopyDir(normalizedChart, filepath.Clean(chartPath)); err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return chartPath, nil
|
||||
}
|
||||
|
||||
// forcedDownloadChart handles forced chart downloads
|
||||
func (st *HelmState) forcedDownloadChart(chartName, dir string, release *ReleaseSpec, helm helmexec.Interface, opts ChartPrepareOptions) (string, error) {
|
||||
// Check global chart cache first for non-OCI charts
|
||||
cacheKey := st.getChartCacheKey(release)
|
||||
if cachedPath, exists := st.checkChartCache(cacheKey); exists && st.fs.DirectoryExistsAt(cachedPath) {
|
||||
st.logger.Debugf("Chart %s:%s already downloaded, using cached version at %s", chartName, release.Version, cachedPath)
|
||||
return cachedPath, nil
|
||||
}
|
||||
|
||||
chartPath, err := generateChartPath(chartName, dir, release, opts.OutputDirTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// only fetch chart if it is not already fetched
|
||||
if _, err := os.Stat(chartPath); os.IsNotExist(err) {
|
||||
var fetchFlags []string
|
||||
fetchFlags = st.appendChartVersionFlags(fetchFlags, release)
|
||||
fetchFlags = append(fetchFlags, "--untar", "--untardir", chartPath)
|
||||
if err := helm.Fetch(chartName, fetchFlags...); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
st.logger.Infof("\"%s\" has not been downloaded because the output directory \"%s\" already exists", chartName, chartPath)
|
||||
}
|
||||
|
||||
// Set chartPath to be the path containing Chart.yaml, if found
|
||||
fullChartPath, err := findChartDirectory(chartPath)
|
||||
if err == nil {
|
||||
chartPath = filepath.Dir(fullChartPath)
|
||||
}
|
||||
|
||||
// Add to global chart cache
|
||||
st.addToChartCache(cacheKey, chartPath)
|
||||
|
||||
return chartPath, nil
|
||||
}
|
||||
|
||||
// prepareChartForRelease processes a single release and prepares its chart
|
||||
func (st *HelmState) prepareChartForRelease(release *ReleaseSpec, helm helmexec.Interface, dir string, helmfileCommand string, opts ChartPrepareOptions, workerIndex int) *chartPrepareResult {
|
||||
if st.OverrideChart != "" {
|
||||
release.Chart = st.OverrideChart
|
||||
}
|
||||
|
||||
// Call user-defined `prepare` hooks to create/modify local charts to be used by
|
||||
// the later process.
|
||||
//
|
||||
// If it wasn't called here, Helmfile can end up an issue like
|
||||
// https://github.com/roboll/helmfile/issues/1328
|
||||
if _, err := st.triggerPrepareEvent(release, helmfileCommand); err != nil {
|
||||
return &chartPrepareResult{err: err}
|
||||
}
|
||||
|
||||
chartName := release.Chart
|
||||
|
||||
chartPath, err := st.downloadChartWithGoGetter(release)
|
||||
if err != nil {
|
||||
return &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||
}
|
||||
chartFetchedByGoGetter := chartPath != chartName
|
||||
|
||||
if !chartFetchedByGoGetter {
|
||||
ociChartPath, err := st.getOCIChart(release, dir, helm, opts)
|
||||
if err != nil {
|
||||
return &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||
}
|
||||
|
||||
if ociChartPath != nil {
|
||||
chartPath = *ociChartPath
|
||||
}
|
||||
}
|
||||
|
||||
isLocal := st.fs.DirectoryExistsAt(normalizeChart(st.basePath, chartName))
|
||||
|
||||
chartification, clean, err := st.PrepareChartify(helm, release, chartPath, workerIndex)
|
||||
|
||||
if !opts.SkipCleanup {
|
||||
// nolint: staticcheck
|
||||
defer clean()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return &chartPrepareResult{err: err}
|
||||
}
|
||||
|
||||
var buildDeps bool
|
||||
|
||||
skipDepsGlobal := opts.SkipDeps
|
||||
skipDepsRelease := release.SkipDeps != nil && *release.SkipDeps
|
||||
skipDepsDefault := release.SkipDeps == nil && st.HelmDefaults.SkipDeps
|
||||
skipDeps := (!isLocal && !chartFetchedByGoGetter) || skipDepsGlobal || skipDepsRelease || skipDepsDefault
|
||||
|
||||
if chartification != nil && helmfileCommand != "pull" {
|
||||
chartPath, buildDeps, err = st.processChartification(chartification, release, chartPath, opts, skipDeps)
|
||||
if err != nil {
|
||||
return &chartPrepareResult{err: err}
|
||||
}
|
||||
} else if normalizedChart := normalizeChart(st.basePath, chartPath); st.fs.DirectoryExistsAt(normalizedChart) {
|
||||
chartPath, err = st.processLocalChart(normalizedChart, dir, release, helmfileCommand, opts, isLocal)
|
||||
if err != nil {
|
||||
return &chartPrepareResult{err: err}
|
||||
}
|
||||
buildDeps = !skipDeps
|
||||
} else if !opts.ForceDownload {
|
||||
// At this point, we are sure that either:
|
||||
|
|
@ -1400,33 +1496,13 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
|||
// For helm 2, we `helm fetch` with the version flags and call `helm template`
|
||||
// WITHOUT the version flags.
|
||||
} else {
|
||||
chartPath, err = generateChartPath(chartName, dir, release, opts.OutputDirTemplate)
|
||||
chartPath, err = st.forcedDownloadChart(chartName, dir, release, helm, opts)
|
||||
if err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
}
|
||||
|
||||
// only fetch chart if it is not already fetched
|
||||
if _, err := os.Stat(chartPath); os.IsNotExist(err) {
|
||||
var fetchFlags []string
|
||||
fetchFlags = st.appendChartVersionFlags(fetchFlags, release)
|
||||
fetchFlags = append(fetchFlags, "--untar", "--untardir", chartPath)
|
||||
if err := helm.Fetch(chartName, fetchFlags...); err != nil {
|
||||
results <- &chartPrepareResult{err: err}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
st.logger.Infof("\"%s\" has not been downloaded because the output directory \"%s\" already exists", chartName, chartPath)
|
||||
}
|
||||
|
||||
// Set chartPath to be the path containing Chart.yaml, if found
|
||||
fullChartPath, err := findChartDirectory(chartPath)
|
||||
if err == nil {
|
||||
chartPath = filepath.Dir(fullChartPath)
|
||||
return &chartPrepareResult{err: err}
|
||||
}
|
||||
}
|
||||
|
||||
results <- &chartPrepareResult{
|
||||
return &chartPrepareResult{
|
||||
releaseName: release.Name,
|
||||
chartName: chartName,
|
||||
releaseNamespace: release.Namespace,
|
||||
|
|
@ -1436,6 +1512,53 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
|||
skipRefresh: !isLocal || opts.SkipRefresh,
|
||||
chartFetchedByGoGetter: chartFetchedByGoGetter,
|
||||
}
|
||||
}
|
||||
|
||||
func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurrency int, helmfileCommand string, opts ChartPrepareOptions) (map[PrepareChartKey]string, []error) {
|
||||
if !opts.SkipResolve {
|
||||
updated, err := st.ResolveDeps()
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
*st = *updated
|
||||
}
|
||||
selected, err := st.GetSelectedReleases(opts.IncludeTransitiveNeeds)
|
||||
if err != nil {
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
releases := releasesNeedCharts(selected)
|
||||
|
||||
// For build command, skip releases that require chartify (jsonPatches, etc.)
|
||||
// as we only need to output state, not actually template the charts
|
||||
if helmfileCommand == "build" {
|
||||
releases = filterReleasesForBuild(releases)
|
||||
}
|
||||
|
||||
var prepareChartInfoMutex sync.Mutex
|
||||
|
||||
prepareChartInfo := make(map[PrepareChartKey]string, len(releases))
|
||||
|
||||
errs := []error{}
|
||||
|
||||
jobQueue := make(chan *ReleaseSpec, len(releases))
|
||||
results := make(chan *chartPrepareResult, len(releases))
|
||||
|
||||
var builds []*chartPrepareResult
|
||||
|
||||
st.scatterGather(
|
||||
concurrency,
|
||||
len(releases),
|
||||
func() {
|
||||
for i := 0; i < len(releases); i++ {
|
||||
jobQueue <- &releases[i]
|
||||
}
|
||||
close(jobQueue)
|
||||
},
|
||||
func(workerIndex int) {
|
||||
for release := range jobQueue {
|
||||
result := st.prepareChartForRelease(release, helm, dir, helmfileCommand, opts, workerIndex)
|
||||
results <- result
|
||||
}
|
||||
},
|
||||
func() {
|
||||
|
|
@ -2797,7 +2920,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp
|
|||
flags = st.appendChartVersionFlags(flags, release)
|
||||
flags = st.appendEnableDNSFlags(flags, release)
|
||||
|
||||
flags = st.appendWaitFlags(flags, helm, release, opt)
|
||||
flags = st.appendWaitFlags(flags, release, opt)
|
||||
flags = st.appendWaitForJobsFlags(flags, release, opt)
|
||||
|
||||
// non-OCI chart should be verified here
|
||||
|
|
@ -3693,6 +3816,28 @@ func (ar *AffectedReleases) DisplayAffectedReleases(logger *zap.SugaredLogger) {
|
|||
}
|
||||
logger.Info(tbl.String())
|
||||
}
|
||||
if len(ar.Reinstalled) > 0 {
|
||||
logger.Info("\nREINSTALLED RELEASES:")
|
||||
tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"},
|
||||
prettytable.Column{Header: "NAMESPACE", MinWidth: 6},
|
||||
prettytable.Column{Header: "CHART", MinWidth: 6},
|
||||
prettytable.Column{Header: "VERSION", MinWidth: 6},
|
||||
prettytable.Column{Header: "DURATION", AlignRight: true},
|
||||
)
|
||||
tbl.Separator = " "
|
||||
for _, release := range ar.Reinstalled {
|
||||
modifiedChart, modErr := hideChartCredentials(release.Chart)
|
||||
if modErr != nil {
|
||||
logger.Warn("Could not modify chart credentials, %v", modErr)
|
||||
continue
|
||||
}
|
||||
err := tbl.AddRow(release.Name, release.Namespace, modifiedChart, release.installedVersion, release.duration.Round(time.Second))
|
||||
if err != nil {
|
||||
logger.Warn("Could not add row, %v", err)
|
||||
}
|
||||
}
|
||||
logger.Info(tbl.String())
|
||||
}
|
||||
if len(ar.Deleted) > 0 {
|
||||
logger.Info("\nDELETED RELEASES:")
|
||||
tbl, _ := prettytable.NewTable(prettytable.Column{Header: "NAME"},
|
||||
|
|
@ -4025,7 +4170,48 @@ func (st *HelmState) Reverse() {
|
|||
}
|
||||
}
|
||||
|
||||
func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helmexec.Interface, outputDirTemplate string) (*string, error) {
|
||||
// Chart cache for both OCI and non-OCI charts to avoid duplicate downloads
|
||||
type ChartCacheKey struct {
|
||||
Chart string
|
||||
Version string
|
||||
}
|
||||
|
||||
var downloadedCharts = make(map[ChartCacheKey]string) // key -> chart path
|
||||
var downloadedChartsMutex sync.RWMutex
|
||||
|
||||
// Legacy OCI-specific cache (kept for backward compatibility)
|
||||
var downloadedOCICharts = make(map[string]bool)
|
||||
var downloadedOCIMutex sync.RWMutex
|
||||
|
||||
// getChartCacheKey creates a cache key for a chart and version
|
||||
func (st *HelmState) getChartCacheKey(release *ReleaseSpec) ChartCacheKey {
|
||||
version := release.Version
|
||||
if version == "" {
|
||||
// Use empty string for latest version
|
||||
version = ""
|
||||
}
|
||||
return ChartCacheKey{
|
||||
Chart: release.Chart,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// checkChartCache checks if a chart is already downloaded and returns its path
|
||||
func (st *HelmState) checkChartCache(key ChartCacheKey) (string, bool) {
|
||||
downloadedChartsMutex.RLock()
|
||||
defer downloadedChartsMutex.RUnlock()
|
||||
path, exists := downloadedCharts[key]
|
||||
return path, exists
|
||||
}
|
||||
|
||||
// addToChartCache adds a chart to the cache
|
||||
func (st *HelmState) addToChartCache(key ChartCacheKey, path string) {
|
||||
downloadedChartsMutex.Lock()
|
||||
defer downloadedChartsMutex.Unlock()
|
||||
downloadedCharts[key] = path
|
||||
}
|
||||
|
||||
func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helmexec.Interface, opts ChartPrepareOptions) (*string, error) {
|
||||
qualifiedChartName, chartName, chartVersion, err := st.getOCIQualifiedChartName(release, helm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -4035,7 +4221,41 @@ func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helm
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
chartPath, _ := st.getOCIChartPath(tempDir, release, chartName, chartVersion, outputDirTemplate)
|
||||
// Check global chart cache first
|
||||
cacheKey := st.getChartCacheKey(release)
|
||||
if cachedPath, exists := st.checkChartCache(cacheKey); exists && st.fs.DirectoryExistsAt(cachedPath) {
|
||||
st.logger.Debugf("OCI chart %s:%s already downloaded, using cached version at %s", chartName, chartVersion, cachedPath)
|
||||
return &cachedPath, nil
|
||||
}
|
||||
|
||||
if opts.OutputDirTemplate == "" {
|
||||
tempDir = remote.CacheDir()
|
||||
}
|
||||
|
||||
chartPath, _ := st.getOCIChartPath(tempDir, release, chartName, chartVersion, opts.OutputDirTemplate)
|
||||
|
||||
mu := st.getNamedMutex(chartPath)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
_, err = os.Stat(tempDir)
|
||||
if err != nil {
|
||||
err = os.MkdirAll(tempDir, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
downloadedOCIMutex.RLock()
|
||||
alreadyDownloadedFlag := downloadedOCICharts[chartPath]
|
||||
downloadedOCIMutex.RUnlock()
|
||||
|
||||
if !opts.SkipDeps && !opts.SkipRefresh && !alreadyDownloadedFlag {
|
||||
err = os.RemoveAll(chartPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if st.fs.DirectoryExistsAt(chartPath) {
|
||||
st.logger.Debugf("chart already exists at %s", chartPath)
|
||||
|
|
@ -4062,8 +4282,15 @@ func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helm
|
|||
return nil, err
|
||||
}
|
||||
|
||||
downloadedOCIMutex.Lock()
|
||||
downloadedOCICharts[chartPath] = true
|
||||
downloadedOCIMutex.Unlock()
|
||||
|
||||
chartPath = filepath.Dir(fullChartPath)
|
||||
|
||||
// Add to global chart cache
|
||||
st.addToChartCache(cacheKey, chartPath)
|
||||
|
||||
return &chartPath, nil
|
||||
}
|
||||
|
||||
|
|
@ -4128,15 +4355,15 @@ func (st *HelmState) getOCIChartPath(tempDir string, release *ReleaseSpec, chart
|
|||
|
||||
pathElems := []string{tempDir}
|
||||
|
||||
if release.Namespace != "" {
|
||||
pathElems = append(pathElems, release.Namespace)
|
||||
}
|
||||
|
||||
if release.KubeContext != "" {
|
||||
pathElems = append(pathElems, release.KubeContext)
|
||||
}
|
||||
|
||||
pathElems = append(pathElems, release.Name, chartName, safeVersionPath(chartVersion))
|
||||
replacer := strings.NewReplacer(
|
||||
":", "_",
|
||||
"//", "_",
|
||||
".", "_",
|
||||
"&", "_",
|
||||
)
|
||||
qName := strings.Split(replacer.Replace(release.Chart), "/")
|
||||
|
||||
pathElems = append(pathElems, qName...)
|
||||
pathElems = append(pathElems, safeVersionPath(chartVersion))
|
||||
return filepath.Join(pathElems...), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1640,6 +1640,112 @@ func TestHelmState_SyncReleasesAffectedRealeases(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHelmState_SyncReleasesAffectedReleasesWithReinstallIfForbidden(t *testing.T) {
|
||||
no := false
|
||||
tests := []struct {
|
||||
name string
|
||||
releases []ReleaseSpec
|
||||
installed []bool
|
||||
wantAffected exectest.Affected
|
||||
}{
|
||||
{
|
||||
name: "2 new",
|
||||
releases: []ReleaseSpec{
|
||||
{
|
||||
Name: "releaseNameFoo-forbidden",
|
||||
Chart: "foo",
|
||||
UpdateStrategy: "reinstallIfForbidden",
|
||||
},
|
||||
{
|
||||
Name: "releaseNameBar-forbidden",
|
||||
Chart: "foo",
|
||||
UpdateStrategy: "reinstallIfForbidden",
|
||||
},
|
||||
},
|
||||
wantAffected: exectest.Affected{
|
||||
Upgraded: []*exectest.Release{
|
||||
{Name: "releaseNameFoo-forbidden", Flags: []string{}},
|
||||
{Name: "releaseNameBar-forbidden", Flags: []string{}},
|
||||
},
|
||||
Reinstalled: nil,
|
||||
Deleted: nil,
|
||||
Failed: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1 removed, 1 new, 1 reinstalled first new",
|
||||
releases: []ReleaseSpec{
|
||||
{
|
||||
Name: "releaseNameFoo-forbidden",
|
||||
Chart: "foo",
|
||||
UpdateStrategy: "reinstallIfForbidden",
|
||||
},
|
||||
{
|
||||
Name: "releaseNameBar",
|
||||
Chart: "foo",
|
||||
UpdateStrategy: "reinstallIfForbidden",
|
||||
Installed: &no,
|
||||
},
|
||||
{
|
||||
Name: "releaseNameFoo-forbidden",
|
||||
Chart: "foo",
|
||||
UpdateStrategy: "reinstallIfForbidden",
|
||||
},
|
||||
},
|
||||
installed: []bool{true, true, true},
|
||||
wantAffected: exectest.Affected{
|
||||
Upgraded: []*exectest.Release{
|
||||
{Name: "releaseNameFoo-forbidden", Flags: []string{}},
|
||||
},
|
||||
Reinstalled: []*exectest.Release{
|
||||
{Name: "releaseNameFoo-forbidden", Flags: []string{}},
|
||||
},
|
||||
Deleted: []*exectest.Release{
|
||||
{Name: "releaseNameBar", Flags: []string{}},
|
||||
},
|
||||
Failed: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
state := &HelmState{
|
||||
ReleaseSetSpec: ReleaseSetSpec{
|
||||
Releases: tt.releases,
|
||||
},
|
||||
logger: logger,
|
||||
valsRuntime: valsRuntime,
|
||||
RenderedValues: map[string]any{},
|
||||
}
|
||||
helm := &exectest.Helm{
|
||||
Lists: map[exectest.ListKey]string{},
|
||||
}
|
||||
//simulate the release is already installed
|
||||
for i, release := range tt.releases {
|
||||
if tt.installed != nil && tt.installed[i] {
|
||||
helm.Lists[exectest.ListKey{Filter: "^" + release.Name + "$", Flags: "--uninstalling --deployed --failed --pending"}] = release.Name
|
||||
}
|
||||
}
|
||||
|
||||
affectedReleases := AffectedReleases{}
|
||||
if err := state.SyncReleases(&affectedReleases, helm, []string{}, 1); err != nil {
|
||||
if !testEq(affectedReleases.Failed, tt.wantAffected.Failed) {
|
||||
t.Errorf("HelmState.SyncReleases() error failed for [%s] = %v, want %v", tt.name, affectedReleases.Failed, tt.wantAffected.Failed)
|
||||
} //else expected error
|
||||
}
|
||||
if !testEq(affectedReleases.Upgraded, tt.wantAffected.Upgraded) {
|
||||
t.Errorf("HelmState.SyncReleases() upgrade failed for [%s] = %v, want %v", tt.name, affectedReleases.Upgraded, tt.wantAffected.Upgraded)
|
||||
}
|
||||
if !testEq(affectedReleases.Reinstalled, tt.wantAffected.Reinstalled) {
|
||||
t.Errorf("HelmState.SyncReleases() reinstalled failed for [%s] = %v, want %v", tt.name, affectedReleases.Reinstalled, tt.wantAffected.Reinstalled)
|
||||
}
|
||||
if !testEq(affectedReleases.Deleted, tt.wantAffected.Deleted) {
|
||||
t.Errorf("HelmState.SyncReleases() deleted failed for [%s] = %v, want %v", tt.name, affectedReleases.Deleted, tt.wantAffected.Deleted)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testEq(a []*ReleaseSpec, b []*exectest.Release) bool {
|
||||
// If one is nil, the other must also be nil.
|
||||
if (a == nil) != (b == nil) {
|
||||
|
|
@ -4638,3 +4744,58 @@ func TestPrepareSyncReleases_ValueControlReleaseOverride(t *testing.T) {
|
|||
require.Equal(t, tt.flags, r.flags, "Wrong value control flag for release %s", r.release.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChartCacheKey(t *testing.T) {
|
||||
st := &HelmState{}
|
||||
|
||||
// Test case 1: release with version
|
||||
release1 := &ReleaseSpec{
|
||||
Chart: "stable/nginx",
|
||||
Version: "1.2.3",
|
||||
}
|
||||
|
||||
key1 := st.getChartCacheKey(release1)
|
||||
expected1 := ChartCacheKey{Chart: "stable/nginx", Version: "1.2.3"}
|
||||
|
||||
if key1 != expected1 {
|
||||
t.Errorf("Expected %+v, got %+v", expected1, key1)
|
||||
}
|
||||
|
||||
// Test case 2: release without version
|
||||
release2 := &ReleaseSpec{
|
||||
Chart: "stable/nginx",
|
||||
}
|
||||
|
||||
key2 := st.getChartCacheKey(release2)
|
||||
expected2 := ChartCacheKey{Chart: "stable/nginx", Version: ""}
|
||||
|
||||
if key2 != expected2 {
|
||||
t.Errorf("Expected %+v, got %+v", expected2, key2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChartCache(t *testing.T) {
|
||||
st := &HelmState{}
|
||||
|
||||
// Create a test key
|
||||
key := ChartCacheKey{Chart: "stable/test", Version: "1.0.0"}
|
||||
path := "/tmp/test-chart"
|
||||
|
||||
// Initially, chart should not be in cache
|
||||
_, exists := st.checkChartCache(key)
|
||||
if exists {
|
||||
t.Error("Chart should not be in cache initially")
|
||||
}
|
||||
|
||||
// Add to cache
|
||||
st.addToChartCache(key, path)
|
||||
|
||||
// Now chart should be in cache
|
||||
cachedPath, exists := st.checkChartCache(key)
|
||||
if !exists {
|
||||
t.Error("Chart should be in cache after adding")
|
||||
}
|
||||
if cachedPath != path {
|
||||
t.Errorf("Expected path %s, got %s", path, cachedPath)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,39 +38,39 @@ func TestGenerateID(t *testing.T) {
|
|||
run(testcase{
|
||||
subject: "baseline",
|
||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||
want: "foo-values-7d454b9558",
|
||||
want: "foo-values-67dc97cbcb",
|
||||
})
|
||||
|
||||
run(testcase{
|
||||
subject: "different bytes content",
|
||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||
data: []byte(`{"k":"v"}`),
|
||||
want: "foo-values-59c86d55bf",
|
||||
want: "foo-values-75d7c4758c",
|
||||
})
|
||||
|
||||
run(testcase{
|
||||
subject: "different map content",
|
||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw"},
|
||||
data: map[string]any{"k": "v"},
|
||||
want: "foo-values-6f87c5cd79",
|
||||
want: "foo-values-685f8cf685",
|
||||
})
|
||||
|
||||
run(testcase{
|
||||
subject: "different chart",
|
||||
release: ReleaseSpec{Name: "foo", Chart: "stable/envoy"},
|
||||
want: "foo-values-5dfd748475",
|
||||
want: "foo-values-75597d9c57",
|
||||
})
|
||||
|
||||
run(testcase{
|
||||
subject: "different name",
|
||||
release: ReleaseSpec{Name: "bar", Chart: "incubator/raw"},
|
||||
want: "bar-values-858b9c55cc",
|
||||
want: "bar-values-7b77df65ff",
|
||||
})
|
||||
|
||||
run(testcase{
|
||||
subject: "specific ns",
|
||||
release: ReleaseSpec{Name: "foo", Chart: "incubator/raw", Namespace: "myns"},
|
||||
want: "myns-foo-values-58dc9c6667",
|
||||
want: "myns-foo-values-85f979545c",
|
||||
})
|
||||
|
||||
for id, n := range ids {
|
||||
|
|
|
|||
|
|
@ -30,12 +30,6 @@ var (
|
|||
helmShortVersionRegex = regexp.MustCompile(`v\d+\.\d+\.\d+\+[a-z0-9]+`)
|
||||
)
|
||||
|
||||
type ociChart struct {
|
||||
name string
|
||||
version string
|
||||
digest string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
LocalDockerRegistry struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
|
|
@ -166,8 +160,6 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
}
|
||||
})
|
||||
|
||||
// ociCharts holds a list of chart name, version and digest distributed by local oci registry.
|
||||
ociCharts := []ociChart{}
|
||||
// If localDockerRegistry.enabled is set to `true`,
|
||||
// run the docker registry v2 and push the test charts to the registry
|
||||
// so that it can be accessed by helm and helmfile as a oci registry based chart repository.
|
||||
|
|
@ -201,16 +193,9 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
if !c.IsDir() {
|
||||
t.Fatalf("%s is not a directory", c)
|
||||
}
|
||||
chartName, chartVersion := execHelmShowChart(t, chartPath)
|
||||
tgzFile := execHelmPackage(t, chartPath)
|
||||
chartDigest, err := execHelmPush(t, tgzFile, fmt.Sprintf("oci://localhost:%d/myrepo", hostPort))
|
||||
_, err := execHelmPush(t, tgzFile, fmt.Sprintf("oci://localhost:%d/myrepo", hostPort))
|
||||
require.NoError(t, err, "Unable to run helm push to local registry: %v", err)
|
||||
|
||||
ociCharts = append(ociCharts, ociChart{
|
||||
name: chartName,
|
||||
version: chartVersion,
|
||||
digest: chartDigest,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +206,7 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
helmfileCacheHome := filepath.Join(tmpDir, "helmfile_cache")
|
||||
// HELM_CONFIG_HOME contains the registry auth file (registry.json) and the index of all the repos added via helm-repo-add (repositories.yaml).
|
||||
helmConfigHome := filepath.Join(tmpDir, "helm_config")
|
||||
t.Logf("Using HELM_CACHE_HOME=%s, HELMFILE_CACHE_HOME=%s, HELM_CONFIG_HOME=%s", helmCacheHome, helmfileCacheHome, helmConfigHome)
|
||||
t.Logf("Using HELM_CACHE_HOME=%s, HELMFILE_CACHE_HOME=%s, HELM_CONFIG_HOME=%s, WD=%s", helmCacheHome, helmfileCacheHome, helmConfigHome, wd)
|
||||
|
||||
inputFile := filepath.Join(testdataDir, name, "input.yaml.gotmpl")
|
||||
outputFile := ""
|
||||
|
|
@ -230,6 +215,7 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
} else {
|
||||
outputFile = filepath.Join(testdataDir, name, "gopkg.in-yaml.v2-output.yaml")
|
||||
}
|
||||
expectedOutputFile := filepath.Join(testdataDir, name, "output.yaml")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
defer cancel()
|
||||
|
|
@ -262,15 +248,8 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
gotStr = chartGitFullPathRegex.ReplaceAllString(gotStr, `chart=$$GoGetterPath`)
|
||||
// Replace helm version with $HelmVersion
|
||||
gotStr = helmShortVersionRegex.ReplaceAllString(gotStr, `$$HelmVersion`)
|
||||
// Replace all occurrences of HELMFILE_CACHE_HOME with /home/runner/.cache/helmfile
|
||||
// for stable test result
|
||||
gotStr = strings.ReplaceAll(gotStr, helmfileCacheHome, "/home/runner/.cache/helmfile")
|
||||
|
||||
// OCI based helm charts are pulled and exported under temporary directory.
|
||||
// We are not sure the exact name of the temporary directory generated by helmfile,
|
||||
// so redact its base directory name with $TMP.
|
||||
if config.LocalDockerRegistry.Enabled {
|
||||
var releaseName, chartPath string
|
||||
sc := bufio.NewScanner(strings.NewReader(gotStr))
|
||||
for sc.Scan() {
|
||||
if !strings.HasPrefix(sc.Text(), "Templating ") {
|
||||
|
|
@ -281,28 +260,20 @@ func testHelmfileTemplateWithBuildCommand(t *testing.T, GoYamlV3 bool) {
|
|||
if len(releaseChartParts) != 2 {
|
||||
t.Fatal("Found unexpected log output of templating oci based helm chart, want=\"Templating release=<release_name>, chart=<chart_name>\"")
|
||||
}
|
||||
releaseNamePart, chartPathPart := releaseChartParts[0], releaseChartParts[1]
|
||||
releaseName = strings.TrimPrefix(releaseNamePart, "release=")
|
||||
chartPath = chartPathPart
|
||||
}
|
||||
for _, ociChart := range ociCharts {
|
||||
chartPathWithoutTempDirBase := fmt.Sprintf("/%s/%s/%s/%s", releaseName, ociChart.name, ociChart.version, ociChart.name)
|
||||
var chartPathBase string
|
||||
if strings.HasSuffix(chartPath, chartPathWithoutTempDirBase) {
|
||||
chartPathBase = strings.TrimSuffix(chartPath, chartPathWithoutTempDirBase)
|
||||
}
|
||||
if len(chartPathBase) != 0 {
|
||||
gotStr = strings.ReplaceAll(gotStr, chartPathBase, "chart=$TMP")
|
||||
}
|
||||
gotStr = strings.ReplaceAll(gotStr, fmt.Sprintf("Digest: %s", ociChart.digest), "Digest: $DIGEST")
|
||||
}
|
||||
}
|
||||
|
||||
gotStr = strings.ReplaceAll(gotStr, helmfileCacheHome, "$HELMFILE_CACHE_HOME")
|
||||
gotStr = strings.ReplaceAll(gotStr, wd, "__workingdir__")
|
||||
|
||||
if stat, _ := os.Stat(outputFile); stat != nil {
|
||||
want, err := os.ReadFile(outputFile)
|
||||
wantStr := strings.ReplaceAll(string(want), "__workingdir__", wd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, wantStr, gotStr)
|
||||
require.Equal(t, string(want), gotStr)
|
||||
} else if stat, _ := os.Stat(expectedOutputFile); stat != nil {
|
||||
want, err := os.ReadFile(expectedOutputFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(want), gotStr)
|
||||
} else {
|
||||
// To update the test golden image(output.yaml), just remove it and rerun this test.
|
||||
// We automatically capture the output to `output.yaml` in the test case directory
|
||||
|
|
@ -325,23 +296,6 @@ func execDocker(t *testing.T, args ...string) {
|
|||
}
|
||||
}
|
||||
|
||||
func execHelmShowChart(t *testing.T, localChart string) (string, string) {
|
||||
t.Helper()
|
||||
|
||||
name, version := "", ""
|
||||
out := execHelm(t, "show", "chart", localChart)
|
||||
sc := bufio.NewScanner(strings.NewReader(out))
|
||||
for sc.Scan() {
|
||||
if strings.HasPrefix(sc.Text(), "name:") {
|
||||
name = strings.TrimPrefix(sc.Text(), "name: ")
|
||||
}
|
||||
if strings.HasPrefix(sc.Text(), "version:") {
|
||||
version = strings.TrimPrefix(sc.Text(), "version: ")
|
||||
}
|
||||
}
|
||||
return name, version
|
||||
}
|
||||
|
||||
func execHelmPackage(t *testing.T, localChart string) string {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Pulling localhost:5001/myrepo/raw:0.1.0
|
||||
Templating release=foo, chart=$TMP/foo/raw/0.1.0/raw
|
||||
Templating release=foo, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
|
|
|
|||
6
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/config.yaml
vendored
Normal file
6
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/config.yaml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
localDockerRegistry:
|
||||
enabled: true
|
||||
port: 5001
|
||||
chartifyTempDir: temp2
|
||||
helmfileArgs:
|
||||
- template
|
||||
22
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/input.yaml.gotmpl
vendored
Normal file
22
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/input.yaml.gotmpl
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
releases:
|
||||
- name: foo
|
||||
chart: oci://localhost:5001/myrepo/raw
|
||||
version: 0.1.0
|
||||
values: &oci_chart_pull_direct
|
||||
- templates:
|
||||
- |
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{`{{ .Release.Name }}`}}
|
||||
namespace: {{`{{ .Release.Namespace }}`}}
|
||||
annotations:
|
||||
chart-version: {{`{{ .Chart.Version }}`}}
|
||||
data:
|
||||
values: {{`{{ .Release.Name }}`}}
|
||||
|
||||
- name: bar
|
||||
chart: oci://localhost:5001/myrepo/raw
|
||||
version: 0.1.0
|
||||
namespace: ns2
|
||||
values: *oci_chart_pull_direct
|
||||
27
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/output.yaml
vendored
Normal file
27
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_direct/output.yaml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Pulling localhost:5001/myrepo/raw:0.1.0
|
||||
Templating release=foo, chart=$HELMFILE_CACHE_HOME/oci__localhost_5001/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: foo
|
||||
|
||||
Templating release=bar, chart=$HELMFILE_CACHE_HOME/oci__localhost_5001/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bar
|
||||
namespace: ns2
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: bar
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Templating two versions of the same chart with only one pulling of each version
|
||||
localDockerRegistry:
|
||||
enabled: true
|
||||
port: 5001
|
||||
chartifyTempDir: temp3
|
||||
helmfileArgs:
|
||||
# Prevent releases from racing and randomizing the log
|
||||
- --concurrency=1
|
||||
- template
|
||||
43
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once/input.yaml.gotmpl
vendored
Normal file
43
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once/input.yaml.gotmpl
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
repositories:
|
||||
- name: myrepo
|
||||
url: localhost:5001/myrepo
|
||||
oci: true
|
||||
|
||||
releases:
|
||||
- name: foo
|
||||
chart: myrepo/raw
|
||||
version: 0.1.0
|
||||
values: &oci_chart_pull_once_values
|
||||
- templates:
|
||||
- |
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{`{{ .Release.Name }}`}}
|
||||
namespace: {{`{{ .Release.Namespace }}`}}
|
||||
annotations:
|
||||
chart-version: {{`{{ .Chart.Version }}`}}
|
||||
data:
|
||||
values: {{`{{ .Release.Name }}`}}
|
||||
|
||||
- name: bar
|
||||
chart: myrepo/raw
|
||||
version: 0.1.0
|
||||
values: *oci_chart_pull_once_values
|
||||
|
||||
- name: release-no-default-ns
|
||||
chart: myrepo/raw
|
||||
version: 0.1.0
|
||||
namespace: no-default
|
||||
values: *oci_chart_pull_once_values
|
||||
|
||||
- name: second-version-of-chart
|
||||
chart: myrepo/raw
|
||||
version: 0.0.1
|
||||
namespace: foobar
|
||||
values: *oci_chart_pull_once_values
|
||||
|
||||
- name: first-release-version-of-chart
|
||||
chart: myrepo/raw
|
||||
version: 0.1.0
|
||||
values: *oci_chart_pull_once_values
|
||||
67
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once/output.yaml
vendored
Normal file
67
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once/output.yaml
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
Pulling localhost:5001/myrepo/raw:0.1.0
|
||||
Pulling localhost:5001/myrepo/raw:0.0.1
|
||||
Templating release=foo, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: foo
|
||||
|
||||
Templating release=bar, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bar
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: bar
|
||||
|
||||
Templating release=release-no-default-ns, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-no-default-ns
|
||||
namespace: no-default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-no-default-ns
|
||||
|
||||
Templating release=second-version-of-chart, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.0.1/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: second-version-of-chart
|
||||
namespace: foobar
|
||||
annotations:
|
||||
chart-version: 0.0.1
|
||||
data:
|
||||
values: second-version-of-chart
|
||||
|
||||
Templating release=first-release-version-of-chart, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: first-release-version-of-chart
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: first-release-version-of-chart
|
||||
|
||||
7
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/config.yaml
vendored
Normal file
7
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/config.yaml
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Templating few releases with the same chart\version and only one pulling
|
||||
localDockerRegistry:
|
||||
enabled: true
|
||||
port: 5001
|
||||
chartifyTempDir: temp3
|
||||
helmfileArgs:
|
||||
- template
|
||||
23
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/input.yaml.gotmpl
vendored
Normal file
23
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/input.yaml.gotmpl
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
repositories:
|
||||
- name: myrepo
|
||||
url: localhost:5001/myrepo
|
||||
oci: true
|
||||
|
||||
releases:
|
||||
{{- range $i := until 5 }}
|
||||
- name: release-{{ $i }}
|
||||
chart: myrepo/raw
|
||||
version: 0.1.0
|
||||
values:
|
||||
- templates:
|
||||
- |
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{`{{ .Release.Name }}`}}
|
||||
namespace: {{`{{ .Release.Namespace }}`}}
|
||||
annotations:
|
||||
chart-version: {{`{{ .Chart.Version }}`}}
|
||||
data:
|
||||
values: {{`{{ .Release.Name }}`}}
|
||||
{{- end }}
|
||||
66
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/output.yaml
vendored
Normal file
66
test/e2e/template/helmfile/testdata/snapshot/oci_chart_pull_once2/output.yaml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
Pulling localhost:5001/myrepo/raw:0.1.0
|
||||
Templating release=release-0, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-0
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-0
|
||||
|
||||
Templating release=release-1, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-1
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-1
|
||||
|
||||
Templating release=release-2, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-2
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-2
|
||||
|
||||
Templating release=release-3, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-3
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-3
|
||||
|
||||
Templating release=release-4, chart=$HELMFILE_CACHE_HOME/myrepo/raw/0.1.0/raw
|
||||
---
|
||||
# Source: raw/templates/resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: release-4
|
||||
namespace: default
|
||||
annotations:
|
||||
chart-version: 0.1.0
|
||||
data:
|
||||
values: release-4
|
||||
|
||||
Loading…
Reference in New Issue