Compare commits

..

No commits in common. "main" and "v0.155.0" have entirely different histories.

485 changed files with 16379 additions and 47167 deletions

View File

@ -1,234 +0,0 @@
# Copilot Instructions for Helmfile
## Repository Overview
Helmfile is a tool for deploying Helm charts that manages Kubernetes deployments as code. It provides templating, environment management, and GitOps workflows for Helm chart deployments.
Helmfile is a declarative tool. In Helmfile, all elements of the desired state for deployments must be included in the `helmfile.yaml` config file and any associated files. Only operational matters can be provided dynamically, via command-line flags and environment variables.
**Key Details:**
- **Language:** Go 1.24.2+
- **Type:** CLI tool / Kubernetes deployment management
- **Size:** Large codebase (~229MB binary, 200+ dependencies)
- **Runtime:** Requires Helm 3.x as external dependency
- **Target Platform:** Linux/macOS/Windows, Kubernetes clusters
## Build and Validation Commands
### Essential Setup
Helmfile requires Helm 3.x and Kustomize 5.x as runtime dependencies:
```bash
# Check for Helm dependency (REQUIRED)
helm version # Must show version 3.x
# Initialize Helm plugins after helmfile installation
./helmfile init # Installs required helm-diff plugin
# Alternative: Force install without prompts
./helmfile init --force
```
### Build Process
```bash
# Standard build (takes 2-3 minutes due to many dependencies)
make build
# Alternative direct build
go build -o helmfile .
# Build with test tools (required for integration tests, ~1 minute)
make build-test-tools # Creates diff-yamls and downloads dyff
# Cross-platform builds
make cross
```
**Build Timing:** First build downloads 200+ Go packages and takes 2-3 minutes. Subsequent builds are faster due to module cache. Test tools build is faster (~1 minute).
### Validation Pipeline
Run in this exact order to match CI requirements:
```bash
# 1. Code formatting and linting
make check # Run go vet (required - always works)
# Note: make fmt requires gci tool (go install github.com/daixiang0/gci@latest)
# 2. Unit tests (fast, ~30 seconds)
go test -v ./pkg/... -race -p=1
# 3. Integration tests (requires Kubernetes - see Environment Setup)
make integration # Takes 5-10 minutes, needs minikube/k8s cluster
# 4. E2E tests (optional, needs expect package)
sudo apt-get install expect # On Ubuntu/Debian
bash test/e2e/helmfile-init/init_linux.sh
```
### Linting Configuration
Uses golangci-lint with configuration in `.golangci.yaml`. Install via:
```bash
# For local development
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.1.6
golangci-lint run
```
**Critical Lint Rules:** staticcheck, errcheck, revive, unused. Fix lint errors before committing.
## Environment Setup Requirements
### Dependencies for Development
```bash
# Required for building/testing
go version # Must be 1.24.2+
helm version # Must be 3.x
kubectl version # For K8s integration
# Required for integration tests
minikube start # Or other K8s cluster
kustomize version # v5.2.1+ for some tests
```
### Integration Test Environment
Integration tests require a running Kubernetes cluster:
```bash
# Using minikube (recommended for CI)
minikube start
export KUBECONFIG=$(minikube kubeconfig-path)
# Using kind (alternative)
kind create cluster --name helmfile-test
# Verify cluster access
kubectl cluster-info
```
**Timing:** Integration tests take 5-10 minutes and may fail due to timing issues in resource-constrained environments.
## Project Architecture and Layout
### Core Directory Structure
```
/
├── main.go # Entry point - CLI initialization and signal handling
├── cmd/ # CLI commands (apply, diff, sync, template, etc.)
│ ├── root.go # Main cobra command setup and global flags
│ ├── apply.go # helmfile apply command
│ ├── diff.go # helmfile diff command
│ └── ... # Other subcommands
├── pkg/ # Core library packages
│ ├── app/ # Main application logic and execution
│ ├── state/ # Helmfile state management and chart dependencies
│ ├── helmexec/ # Helm execution and command wrapper
│ ├── tmpl/ # Go template processing and functions
│ ├── environment/ # Environment and values management
│ └── ... # Other core packages
├── test/ # Test suites
│ ├── integration/ # Integration tests with real K8s clusters
│ ├── e2e/ # End-to-end user workflow tests
│ └── advanced/ # Advanced feature tests
├── docs/ # Documentation (mkdocs format)
├── examples/ # Example helmfile configurations
└── .github/workflows/ # CI/CD pipeline definitions
```
### Key Source Files
- **main.go:** Signal handling, CLI execution entry point
- **cmd/root.go:** Global CLI configuration, error handling, logging setup
- **pkg/app/app.go:** Main application orchestration, state management
- **pkg/state/state.go:** Helmfile state parsing, release management
- **pkg/helmexec/exec.go:** Helm command execution, version detection
### Configuration Files
- **go.mod/go.sum:** Go dependencies (many cloud providers, k8s libraries)
- **.golangci.yaml:** Linting rules and settings
- **Makefile:** Build targets and development workflows
- **mkdocs.yml:** Documentation generation configuration
- **.github/workflows/ci.yaml:** Complete CI pipeline definition
## Continuous Integration Pipeline
### GitHub Actions Workflow (`.github/workflows/ci.yaml`)
1. **Lint Job:** golangci-lint with custom configuration (~5 minutes)
2. **Test Job:** Unit tests + binary build (~10 minutes)
3. **Integration Job:** Tests with multiple Helm/Kustomize versions (~15-20 minutes each)
4. **E2E Job:** User workflow validation (~5 minutes)
**Matrix Testing:** CI tests against multiple Helm versions (3.17.x, 3.18.x) and Kustomize versions (5.2.x, 5.4.x).
### Pre-commit Validation Steps
Always run these locally before pushing:
```bash
make check # Format and vet (required)
go test ./pkg/... # Unit tests
make build # Verify build works
# Note: make fmt requires gci tool: go install github.com/daixiang0/gci@latest
```
### Common CI Failure Causes
- **Linting errors:** Run `golangci-lint run` locally first
- **Integration test timeouts:** K8s cluster setup timing issues
- **Version compatibility:** Ensure Go 1.24.2+ and Helm 3.x
- **Race conditions:** Some tests are sensitive to parallel execution
## Development Gotchas and Known Issues
### Build Issues
- **Long initial build time:** First `make build` downloads 200+ packages (~2-3 minutes)
- **Memory usage:** Large binary size due to embedded dependencies
- **Git tags:** Build may show version warnings if not on tagged commit
- **Tool dependencies:** `make fmt` requires `gci` tool installation
### Testing Issues
- **Integration tests require K8s:** Will fail without cluster access
- **Test isolation:** Use `-p=1` flag to avoid race conditions
- **Minikube timing:** May need to wait for cluster ready state
- **Plugin dependencies:** Tests need helm-diff and helm-secrets plugins
### Runtime Requirements
- **Helm dependency:** Always required at runtime, not just build time (available in CI)
- **kubectl access:** Most operations need valid kubeconfig
- **Plugin management:** `helmfile init` must be run after installation
### Common Error Patterns
```bash
# Missing Helm
"helm: command not found" → Install Helm first
# Plugin missing
"Error: plugin 'diff' not found" → Run helmfile init
# K8s access
"connection refused" → Check kubectl cluster-info
# Permission errors
"permission denied" → Check kubeconfig and cluster access
# Missing tools
"gci: No such file or directory" → go install github.com/daixiang0/gci@latest
```
## Working with the Codebase
### Making Changes
- **Follow Helmfile design**: Helmfile is a declarative deployment tool. Anything that is part of the desired state of the deployments needs to be managed by Helmfile configs. Only operational knowledge that affects "how" to apply the desired state needs to be runtime options, like command-like flags and environment variables.
- **Small, focused changes:** Each PR should address single concern
- **Test coverage:** Add unit tests for new pkg/ functionality
- **Integration tests:** Update test-cases/ for new CLI features
- **Documentation:** Update docs/ for user-facing changes
### Key Packages to Understand
- **pkg/app:** Main business logic, start here for feature changes
- **pkg/state:** Helmfile parsing and release orchestration
- **cmd/:** CLI interface changes and new subcommands
- **pkg/helmexec:** Helm integration and command execution
### Architecture Patterns
- **Dependency injection:** App uses interfaces for testability
- **State management:** Immutable state objects, functional transforms
- **Error handling:** Custom error types with exit codes
- **Plugin system:** Extensible via Helm plugins and Go templates
---
**Trust these instructions:** This information is validated against the current codebase. Only search for additional details if these instructions are incomplete or found to be incorrect for your specific task.

View File

@ -9,7 +9,3 @@ updates:
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

1
.github/stale.yml vendored
View File

@ -7,7 +7,6 @@ exemptLabels:
- in progress
- pinned
- security
- bug
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable

View File

@ -1,8 +1,8 @@
HELM_VERSION ?= v3.7.2
KUSTOMIZE_VERSION ?= v5.4.3
K8S_VERSION ?= v1.32.1
MINIKUBE_VERSION ?= v1.31.1
SOPS_VERSION ?= v3.9.3
KUSTOMIZE_VERSION ?= v4.5.7
K8S_VERSION ?= v1.13.12
MINIKUBE_VERSION ?= v0.30.0
SOPS_VERSION ?= v3.6.1
# ---
CHANGE_MINIKUBE_NONE_USER ?= true
@ -57,12 +57,12 @@ minikube:
vault:
docker kill $$(docker ps -a --filter "name=vault" -q) || true
docker run -d -p8200:8200 --rm --name vault vault:1.2.0 server -dev -dev-root-token-id=toor
docker run --rm --network="host" --cap-add IPC_LOCK -e VAULT_ADDR=$$VAULT_ADDR -e VAULT_TOKEN=$$VAULT_TOKEN vault:1.2.0 secrets enable -path=sops transit
docker run --rm --network="host" --cap-add IPC_LOCK -e VAULT_ADDR=$$VAULT_ADDR -e VAULT_TOKEN=$$VAULT_TOKEN vault:1.2.0 write sops/keys/key type=rsa-4096
docker run --rm --network="host" -e VAULT_ADDR=$$VAULT_ADDR -e VAULT_TOKEN=$$VAULT_TOKEN vault:1.2.0 secrets enable -path=sops transit
docker run --rm --network="host" -e VAULT_ADDR=$$VAULT_ADDR -e VAULT_TOKEN=$$VAULT_TOKEN vault:1.2.0 write sops/keys/key type=rsa-4096
.PHONY: vault
sops:
curl -sSLo $(tmp)/sops "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64"
curl -sSLo $(tmp)/sops "https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux"
chmod +x $(tmp)/sops
sudo mv ${tmp}/sops /usr/local/bin/
.PHONY: sops

View File

@ -1,26 +0,0 @@
name: Cleanup cache
on:
pull_request:
types:
- closed
jobs:
cleanup-cache:
runs-on: ubuntu-latest
steps:
- run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache keys"
cacheKeys=$(gh actions-cache list -R $GITHUB_REPOSITORY -B $BRANCH | cut -f 1 )
echo "Deleting caches..."
for cacheKey in $cacheKeys; do
gh actions-cache delete $cacheKey -R $GITHUB_REPOSITORY -B $BRANCH --confirm
done
shell: bash
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge

View File

@ -9,134 +9,163 @@ on:
paths-ignore: [ '**.md', '**/docs/**' ]
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: false
- uses: golangci/golangci-lint-action@v8
with:
version: v2.1.6
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Build
run: make build build-test-tools
- name: Test
run: make check test
- name: Archive built binaries
run: tar -cvf built-binaries.tar helmfile diff-yamls dyff
- uses: actions/upload-artifact@v5
with:
name: built-binaries-${{ github.run_id }}
path: built-binaries.tar
retention-days: 1
- name: Display built binaries
run: ls -l helmfile diff-yamls dyff
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Cache libraries
uses: actions/cache@v2
env:
cache-name: cache-go
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
- name: Env
run: go env
- name: Build
run: make build build-test-tools
- name: Test
run: make check test
- name: Prepare tar to upload built binaries
run: tar -cvf built-binaries.tar helmfile diff-yamls dyff
- name: Upload built binaries
uses: actions/upload-artifact@v2
with:
name: built-binaries-${{ github.run_id }}
path: built-binaries.tar
retention-days: 1
- name: Display built binaries
run: ls -l helmfile diff-yamls dyff
integration_tests:
needs: tests
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 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.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.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.
# Once it turned out to be not practically true,
# we will mark this combination as failable,
# and instruct users to upgrade helm and helm-secrets at once.
plugin-secrets-version: 4.6.5
plugin-diff-version: 3.12.5
extra-helmfile-flags: ''
- 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.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.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'
include:
# 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.11.3
kustomize-version: v4.4.1
plugin-secrets-version: 3.15.0
plugin-diff-version: 3.7.0
extra-helmfile-flags:
v1mode:
- helm-version: v3.11.3
kustomize-version: v4.5.7
# We assume that the helm-secrets plugin is supposed to
# work with the two most recent helm minor versions.
# Once it turned out to be not practically true,
# we will mark this combination as failable,
# and instruct users to upgrade helm and helm-secrets at once.
plugin-secrets-version: 4.0.0
plugin-diff-version: 3.8.1
extra-helmfile-flags:
v1mode:
- helm-version: v3.12.1
kustomize-version: v4.4.1
plugin-secrets-version: 3.15.0
plugin-diff-version: 3.7.0
extra-helmfile-flags:
v1mode:
- helm-version: v3.12.1
kustomize-version: v4.5.7
plugin-secrets-version: 4.0.0
plugin-diff-version: 3.8.1
extra-helmfile-flags:
v1mode:
# Helmfile v1
- helm-version: v3.12.1
kustomize-version: v4.5.7
plugin-secrets-version: 4.0.0
plugin-diff-version: 3.8.1
extra-helmfile-flags:
v1mode: "true"
# In case you need to test some optional helmfile features,
# enable it via extra-helmfile-flags below.
- helm-version: v3.12.1
kustomize-version: v4.5.7
plugin-secrets-version: 4.0.0
plugin-diff-version: 3.8.1
extra-helmfile-flags: "--enable-live-output"
v1mode:
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- uses: actions/checkout@v2
- name: Cache libraries
uses: actions/cache@v2
env:
cache-name: cache-go
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
- name: Env
run: go env
- uses: actions/download-artifact@v6
with:
name: built-binaries-${{ github.run_id }}
- name: install semver
run: go install github.com/ffurrer2/semver/v2/cmd/semver@latest
- name: Extract tar to get built binaries
run: tar -xvf built-binaries.tar
- name: Display built binaries
run: ls -l helmfile diff-yamls dyff
- name: Install test dependencies
env:
HELM_VERSION: ${{ matrix.helm-version }}
KUSTOMIZE_VERSION: ${{ matrix.kustomize-version }}
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:
HELM_SECRETS_VERSION: ${{ matrix.plugin-secrets-version }}
HELM_DIFF_VERSION: ${{ matrix.plugin-diff-version }}
HELMFILE_HELM3: 1
TERM: xterm
EXTRA_HELMFILE_FLAGS: ${{ matrix.extra-helmfile-flags }}
- name: Download built binaries
uses: actions/download-artifact@v2
with:
name: built-binaries-${{ github.run_id }}
- name: Extract tar to get built binaries
run: tar -xvf built-binaries.tar
- name: Display built binaries
run: ls -l helmfile diff-yamls dyff
- name: Install test dependencies
env:
HELM_VERSION: ${{ matrix.helm-version }}
KUSTOMIZE_VERSION: ${{ matrix.kustomize-version }}
run: make -C .github/workflows helm vault sops kustomize
- name: Start minikube
uses: medyagh/setup-minikube@master
- name: Execute integration tests
env:
HELM_SECRETS_VERSION: ${{ matrix.plugin-secrets-version }}
HELM_DIFF_VERSION: ${{ matrix.plugin-diff-version }}
HELMFILE_HELM3: 1
TERM: xterm
EXTRA_HELMFILE_FLAGS: ${{ matrix.extra-helmfile-flags }}
HELMFILE_V1MODE: ${{ matrix.v1mode }}
run: make integration
e2e_tests:
needs: tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/download-artifact@v6
- name: Install package
run: |
sudo apt-get -y install expect
- name: Download built binaries
uses: actions/download-artifact@v2
with:
name: built-binaries-${{ github.run_id }}
- name: Extract tar to get built binaries
run: tar -xvf built-binaries.tar
- name: Display built binaries
run: ls -l helmfile diff-yamls dyff
- name: Install package
run: sudo apt-get -y install expect
- name: Run helmfile init
run: bash test/e2e/helmfile-init/init_linux.sh
env:
TERM: xterm
run: bash test/e2e/helmfile-init/init_linux.sh

View File

@ -39,21 +39,21 @@ jobs:
suffix: "-ubuntu"
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}${{ matrix.image.suffix }}
flavor: latest=false
@ -64,14 +64,14 @@ jobs:
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build / Push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v3
with:
context: .
file: ${{ matrix.image.dockerfile }}

30
.github/workflows/lint.yaml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Lint
on:
push:
branches: [ main ]
paths-ignore: [ '**.md', '**/docs/**' ]
pull_request:
branches: [ main ]
paths-ignore: [ '**.md', '**/docs/**' ]
env:
GO_VERSION: 1.20
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/setup-go@v3
with:
go-version: '1.20'
- name: Checkout code
uses: actions/checkout@v3
- name: Golangci lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.51.0

View File

@ -21,7 +21,7 @@ jobs:
lock:
runs-on: 'ubuntu-latest'
steps:
- uses: 'dessant/lock-threads@v5'
- uses: 'dessant/lock-threads@v2'
with:
github-token: '${{ github.token }}'
issue-lock-inactive-days: 1

29
.github/workflows/publish_binaries.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Publish v0.x Binaries
on:
push:
branches:
- "!*"
tags:
- "v0*"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v1
-
name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,33 @@
name: Publish v1.x Binaries
on:
push:
branches:
- "!*"
tags:
- "v1*"
env:
# This is referenced from .goreleaser.yml
HELMFILE_V1MODE: "true"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v1
-
name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,47 +0,0 @@
name: GoReleaser
on:
push:
tags:
- 'v0*'
- 'v1*'
branches:
- 'main'
pull_request:
branches:
- 'main'
permissions:
contents: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SNAPSHOT: ${{ !startsWith(github.ref, 'refs/tags/v') && '--snapshot' || '' }}
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- 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
args: release --clean ${{ env.SNAPSHOT }}

View File

@ -0,0 +1,26 @@
name: Test Binary Builds
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v1
-
name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '1.20'
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist --snapshot

1
.gitignore vendored
View File

@ -11,4 +11,3 @@ vendor/
*.lock
test/integration/.gnupg/
.vscode/launch.json
cover.out

View File

@ -1,146 +1,360 @@
version: "2"
# This file contains all available configuration options
# with their default values.
# options for analysis running
run:
# default concurrency is a available CPU number
# concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 30m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
# include test files or not, default is true
tests: true
# list of build tags, all linters use it. Default is empty list.
# build-tags:
# - mytag
# which dirs to skip: issues from them won't be reported;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but default dirs are skipped independently
# from this option's value (see skip-dirs-use-default).
# skip-dirs:
# - src/external_libs
# - autogenerated_by_my_lib
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
# skip-files:
# - ".*\\.my\\.go$"
# - lib/bad.go
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
# to go.mod are needed. This setting is most useful to check that go.mod does
# not need updates, such as in a continuous integration and testing system.
# If invoked with -mod=vendor, the go command assumes that the vendor
# directory holds the correct copies of dependencies and ignores
# the dependency descriptions in go.mod.
# modules-download-mode: readonly|release|vendor
# output configuration options
output:
formats:
text:
path: stdout
print-linter-name: true
print-issued-lines: true
colors: false
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
format: line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# all available settings of specific linters
linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/helmfile/helmfile)
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
# [deprecated] comma-separated list of pairs of the form pkg:regex
# the regex is used to ignore names within pkg. (default "fmt:.*").
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
# ignore: fmt:.*
# path to a file containing a list of functions to exclude from checking
# see https://github.com/kisielk/errcheck#excluding-functions for details
# exclude: /path/to/file.txt
# Disable error checking, as errorcheck detects more errors and is more configurable.
gosec:
exclude:
- "G104"
govet:
# report about shadowed variables
check-shadowing: false
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
# enable or disable analyzers by name
# enable:
# - atomicalign
# enable-all: false
# disable:
# - shadow
# disable-all: false
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
# local-prefixes: github.com/org/project
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 30
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 100
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
goconst:
# minimal length of string constant, 3 by default
min-len: 3
# minimal occurrences count to trigger, 3 by default
min-occurrences: 8
# depguard:
# list-type: blacklist
# include-go-root: false
# packages:
# - github.com/sirupsen/logrus
# packages-with-error-messages:
# # specify an error message to output when a blacklisted package is used
# github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
ignore-words:
- GitLab
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 120
# tab width in spaces. Default to 1.
tab-width: 1
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
# True by default.
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
gocritic:
# Which checks should be enabled; can't be combined with 'disabled-checks';
# See https://go-critic.github.io/overview#checks-overview
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
# By default list of stable checks is used.
# enabled-checks:
# - rangeValCopy
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
# disabled-checks:
# - regexpMust
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
# enabled-tags:
# - performance
settings: # settings passed to gocritic
captLocal: # must be valid enabled check name
paramsOnly: true
# rangeValCopy:
# sizeThreshold: 32
godox:
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
# might be left in the code accidentally and should be resolved before merging
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
- TODO
- BUG
- FIXME
- NOTE
- OPTIMIZE # marks code that should be optimized before merging
- HACK # marks hack-arounds that should be removed before merging
dogsled:
# checks assignments with too many blank identifiers; default is 2
max-blank-identifiers: 2
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
wsl:
# If true append is only allowed to be cuddled if appending value is
# matching variables, fields or types on line above. Default is true.
strict-append: true
# Allow calls and assignments to be cuddled as long as the lines have any
# matching variables, fields or types. Default is true.
allow-assign-and-call: true
# Allow multiline assignments to be cuddled. Default is true.
allow-multiline-assign: true
# Allow declarations (var) to be cuddled.
allow-cuddle-declarations: false
# Allow trailing comments in ending of blocks
allow-trailing-comment: false
# Force newlines in end of case at this limit (0 = never).
force-case-trailing-whitespace: 0
revive:
ignore-generated-header: true
severity: warning
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 280
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 140
linters:
default: none
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- copyloopvar
- depguard
- usestdlibvars
- reassign
- errcheck
- funlen
- gocognit
- goconst
- gofmt
- goimports
- revive
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- reassign
- revive
- exportloopref
- staticcheck
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
settings:
staticcheck:
checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-ST1005", "-QF1001", "-QF1008"]
dogsled:
max-blank-identifiers: 2
dupl:
threshold: 100
errcheck:
check-type-assertions: false
check-blank: false
funlen:
lines: 280
statements: 140
gocognit:
min-complexity: 110
goconst:
min-len: 3
min-occurrences: 8
gocritic:
settings:
captLocal:
paramsOnly: true
gocyclo:
min-complexity: 30
godox:
keywords:
- TODO
- BUG
- FIXME
- NOTE
- OPTIMIZE
- HACK
gosec:
excludes:
- G104
govet:
disable:
- shadow
settings:
printf:
funcs:
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf
lll:
line-length: 120
tab-width: 1
misspell:
locale: US
ignore-rules:
- GitLab
nakedret:
max-func-lines: 50
prealloc:
simple: true
range-loops: true
for-loops: false
revive:
confidence: 0.8
severity: warning
unparam:
check-exported: false
whitespace:
multi-if: false
multi-func: false
wsl:
strict-append: true
allow-assign-and-call: true
allow-multiline-assign: true
force-case-trailing-whitespace: 0
allow-trailing-comment: false
allow-cuddle-declarations: false
exclusions:
generated: lax
rules:
- linters:
- dupl
- errcheck
- funlen
- gocyclo
- gosec
path: _test\.go
- linters:
- lll
source: '^//go:generate '
paths:
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
new: false
formatters:
enable:
- gci
- gofmt
- goimports
settings:
gci:
sections:
- standard
- default
- prefix(github.com/helmfile/helmfile)
gofmt:
simplify: true
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
# - gocritic
# - godox
# - gosec
# - interfacer
# - stylecheck
# - dogsled
# - dupl
# don't enable:
# - deadcode
# - gochecknoglobals
# - gochecknoinits
# - gocyclo
# - lll
# - maligned
# - prealloc
# - varcheck
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
# exclude:
# - abcdef
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- funlen
# Exclude known linters from partially hard-vendored code,
# which is impossible to exclude via "nolint" comments.
# - path: internal/hmac/
# text: "weak cryptographic primitive"
# linters:
# - gosec
# Exclude some staticcheck messages
# - linters:
# - staticcheck
# text: "SA9003:"
# Exclude lll issues for long lines with go:generate
- linters:
- lll
source: "^//go:generate "
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: false
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-issues-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0
# Show only new issues: if there are unstaged changes or untracked files,
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
# It's a super-useful option for integration of golangci-lint into existing
# large codebase. It's not practical to fix all existing issues at the moment
# of integration: much better don't allow issues in new code.
# Default is false.
new: false
# Show only new issues created after git revision `REV`
# This should be passed as flag during individual CI jobs.
# new-from-rev: REV
# Show only new issues created in git patch with set file path.
# new-from-patch: path/to/patch/file

View File

@ -1,5 +1,7 @@
version: 2
project_name: helmfile
env:
# We default to non-v1 mode (=helmfile v0.x behavior) when HELMFILE_V1MODE is not set
- HELMFILE_V1MODE={{ if index .Env "HELMFILE_V1MODE" }}{{ .Env.HELMFILE_V1MODE }}{{ else }}false{{ end }}
builds:
- id: helmfile
main: .
@ -12,6 +14,7 @@ builds:
- -X go.szostok.io/version.commit={{.FullCommit}}
- -X go.szostok.io/version.commitDate={{.CommitDate}}
- -X go.szostok.io/version.dirtyBuild=false
- -X github.com/helmfile/helmfile/pkg/runtime.v1Mode={{.Env.HELMFILE_V1MODE}}
goos:
- darwin
- linux
@ -20,26 +23,5 @@ builds:
- amd64
- arm64
- "386"
archives:
- id: helmfile
ids:
- helmfile
builds_info:
group: root
owner: root
mode: 0644
changelog:
use: github
sort: asc
groups:
- title: Features
regexp: '^.*[Ff]eat[(\\w)]*:+.*$'
order: 0
- title: "Fixes"
regexp: '^.*fix[(\\w)]*.*$'
order: 1
- title: "Dependencies"
regexp: '^.*(deps|bump)[(\\w)]*.*$'
order: 2
- title: Others
order: 999
use: github-native

View File

@ -4,11 +4,6 @@ mkdocs:
configuration: mkdocs.yml
fail_on_warning: false
build:
os: ubuntu-24.04
tools:
python: "3.12"
python:
install:
- requirements: docs/requirements.txt

View File

@ -1,2 +1,2 @@
# Helmfile Maintainers
* @mumoshu @itscaro @yxxhero @xiaomudk @zhaque44
* @mumoshu @itscaro @yxxhero @xiaomudk

View File

@ -4,7 +4,7 @@ By contributing to `helmfile`, you accept and agree to the following DCO and lic
conditions for your present and future Contributions submitted to the `helmfile` project.
[DCO](https://developercertificate.org/)
[License](https://github.com/helmfile/helmfile/blob/main/LICENSE)
[License](https://github.com/helmfile/helmfile/blob/master/LICENSE)
# Developing helmfile
@ -15,7 +15,7 @@ $ go get github.com/helmfile/helmfile
$ cd $GOPATH/src/github.com/helmfile/helmfile
$ git checkout -b your-shiny-new-feature origin/main
$ git checkout -b your-shiny-new-feature origin/master
...

View File

@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.20-alpine as builder
RUN apk add --no-cache make git
WORKDIR /workspace/helmfile
@ -12,11 +12,11 @@ RUN make static-${TARGETOS}-${TARGETARCH}
# -----------------------------------------------------------------------------
FROM alpine:3.22
FROM alpine:3.16
LABEL org.opencontainers.image.source=https://github.com/helmfile/helmfile
LABEL org.opencontainers.image.source https://github.com/helmfile/helmfile
RUN apk add --no-cache ca-certificates git bash curl jq yq openssh-client gnupg
RUN apk add --no-cache ca-certificates git bash curl jq 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.19.0"
ARG HELM_VERSION="v3.12.1"
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="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
"linux/amd64") HELM_SHA256="1a7074f58ef7190f74ce6db5db0b70e355a655e2013c4d5db2317e63fa9e3dea" ;; \
"linux/arm64") HELM_SHA256="50548d4fedef9d8d01d1ed5a2dd5c849271d1017127417dc4c7ef6777ae68f7e" ;; \
esac && \
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
echo Extracting ${HELM_FILENAME}... && \
@ -50,41 +50,40 @@ RUN set -x && \
# using the install documentation found at https://kubernetes.io/docs/tasks/tools/install-kubectl/
# for now but in a future version of alpine (in the testing version at the time of writing)
# we should be able to install using apk add.
ENV KUBECTL_VERSION="v1.32.1"
ENV KUBECTL_VERSION="v1.25.2"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl" && \
case ${TARGETPLATFORM} in \
"linux/amd64") KUBECTL_SHA256="e16c80f1a9f94db31063477eb9e61a2e24c1a4eee09ba776b029048f5369db0c" ;; \
"linux/arm64") KUBECTL_SHA256="98206fd83a4fd17f013f8c61c33d0ae8ec3a7c53ec59ef3d6a0a9400862dc5b2" ;; \
"linux/amd64") KUBECTL_SHA256="8639f2b9c33d38910d706171ce3d25be9b19fc139d0e3d4627f38ce84f9040eb" ;; \
"linux/arm64") KUBECTL_SHA256="b26aa656194545699471278ad899a90b1ea9408d35f6c65e3a46831b9c063fd5" ;; \
esac && \
echo "${KUBECTL_SHA256} kubectl" | sha256sum -c && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl && \
[ "$(kubectl version -o json | jq -r '.clientVersion.gitVersion')" = "${KUBECTL_VERSION}" ]
ENV KUSTOMIZE_VERSION="v5.4.3"
ENV KUSTOMIZE_VERSION="v4.5.7"
ARG KUSTOMIZE_FILENAME="kustomize_${KUSTOMIZE_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/${KUSTOMIZE_FILENAME}" && \
case ${TARGETPLATFORM} in \
# Checksums are available at https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/checksums.txt
"linux/amd64") KUSTOMIZE_SHA256="3669470b454d865c8184d6bce78df05e977c9aea31c30df3c669317d43bcc7a7" ;; \
"linux/arm64") KUSTOMIZE_SHA256="1b515578b0af12c15d9856720066ce2fe66756d63785b2cbccaf2885beb2381c" ;; \
"linux/amd64") KUSTOMIZE_SHA256="701e3c4bfa14e4c520d481fdf7131f902531bfc002cb5062dcf31263a09c70c9" ;; \
"linux/arm64") KUSTOMIZE_SHA256="65665b39297cc73c13918f05bbe8450d17556f0acd16242a339271e14861df67" ;; \
esac && \
echo "${KUSTOMIZE_SHA256} ${KUSTOMIZE_FILENAME}" | sha256sum -c && \
tar xvf "${KUSTOMIZE_FILENAME}" -C /usr/local/bin && \
rm "${KUSTOMIZE_FILENAME}" && \
[ "$(kustomize version)" = "${KUSTOMIZE_VERSION}" ]
kustomize version --short | grep "kustomize/${KUSTOMIZE_VERSION}"
ENV SOPS_VERSION="v3.10.2"
ENV SOPS_VERSION="v3.7.3"
ARG SOPS_FILENAME="sops-${SOPS_VERSION}.${TARGETOS}.${TARGETARCH}"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
curl --retry 5 --retry-connrefused -LO "https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
chmod +x "${SOPS_FILENAME}" && \
mv "${SOPS_FILENAME}" /usr/local/bin/sops && \
sops --version --disable-version-check | grep -E "^sops ${SOPS_VERSION#v}"
sops --version | grep -E "^sops ${SOPS_VERSION#v}"
ENV AGE_VERSION="v1.2.1"
ENV AGE_VERSION="v1.0.0"
ARG AGE_FILENAME="age-${AGE_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/${AGE_FILENAME}" && \
@ -93,10 +92,10 @@ RUN set -x && \
[ "$(age --version)" = "${AGE_VERSION}" ] && \
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
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 && \
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.8.1 && \
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.1.1 && \
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.14.0 && \
helm plugin install https://github.com/aslafy-z/helm-git.git --version v0.12.0 && \
rm -rf ${HELM_CACHE_HOME}/plugins
# Allow users other than root to use helm plugins located in root home

View File

@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.20-alpine as builder
RUN apk add --no-cache make git
WORKDIR /workspace/helmfile
@ -14,20 +14,17 @@ RUN make static-${TARGETOS}-${TARGETARCH}
FROM debian:stable-slim
LABEL org.opencontainers.image.source=https://github.com/helmfile/helmfile
LABEL org.opencontainers.image.source https://github.com/helmfile/helmfile
RUN apt update -qq && \
apt install --no-install-recommends -y \
ca-certificates \
gnupg \
git bash curl jq wget openssh-client && \
ca-certificates \
gnupg \
git bash curl jq wget openssh-client && \
rm -rf /var/lib/apt/lists/*
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}"
@ -38,7 +35,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.19.0"
ARG HELM_VERSION="v3.12.1"
ENV HELM_VERSION="${HELM_VERSION}"
ARG HELM_LOCATION="https://get.helm.sh"
ARG HELM_FILENAME="helm-${HELM_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
@ -46,8 +43,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="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
"linux/amd64") HELM_SHA256="1a7074f58ef7190f74ce6db5db0b70e355a655e2013c4d5db2317e63fa9e3dea" ;; \
"linux/arm64") HELM_SHA256="50548d4fedef9d8d01d1ed5a2dd5c849271d1017127417dc4c7ef6777ae68f7e" ;; \
esac && \
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
echo Extracting ${HELM_FILENAME}... && \
@ -58,42 +55,40 @@ RUN set -x && \
# using the install documentation found at https://kubernetes.io/docs/tasks/tools/install-kubectl/
# for now but in a future version of alpine (in the testing version at the time of writing)
# we should be able to install using apk add.
ENV KUBECTL_VERSION="v1.32.1"
ENV KUBECTL_VERSION="v1.25.2"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl" && \
case ${TARGETPLATFORM} in \
# checksums are available at https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl.sha256
"linux/amd64") KUBECTL_SHA256="e16c80f1a9f94db31063477eb9e61a2e24c1a4eee09ba776b029048f5369db0c" ;; \
"linux/arm64") KUBECTL_SHA256="98206fd83a4fd17f013f8c61c33d0ae8ec3a7c53ec59ef3d6a0a9400862dc5b2" ;; \
"linux/amd64") KUBECTL_SHA256="8639f2b9c33d38910d706171ce3d25be9b19fc139d0e3d4627f38ce84f9040eb" ;; \
"linux/arm64") KUBECTL_SHA256="b26aa656194545699471278ad899a90b1ea9408d35f6c65e3a46831b9c063fd5" ;; \
esac && \
echo "${KUBECTL_SHA256} kubectl" | sha256sum -c && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl && \
[ "$(kubectl version -o json | jq -r '.clientVersion.gitVersion')" = "${KUBECTL_VERSION}" ]
ENV KUSTOMIZE_VERSION="v5.4.3"
ENV KUSTOMIZE_VERSION="v4.5.7"
ARG KUSTOMIZE_FILENAME="kustomize_${KUSTOMIZE_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/${KUSTOMIZE_FILENAME}" && \
case ${TARGETPLATFORM} in \
# Checksums are available at https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/checksums.txt
"linux/amd64") KUSTOMIZE_SHA256="3669470b454d865c8184d6bce78df05e977c9aea31c30df3c669317d43bcc7a7" ;; \
"linux/arm64") KUSTOMIZE_SHA256="1b515578b0af12c15d9856720066ce2fe66756d63785b2cbccaf2885beb2381c" ;; \
"linux/amd64") KUSTOMIZE_SHA256="701e3c4bfa14e4c520d481fdf7131f902531bfc002cb5062dcf31263a09c70c9" ;; \
"linux/arm64") KUSTOMIZE_SHA256="65665b39297cc73c13918f05bbe8450d17556f0acd16242a339271e14861df67" ;; \
esac && \
echo "${KUSTOMIZE_SHA256} ${KUSTOMIZE_FILENAME}" | sha256sum -c && \
tar xvf "${KUSTOMIZE_FILENAME}" -C /usr/local/bin && \
rm "${KUSTOMIZE_FILENAME}" && \
[ "$(kustomize version)" = "${KUSTOMIZE_VERSION}" ]
kustomize version --short | grep "kustomize/${KUSTOMIZE_VERSION}"
ENV SOPS_VERSION="v3.10.2"
ENV SOPS_VERSION="v3.7.3"
ARG SOPS_FILENAME="sops-${SOPS_VERSION}.${TARGETOS}.${TARGETARCH}"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
curl --retry 5 --retry-connrefused -LO "https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
chmod +x "${SOPS_FILENAME}" && \
mv "${SOPS_FILENAME}" /usr/local/bin/sops && \
sops --version --disable-version-check | grep -E "^sops ${SOPS_VERSION#v}"
sops --version | grep -E "^sops ${SOPS_VERSION#v}"
ENV AGE_VERSION="v1.2.1"
ENV AGE_VERSION="v1.0.0"
ARG AGE_FILENAME="age-${AGE_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/${AGE_FILENAME}" && \
@ -102,10 +97,10 @@ RUN set -x && \
[ "$(age --version)" = "${AGE_VERSION}" ] && \
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
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 && \
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.8.1 && \
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.1.1 && \
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.14.0 && \
helm plugin install https://github.com/aslafy-z/helm-git.git --version v0.12.0 && \
rm -rf ${HELM_CACHE_HOME}/plugins
# Allow users other than root to use helm plugins located in root home

View File

@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder
FROM --platform=$BUILDPLATFORM golang:1.20-alpine as builder
RUN apk add --no-cache make git
WORKDIR /workspace/helmfile
@ -12,22 +12,19 @@ RUN make static-${TARGETOS}-${TARGETARCH}
# -----------------------------------------------------------------------------
FROM ubuntu:24.04
FROM ubuntu:20.04
LABEL org.opencontainers.image.source=https://github.com/helmfile/helmfile
LABEL org.opencontainers.image.source https://github.com/helmfile/helmfile
RUN apt update -qq && \
apt install --no-install-recommends -y \
ca-certificates \
gnupg \
git bash curl jq wget openssh-client && \
ca-certificates \
gnupg \
git bash curl jq wget openssh-client && \
rm -rf /var/lib/apt/lists/*
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}"
@ -38,7 +35,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.19.0"
ARG HELM_VERSION="v3.12.1"
ENV HELM_VERSION="${HELM_VERSION}"
ARG HELM_LOCATION="https://get.helm.sh"
ARG HELM_FILENAME="helm-${HELM_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
@ -46,8 +43,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="a7f81ce08007091b86d8bd696eb4d86b8d0f2e1b9f6c714be62f82f96a594496" ;; \
"linux/arm64") HELM_SHA256="440cf7add0aee27ebc93fada965523c1dc2e0ab340d4348da2215737fc0d76ad" ;; \
"linux/amd64") HELM_SHA256="1a7074f58ef7190f74ce6db5db0b70e355a655e2013c4d5db2317e63fa9e3dea" ;; \
"linux/arm64") HELM_SHA256="50548d4fedef9d8d01d1ed5a2dd5c849271d1017127417dc4c7ef6777ae68f7e" ;; \
esac && \
echo "${HELM_SHA256} ${HELM_FILENAME}" | sha256sum -c && \
echo Extracting ${HELM_FILENAME}... && \
@ -58,42 +55,40 @@ RUN set -x && \
# using the install documentation found at https://kubernetes.io/docs/tasks/tools/install-kubectl/
# for now but in a future version of alpine (in the testing version at the time of writing)
# we should be able to install using apk add.
ENV KUBECTL_VERSION="v1.32.1"
ENV KUBECTL_VERSION="v1.25.2"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl" && \
case ${TARGETPLATFORM} in \
# checksums are available at https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl.sha256
"linux/amd64") KUBECTL_SHA256="e16c80f1a9f94db31063477eb9e61a2e24c1a4eee09ba776b029048f5369db0c" ;; \
"linux/arm64") KUBECTL_SHA256="98206fd83a4fd17f013f8c61c33d0ae8ec3a7c53ec59ef3d6a0a9400862dc5b2" ;; \
"linux/amd64") KUBECTL_SHA256="8639f2b9c33d38910d706171ce3d25be9b19fc139d0e3d4627f38ce84f9040eb" ;; \
"linux/arm64") KUBECTL_SHA256="b26aa656194545699471278ad899a90b1ea9408d35f6c65e3a46831b9c063fd5" ;; \
esac && \
echo "${KUBECTL_SHA256} kubectl" | sha256sum -c && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl && \
[ "$(kubectl version -o json | jq -r '.clientVersion.gitVersion')" = "${KUBECTL_VERSION}" ]
ENV KUSTOMIZE_VERSION="v5.4.3"
ENV KUSTOMIZE_VERSION="v4.5.7"
ARG KUSTOMIZE_FILENAME="kustomize_${KUSTOMIZE_VERSION}_${TARGETOS}_${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/${KUSTOMIZE_FILENAME}" && \
case ${TARGETPLATFORM} in \
# Checksums are available at https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/${KUSTOMIZE_VERSION}/checksums.txt
"linux/amd64") KUSTOMIZE_SHA256="3669470b454d865c8184d6bce78df05e977c9aea31c30df3c669317d43bcc7a7" ;; \
"linux/arm64") KUSTOMIZE_SHA256="1b515578b0af12c15d9856720066ce2fe66756d63785b2cbccaf2885beb2381c" ;; \
"linux/amd64") KUSTOMIZE_SHA256="701e3c4bfa14e4c520d481fdf7131f902531bfc002cb5062dcf31263a09c70c9" ;; \
"linux/arm64") KUSTOMIZE_SHA256="65665b39297cc73c13918f05bbe8450d17556f0acd16242a339271e14861df67" ;; \
esac && \
echo "${KUSTOMIZE_SHA256} ${KUSTOMIZE_FILENAME}" | sha256sum -c && \
tar xvf "${KUSTOMIZE_FILENAME}" -C /usr/local/bin && \
rm "${KUSTOMIZE_FILENAME}" && \
[ "$(kustomize version)" = "${KUSTOMIZE_VERSION}" ]
kustomize version --short | grep "kustomize/${KUSTOMIZE_VERSION}"
ENV SOPS_VERSION="v3.10.2"
ENV SOPS_VERSION="v3.7.3"
ARG SOPS_FILENAME="sops-${SOPS_VERSION}.${TARGETOS}.${TARGETARCH}"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
curl --retry 5 --retry-connrefused -LO "https://github.com/mozilla/sops/releases/download/${SOPS_VERSION}/${SOPS_FILENAME}" && \
chmod +x "${SOPS_FILENAME}" && \
mv "${SOPS_FILENAME}" /usr/local/bin/sops && \
sops --version --disable-version-check | grep -E "^sops ${SOPS_VERSION#v}"
sops --version | grep -E "^sops ${SOPS_VERSION#v}"
ENV AGE_VERSION="v1.2.1"
ENV AGE_VERSION="v1.0.0"
ARG AGE_FILENAME="age-${AGE_VERSION}-${TARGETOS}-${TARGETARCH}.tar.gz"
RUN set -x && \
curl --retry 5 --retry-connrefused -LO "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/${AGE_FILENAME}" && \
@ -102,10 +97,10 @@ RUN set -x && \
[ "$(age --version)" = "${AGE_VERSION}" ] && \
[ "$(age-keygen --version)" = "${AGE_VERSION}" ]
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 && \
RUN helm plugin install https://github.com/databus23/helm-diff --version v3.8.1 && \
helm plugin install https://github.com/jkroepke/helm-secrets --version v4.1.1 && \
helm plugin install https://github.com/hypnoglow/helm-s3.git --version v0.14.0 && \
helm plugin install https://github.com/aslafy-z/helm-git.git --version v0.12.0 && \
rm -rf ${HELM_CACHE_HOME}/plugins
# Allow users other than root to use helm plugins located in root home

View File

@ -25,6 +25,10 @@ build:
go build -ldflags="$(GO_BUILD_VERSION_LDFLAGS)" ${TARGETS}
.PHONY: build
build-v1:
go build -ldflags="$(GO_BUILD_VERSION_LDFLAGS) -X github.com/helmfile/helmfile/pkg/runtime.v1Mode=true" ${TARGETS}
.PHONY: build-v1
generate:
go generate ${PKGS}
.PHONY: generate

View File

@ -12,12 +12,10 @@
# Helmfile
[![Tests](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml?query=branch%3Amain)
[![Container Image Repository on GHCR](https://ghcr-badge.egpl.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Container Image Repository on GHCR](https://ghcr-badge.deta.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Go Report Card](https://goreportcard.com/badge/github.com/helmfile/helmfile)](https://goreportcard.com/report/github.com/helmfile/helmfile)
[![Slack Community #helmfile](https://slack.sweetops.com/badge.svg)](https://slack.sweetops.com)
[![Documentation](https://readthedocs.org/projects/helmfile/badge/?version=latest&style=flat)](https://helmfile.readthedocs.io/en/latest/)
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Helmfile%20Guru-006BFF)](https://gurubase.io/g/helmfile)
[![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/helmfile/helmfile)
声明式Helm Chart管理工具
<br />

View File

@ -12,12 +12,10 @@
# Helmfile
[![Tests](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml?query=branch%3Amain)
[![Container Image Repository on GHCR](https://ghcr-badge.egpl.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Container Image Repository on GHCR](https://ghcr-badge.deta.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Go Report Card](https://goreportcard.com/badge/github.com/helmfile/helmfile)](https://goreportcard.com/report/github.com/helmfile/helmfile)
[![Slack Community #helmfile](https://slack.sweetops.com/badge.svg)](https://slack.sweetops.com)
[![Documentation](https://readthedocs.org/projects/helmfile/badge/?version=latest&style=flat)](https://helmfile.readthedocs.io/en/latest/)
[![Gurubase](https://img.shields.io/badge/Gurubase-Ask%20Helmfile%20Guru-006BFF)](https://gurubase.io/g/helmfile)
[![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/helmfile/helmfile)
Deploy Kubernetes Helm Charts
<br />
@ -34,9 +32,7 @@ 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, the following must be installed
- [helm](https://helm.sh/docs/intro/install/)
- [helm-diff](https://github.com/databus23/helm-diff)
To avoid upgrades for each iteration of `helm`, the `helmfile` executable delegates to `helm` - as a result, `helm` must be installed.
## Highlights
@ -50,10 +46,14 @@ To avoid upgrades for each iteration of `helm`, the `helmfile` executable delega
## Status
May 2025 Update
March 2022 Update - The helmfile project has been moved to [helmfile/helmfile](https://github.com/helmfile/helmfile) from the former home `roboll/helmfile`. Please see roboll/helmfile#1824 for more information.
Even though Helmfile is used in production environments [across multiple organizations](USERS.md), it is still in its early stage of development, hence versioned 0.x.
Helmfile complies to Semantic Versioning 2.0.0 in which v0.x means that there could be backward-incompatible changes for every release.
Note that we will try our best to document any backward incompatibility. And in reality, helmfile had no breaking change for a year or so.
* Helmfile v1.0 and v1.1 has been released. We recommend upgrading directly to v1.1 if you are still using v0.x.
* If you haven't already upgraded, please go over this v1 proposal [here](docs/proposals/towards-1.0.md) to see a small list of breaking changes.
## Installation
@ -109,10 +109,6 @@ Iterate on the `helmfile.yaml` by referencing:
* [CLI reference](https://helmfile.readthedocs.io/en/latest/#cli-reference)
* [Helmfile Best Practices Guide](https://helmfile.readthedocs.io/en/latest/writing-helmfile/)
## More complex examples
See: [multi-env-helmfile](https://github.com/helmfile/multi-env-helmfile)
## Docs
Please read [complete documentation](https://helmfile.readthedocs.io/)

View File

@ -36,9 +36,4 @@ information to this file.
| [subshell](https://subshell.com) | production | We're using helmfile since 2021 to deploy all our Kubernetes workloads into our clusters. We love helmfile for its simplicity and power. Thank you! | Hamburg, Germany | August 2022 |
| [Norddeutscher Rundfunk](https://www.ndr.de) | production | Using Helmfile since 2020 to deploy workloads to several similar clusters (dev, qa, prod, test, etc.) for sites tagesschau.de and sportschau.de. Thank you so much for your awesome work! | Hamburg, Germany | August 2022 |
| [Dealhub](https://dealhub.io/) | production | Helmfile was an essential part of our k8s migration. Keep up the good work! | Holon, Israel | January 2023 |
| [BlueLabs](https://bluelabs.eu/) | production | Helmfile is the cornerstone of our lightweight, auditable and centralized GKE deployments. | Europe | February 2021 |
| [Zhihu](https://www.zhihu.com/) | production | helmfile is an important tool for the deployment of our basic components, which can achieve standardization and auditability. |China, Beijing | December 2023 |
| [Tudock](https://tudock.de) | production | We have no public post about it, but we recently started using Helmfile to deploy developer applications and have been very happy with the results! | Hamburg, Germany | March 2024 |
| [Incentive.me](https://incentive.me/) | production | We use helmfile as the main tool for deploying our Kubernetes workloads. | Rio de Janeiro, Brazil | November 2021 |
| [RightCapital](https://www.rightcapital.com/) | production | We use helmfile as the main tool for deploying our Kubernetes workloads. | Shelton, CT, USA | May 2019 |
<!-- TABLE_END -->

19
Vagrantfile vendored Normal file
View File

@ -0,0 +1,19 @@
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/focal64"
config.vm.hostname = "minikube.box"
config.vm.provision :shell, privileged: false,
inline: <<-EOS
set -e
sudo apt-get update
sudo apt-get install -y make docker.io
sudo systemctl start docker
sudo usermod -G docker $USER
cd /vagrant/.circleci
make all
EOS
config.vm.provider "virtualbox" do |v|
v.memory = 2048
v.cpus = 2
end
end

View File

@ -5,6 +5,7 @@ import (
"github.com/helmfile/helmfile/pkg/app"
"github.com/helmfile/helmfile/pkg/config"
"github.com/helmfile/helmfile/pkg/runtime"
)
// NewApplyCmd returns apply subcmd
@ -40,9 +41,13 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.StringVar(&applyOptions.Output, "output", "", "output format for diff plugin")
f.BoolVar(&applyOptions.DetailedExitcode, "detailed-exitcode", false, "return a non-zero exit code 2 instead of 0 when there were changes detected AND the changes are synced successfully")
f.BoolVar(&applyOptions.StripTrailingCR, "strip-trailing-cr", false, "strip trailing carriage return on input")
f.StringVar(&applyOptions.DiffArgs, "diff-args", "", `pass args to helm helm-diff`)
f.StringVar(&applyOptions.SyncArgs, "sync-args", "", `pass args to helm upgrade`)
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec")
if !runtime.V1Mode {
// TODO: Remove this function once Helmfile v0.x
f.BoolVar(&applyOptions.RetainValuesFiles, "retain-values-files", false, "DEPRECATED: Use skip-cleanup instead")
_ = f.MarkDeprecated("retain-values-files", "Use skip-cleanup instead")
}
f.BoolVar(&applyOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security")
f.BoolVar(&applyOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present")
f.BoolVar(&applyOptions.SkipNeeds, "skip-needs", true, `do not automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided. Defaults to true when --include-needs or --include-transitive-needs is not provided`)
@ -54,19 +59,13 @@ func NewApplyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&applyOptions.SuppressSecrets, "suppress-secrets", false, "suppress secrets in the diff output. highly recommended to specify on CI/CD use-cases")
f.BoolVar(&applyOptions.ShowSecrets, "show-secrets", false, "do not redact secret values in the diff output. should be used for debug purpose only")
f.BoolVar(&applyOptions.NoHooks, "no-hooks", false, "do not diff changes made by hooks.")
f.BoolVar(&applyOptions.HideNotes, "hide-notes", false, "add --hide-notes flag to helm")
f.BoolVar(&applyOptions.TakeOwnership, "take-ownership", false, "add --take-ownership flag to helm")
f.BoolVar(&applyOptions.SyncReleaseLabels, "sync-release-labels", false, "sync release labels to the target release")
f.BoolVar(&applyOptions.SuppressDiff, "suppress-diff", false, "suppress diff in the output. Usable in new installs")
f.BoolVar(&applyOptions.Wait, "wait", false, `Override helmDefaults.wait setting "helm upgrade --install --wait"`)
f.BoolVar(&applyOptions.WaitForJobs, "wait-for-jobs", false, `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`)
f.BoolVar(&applyOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reuse-values"`)
f.BoolVar(&applyOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`)
f.StringVar(&applyOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringArrayVar(&applyOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`)
f.BoolVar(&applyOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass --skip-schema-validation to "helm template" or "helm upgrade --install"`)
f.StringVar(&applyOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
f.StringArrayVar(&applyOptions.SuppressOutputLineRegex, "suppress-output-line-regex", nil, "a list of regex patterns to suppress output lines from the diff output")
return cmd
}

41
cmd/charts.go Normal file
View File

@ -0,0 +1,41 @@
// TODO: Remove this function once Helmfile v0.x
package cmd
import (
"github.com/spf13/cobra"
"github.com/helmfile/helmfile/pkg/app"
"github.com/helmfile/helmfile/pkg/config"
)
// NewChartsCmd returns charts subcmd
func NewChartsCmd(globalCfg *config.GlobalImpl) *cobra.Command {
chartsOptions := config.NewChartsOptions()
cmd := &cobra.Command{
Use: "charts",
Short: "DEPRECATED: sync releases from state file (helm upgrade --install)",
RunE: func(cmd *cobra.Command, args []string) error {
chartsImpl := config.NewChartsImpl(globalCfg, chartsOptions)
err := config.NewCLIConfigImpl(chartsImpl.GlobalImpl)
if err != nil {
return err
}
if err := chartsImpl.ValidateConfig(); err != nil {
return err
}
a := app.New(chartsImpl)
return toCLIError(chartsImpl.GlobalImpl, a.DeprecatedSyncCharts(chartsImpl))
},
}
f := cmd.Flags()
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec")
f.StringArrayVar(&chartsOptions.Set, "set", nil, "additional values to be merged into the helm command --set flag")
f.StringArrayVar(&chartsOptions.Values, "values", nil, "additional value files to be merged into the helm command --values flag")
f.IntVar(&chartsOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
return cmd
}

42
cmd/delete.go Normal file
View File

@ -0,0 +1,42 @@
// TODO: Remove this function once Helmfile v0.x
package cmd
import (
"github.com/spf13/cobra"
"github.com/helmfile/helmfile/pkg/app"
"github.com/helmfile/helmfile/pkg/config"
)
// NewDeleteCmd returns delete subcmd
func NewDeleteCmd(globalCfg *config.GlobalImpl) *cobra.Command {
deleteOptions := config.NewDeleteOptions()
cmd := &cobra.Command{
Use: "delete",
Short: "DEPRECATED: delete releases from state file (helm delete)",
RunE: func(cmd *cobra.Command, args []string) error {
deleteImpl := config.NewDeleteImpl(globalCfg, deleteOptions)
err := config.NewCLIConfigImpl(deleteImpl.GlobalImpl)
if err != nil {
return err
}
if err := deleteImpl.ValidateConfig(); err != nil {
return err
}
a := app.New(deleteImpl)
return toCLIError(deleteImpl.GlobalImpl, a.Delete(deleteImpl))
},
}
f := cmd.Flags()
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm exec")
f.StringVar(&deleteOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
f.IntVar(&deleteOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
f.BoolVar(&deleteOptions.Purge, "purge", false, "purge releases i.e. free release names and histories")
f.BoolVar(&deleteOptions.SkipCharts, "skip-charts", false, "don't prepare charts when deleting releases")
return cmd
}

View File

@ -35,8 +35,6 @@ func NewDestroyCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.StringVar(&destroyOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
f.IntVar(&destroyOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
f.BoolVar(&destroyOptions.SkipCharts, "skip-charts", false, "don't prepare charts when destroying releases")
f.BoolVar(&destroyOptions.DeleteWait, "deleteWait", false, `override helmDefaults.wait setting "helm uninstall --wait"`)
f.IntVar(&destroyOptions.DeleteTimeout, "deleteTimeout", 300, `time in seconds to wait for helm uninstall, default: 300`)
return cmd
}

View File

@ -31,7 +31,6 @@ func NewDiffCmd(globalCfg *config.GlobalImpl) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&diffOptions.DiffArgs, "diff-args", "", `pass args to helm helm-diff`)
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm diff")
f.StringArrayVar(&diffOptions.Set, "set", nil, "additional values to be merged into the helm command --set flag")
f.StringArrayVar(&diffOptions.Values, "values", nil, "additional value files to be merged into the helm command --values flag")
@ -52,10 +51,7 @@ func NewDiffCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.StringArrayVar(&diffOptions.Suppress, "suppress", nil, "suppress specified Kubernetes objects in the output. Can be provided multiple times. For example: --suppress KeycloakClient --suppress VaultSecret")
f.BoolVar(&diffOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm diff upgrade --install --reuse-values"`)
f.BoolVar(&diffOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm diff upgrade --install --reset-values"`)
f.BoolVar(&diffOptions.TakeOwnership, "take-ownership", false, "add --take-ownership flag to helm")
f.StringVar(&diffOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringArrayVar(&diffOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`)
f.StringArrayVar(&diffOptions.SuppressOutputLineRegex, "suppress-output-line-regex", nil, "a list of regex patterns to suppress output lines from the diff output")
return cmd
}

View File

@ -60,13 +60,7 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
case globalConfig.Quiet:
logLevel = "warn"
}
// If the log output is not set, default to stderr.
logOut := globalConfig.LogOutput
if logOut == nil {
logOut = os.Stderr
}
logger = helmexec.NewLogger(logOut, logLevel)
logger = helmexec.NewLogger(os.Stderr, logLevel)
globalConfig.SetLogger(logger)
return nil
},
@ -76,7 +70,7 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
// Set the global options for the root command.
setGlobalOptionsForRootCmd(flags, globalConfig)
flags.ParseErrorsAllowlist.UnknownFlags = true
flags.ParseErrorsWhitelist.UnknownFlags = true
globalImpl := config.NewGlobalImpl(globalConfig)
@ -103,29 +97,32 @@ func NewRootCmd(globalConfig *config.GlobalOptions) (*cobra.Command, error) {
NewSyncCmd(globalImpl),
NewDiffCmd(globalImpl),
NewStatusCmd(globalImpl),
NewShowDAGCmd(globalImpl),
extension.NewVersionCobraCmd(
versionOpts...,
),
)
// TODO: Remove this function once Helmfile v0.x
if !runtime.V1Mode {
cmd.AddCommand(
NewChartsCmd(globalImpl),
NewDeleteCmd(globalImpl),
)
}
return cmd, nil
}
func setGlobalOptionsForRootCmd(fs *pflag.FlagSet, globalOptions *config.GlobalOptions) {
fs.StringVarP(&globalOptions.HelmBinary, "helm-binary", "b", app.DefaultHelmBinary, "Path to the helm binary")
fs.StringVarP(&globalOptions.KustomizeBinary, "kustomize-binary", "k", app.DefaultKustomizeBinary, "Path to the kustomize binary")
fs.StringVarP(&globalOptions.File, "file", "f", "", "load config from file or directory. defaults to \"`helmfile.yaml`\" or \"helmfile.yaml.gotmpl\" or \"helmfile.d\" (means \"helmfile.d/*.yaml\" or \"helmfile.d/*.yaml.gotmpl\") in this preference. Specify - to load the config from the standard input.")
fs.StringVarP(&globalOptions.Environment, "environment", "e", "", `specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"`)
fs.StringArrayVar(&globalOptions.StateValuesSet, "state-values-set", nil, "set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).")
fs.StringArrayVar(&globalOptions.StateValuesSetString, "state-values-set-string", nil, "set state STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).")
fs.StringArrayVar(&globalOptions.StateValuesFile, "state-values-file", nil, "specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).")
fs.BoolVar(&globalOptions.SkipDeps, "skip-deps", false, `skip running "helm repo update" and "helm dependency build"`)
fs.BoolVar(&globalOptions.SkipRefresh, "skip-refresh", false, `skip running "helm repo update"`)
fs.BoolVar(&globalOptions.StripArgsValuesOnExitError, "strip-args-values-on-exit-error", true, `Strip the potential secret values of the helm command args contained in a helmfile error message`)
fs.BoolVar(&globalOptions.DisableForceUpdate, "disable-force-update", false, `do not force helm repos to update when executing "helm repo add"`)
fs.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "Silence output. Equivalent to log-level warn")
fs.StringVar(&globalOptions.Kubeconfig, "kubeconfig", "", "Use a particular kubeconfig file")
fs.StringVar(&globalOptions.KubeContext, "kube-context", "", "Set kubectl context. Uses current context by default")
fs.BoolVar(&globalOptions.Debug, "debug", false, "Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect")
fs.BoolVar(&globalOptions.Color, "color", false, "Output with color")

View File

@ -1,32 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
"github.com/helmfile/helmfile/pkg/app"
"github.com/helmfile/helmfile/pkg/config"
)
func NewShowDAGCmd(globalCfg *config.GlobalImpl) *cobra.Command {
showDAGOptions := config.NewShowDAGOptions()
cmd := &cobra.Command{
Use: "show-dag",
Short: "It prints a table with 3 columns, GROUP, RELEASE, and DEPENDENCIES. GROUP is the unsigned, monotonically increasing integer starting from 1. All the releases with the same GROUP are deployed concurrently. Everything in GROUP 2 starts being deployed only after everything in GROUP 1 got successfully deployed. RELEASE is the release that belongs to the GROUP. DEPENDENCIES is the list of releases that the RELEASE depends on. It should always be empty for releases in GROUP 1. DEPENDENCIES for a release in GROUP 2 should have some or all dependencies appeared in GROUP 1. It can be \"some\" because Helmfile simplifies the DAGs of releases into a DAG of groups, so that Helmfile always produce a single DAG for everything written in helmfile.yaml, even when there are technically two or more independent DAGs of releases in it.",
RunE: func(cmd *cobra.Command, args []string) error {
showDAGImpl := config.NewShowDAGImpl(globalCfg, showDAGOptions)
err := config.NewCLIConfigImpl(showDAGImpl.GlobalImpl)
if err != nil {
return err
}
if err := showDAGImpl.ValidateConfig(); err != nil {
return err
}
a := app.New(showDAGImpl)
return toCLIError(showDAGImpl.GlobalImpl, a.PrintDAGState(showDAGImpl))
},
}
return cmd
}

View File

@ -32,7 +32,6 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f := cmd.Flags()
f.StringVar(&globalCfg.GlobalOptions.Args, "args", "", "pass args to helm sync")
f.StringVar(&syncOptions.SyncArgs, "sync-args", "", "pass args to helm upgrade")
f.StringArrayVar(&syncOptions.Set, "set", nil, "additional values to be merged into the helm command --set flag")
f.StringArrayVar(&syncOptions.Values, "values", nil, "additional value files to be merged into the helm command --values flag")
f.IntVar(&syncOptions.Concurrency, "concurrency", 0, "maximum number of concurrent helm processes to run, 0 is unlimited")
@ -41,17 +40,11 @@ func NewSyncCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&syncOptions.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed on sync. By default, CRDs are installed if not already present")
f.BoolVar(&syncOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&syncOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&syncOptions.HideNotes, "hide-notes", false, "add --hide-notes flag to helm")
f.BoolVar(&syncOptions.TakeOwnership, "take-ownership", false, `add --take-ownership flag to helm`)
f.BoolVar(&syncOptions.SyncReleaseLabels, "sync-release-labels", false, "sync release labels to the target release")
f.BoolVar(&syncOptions.Wait, "wait", false, `Override helmDefaults.wait setting "helm upgrade --install --wait"`)
f.BoolVar(&syncOptions.WaitForJobs, "wait-for-jobs", false, `Override helmDefaults.waitForJobs setting "helm upgrade --install --wait-for-jobs"`)
f.IntVar(&syncOptions.Timeout, "timeout", 0, `Override helmDefaults.timeout setting "helm upgrade --install --timeout" (default 0, which means no timeout)`)
f.BoolVar(&syncOptions.ReuseValues, "reuse-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reuse-values"`)
f.BoolVar(&syncOptions.ResetValues, "reset-values", false, `Override helmDefaults.reuseValues "helm upgrade --install --reset-values"`)
f.StringVar(&syncOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringArrayVar(&syncOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`)
f.BoolVar(&syncOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass --skip-schema-validation to "helm template" or "helm upgrade --install"`)
f.StringVar(&syncOptions.Cascade, "cascade", "", "pass cascade to helm exec, default: background")
return cmd

View File

@ -44,12 +44,8 @@ func NewTemplateCmd(globalCfg *config.GlobalImpl) *cobra.Command {
f.BoolVar(&templateOptions.IncludeNeeds, "include-needs", false, `automatically include releases from the target release's "needs" when --selector/-l flag is provided. Does nothing when --selector/-l flag is not provided`)
f.BoolVar(&templateOptions.IncludeTransitiveNeeds, "include-transitive-needs", false, `like --include-needs, but also includes transitive needs (needs of needs). Does nothing when --selector/-l flag is not provided. Overrides exclusions of other selectors and conditions.`)
f.BoolVar(&templateOptions.SkipCleanup, "skip-cleanup", false, "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security")
f.BoolVar(&templateOptions.NoHooks, "no-hooks", false, "do not template files made by hooks.")
f.StringVar(&templateOptions.PostRenderer, "post-renderer", "", `pass --post-renderer to "helm template" or "helm upgrade --install"`)
f.StringArrayVar(&templateOptions.PostRendererArgs, "post-renderer-args", nil, `pass --post-renderer-args to "helm template" or "helm upgrade --install"`)
f.BoolVar(&templateOptions.SkipSchemaValidation, "skip-schema-validation", false, `pass skip-schema-validation to "helm template" or "helm upgrade --install"`)
f.StringVar(&templateOptions.KubeVersion, "kube-version", "", `pass --kube-version to "helm template". Overrides kubeVersion in helmfile.yaml`)
f.StringArrayVar(&templateOptions.ShowOnly, "show-only", nil, `pass --show-only to "helm template"`)
return cmd
}

View File

@ -14,16 +14,16 @@ Helmfile integrates [vals]() to import configuration parameters from following b
- Vault
- SOPS
See [Vals "Supported Backends"](https://github.com/helmfile/vals#supported-backends) for the full list of available backends.
See [Vals "Suported Backends"](https://github.com/helmfile/vals#suported-backends) for the full list of available backends.
This feature was implemented in https://github.com/roboll/helmfile/pull/906.
If you're curious about how it's designed and how it works, please review the pull request.
If you're curious how it's designed and how it works, please consult the pull request.
### Deploy Kustomizations with Helmfile
You can deploy [kustomize](https://github.com/kubernetes-sigs/kustomize) "kustomization"s with Helmfile.
Most Kustomize operations are usually done with `kustomize edit` and can be done declaratively via Helm `values.yaml` files.
Most of Kustomize operations that is usually done with `kustomize edit` can be done declaratively via Helm values.yaml files.
Under the hood, Helmfile transforms the kustomization into a local chart in a temporary directory so that it can be `helm upgrade --install`ed.
@ -99,9 +99,6 @@ Please also see [test/advanced/helmfile.yaml](https://github.com/helmfile/helmfi
With Helmfile's integration with Kustomize, not only deploying Kustomization as a Helm chart, you can kustomize charts before installation.
.. Hint:: The following fields can also specify files, in the same manner as
the `values` field.
Currently, Helmfile allows you to set the following fields for kustomizing the chart:
- [`releases[].strategicMergePatches`](#strategicmergepatches)

View File

@ -52,7 +52,6 @@ releases:
- `Release.Labels`: The labels to be applied to the release
- `Release.Chart`: The chart name of the release
- `Release.KubeContext`: The kube context to be used for the release
- `Release.ChartVersion`: The version of the current chart
- `Values`: Values passed into the environment.
- `StateValues`: alias for `Values`.
- `Environment`: The information about the environment. This is set by the

View File

@ -1,7 +0,0 @@
# Experimental Features
This document describes the experimental features that are available in Helmfile v1.
Any experimental feature may be removed or changed in a future release without notice.
- HCL helmfile-values-file support (PR #1423)

View File

@ -1,667 +0,0 @@
# HCL Functions
## Standard Library
The following functions are all from the [go-cty](https://pkg.go.dev/github.com/zclconf/go-cty/cty/function/stdlib#pkg-functions), [go-cty-yaml](https://pkg.go.dev/github.com/zclconf/go-cty-yaml) and [hcl](https://pkg.go.dev/github.com/hashicorp/hcl/v2@v2.20.1/ext/tryfunc#section-readme) libraries
#### abs
`abs` returns the absolute value
```
abs(number)
```
```
abs(-1)
# 1
abs(2)
# 2
```
#### can
`can` evaluates an expression and returns a boolean if a result can be produced without any error
```
can(expr)
```
```
map = {
myvar = "myvar"
}
can1 = can(hv.map.myVar)
# true
can2 = can(hv.map.notMyVar)
# false
```
#### ceil
`ceil` returns the ceiling value of a given number
```
ceil(number)
```
```
ceil(1)
# 1
ceil(1.1)
# 2
```
#### chomp
`chomp` removes newline characters at the end of a string.
```
chomp(string)
```
```
chomp("myVar\n")
# myVar
```
#### coalesce
`coalesce` returns the first of the given arguments that is not null. If all arguments are null, an error is produced.
All arguments must be of the same type apart from some cases
```
coalesce(any...)
```
```
coalesce(null, 2)
# 2
coalesce(null, "value")
# value
```
Use the three dots notation `...` to expand a list
```
coalesce([null, "value"]...)
# value
```
#### coalescelist
`coalescelist` takes any number of list arguments and returns the first one that isn't empty.
```
coalescelist(list)
```
```
coalescelist([], ["value"])
# ["value"]
```
Use the three dots notation `...` when using list of lists
```
coalescelist([[], ["val1", "val2"]]...)
# ["val1", "val2"]
```
#### compact
`compact` returns a new list with any empty string elements removed.
```
compact(list)
```
```
compact(["", "val1", "val2"])
# ["val1", "val2"]
```
#### concat
`concat` takes one or more sequences (lists or tuples) and returns the single sequence that results from concatenating them together in order.
```
concat(list, list...)
```
```
concat(["val1"], ["val2", "val3"])
# ["val1", "val2", "val3"]
```
#### contains
`contains` returns a boolean if a list contains a given value
```
contains(list, value)
```
```
contains(["val1", "val2"], "val2")
# true
```
#### csvdecode
`csvdecode` decodes a CSV-formatted string into a list of maps
```
csvdecode(string)
```
```
csvdecode("col1,col2\nv1,v2\nv3,v4")
###
[
{
"col1" = "v1"
"col2" = "v2"
},
{
"col1" = "v3"
"col2" = "v4"
}
]
```
#### distinct
`distinct` returns a new list from another by removing all duplicates
```
distinct(list)
```
```
distinct(["v1","v1","v2"])
["v1", "v2"]
```
#### element
`element` returns a single element from a given list at the given index. If index is greater than the length of the list then it is wrapped modulo the list length
```
element(list, index)
```
```
element(["val1","val2"], 1)
# val2
```
#### chunklist
`chunklist` splits a single list into fixed-size chunks, returning a list of lists.
```
chunklist(list, size)
```
```
chunklist(["a","b"], 1)
# [["a"], ["b"]]
```
#### flatten
`flatten` takes a list and replaces any elements that are lists with a flattened sequence of the list contents.
```
flatten(list)
```
```
flatten([["a"], ["a","b"], ["c"]])
# ["a","a","b","c"]
```
#### floor
`floor` returns the closest whole number lesser than or equal to the given value.
```
floor(number)
```
```
floor(1)
# 1
floor(0.7)
# 0
```
#### format
`format` produces a string representation of zero or more values using a format string similar to the "printf" function in C.
[Verbs details](https://pkg.go.dev/github.com/zclconf/go-cty/cty/function/stdlib#Format)
```
format(format, values)
```
```
format("Hello %s", "world")
# Hello world
```
#### formatdate
`formatdate` reformats a timestamp given in RFC3339 syntax into another time syntax defined by a given format string.
[Syntax details](https://pkg.go.dev/github.com/zclconf/go-cty/cty/function/stdlib#FormatDate)
```
formatdate(string, timestampString)
```
```
formatdate("MMM DD YYYY", "2024-01-01T00:12:00Z")
# Jan 01 2024
```
#### formatlist
`formatlist` does the same as `format` but for a list of strings
```
formatlist(formatString, values...)
```
```
formatlist("%s", ["Hello", "World"])
###
[
"Hello",
"World"
]
formatlist("%s %s", "hello", ["World", "You"])
###
[
"hello World",
"hello You",
]
```
#### indent
`indent` adds a given number of spaces to the beginnings of all but the first line in a given multi-line string.
```
indent(number, string)
```
```
indent(4, "hello,\nWorld\n!")
###
hello
World
!
```
#### int
`int` removes the fractional component of the given number returning an integer representing the whole number component, rounding towards zero.
```
int(number)
```
```
int(6.2)
# 6
```
#### join
`join` concatenates together the string elements of one or more lists with a given separator.
```
join(listOfStrings, separator)
```
```
join(" ", ["hello", "world"])
# hello world
```
#### jsondecode
`jsondecode` parses the given JSON string and, if it is valid, returns the value it represents.
```
jsonencode(string)
```
Example :
```
jsonencode({"hello"="world"})
# {"hello": "world"}
```
#### jsonencode
`jsonencode` returns a JSON serialization of the given value.
```
jsondecode(string)
```
Example :
```
jsondecode("{\"hello\": \"world\"}")
# { hello = "world" }
```
#### keys
`keys` takes a map and returns a sorted list of the map keys.
```
keys(map)
```
```
keys({val1=1, val2=2, val3=3})
# ["val1","val2","val3"]
```
#### length
`length` returns the number of elements in the given __collection__.
See `strlen` for strings
```
length(list)
```
```
length([1,2,3])
# 3
```
#### log
`log` returns returns the logarithm of a given number in a given base.
```
log(number, base)
```
```
log(1, 10)
# 0
```
#### lookup
`lookup` performs a dynamic lookup into a map. There are three required arguments, inputMap and key, plus a defaultValue, which is a value to return if the given key is not found in the inputMap.
```
lookup(inputMap, key, defaultValue)
```
```
map = { "luke" = "skywalker"}
lookup(hv.maptest, "luke", "none")
# skywalker
lookup(hv.maptest, "leia", "none")
# none
```
#### lower
`lower` is a Function that converts a given string to lowercase.
```
lower(string)
```
```
lower("HELLO world")
# hello world
```
#### max
`max` returns the maximum number from the given numbers.
```
max(numbers)
```
```
max(1,128,70)
# 128
```
#### merge
`merge` takes an arbitrary number of maps and returns a single map that contains a merged set of elements from all of the maps.
```
merge(maps)
```
```
merge({a="1"}, {a=[1,2], c="world"}, {d=40})
# { a = [1,2], c = "world", d = 40}
```
#### min
`min` returns the minimum number from the given numbers.
```
min(numbers)
```
```
min(1,128,70)
# 1
```
#### parseint
`parseint` parses a string argument and returns an integer of the specified base.
```
parseint(string, base)
```
```
parseint("190", 10)
# 190
parseint("11001", 2)
# 25
```
#### pow
`pow` returns the logarithm of a given number in a given base.
```
pow(number, power)
```
```
pow(1, 10)
# 1
pow(3, 12)
# 531441
```
#### range
`range` creates a list of numbers by starting from the given starting value, then adding the given step value until the result is greater than or equal to the given stopping value. Each intermediate result becomes an element in the resulting list.
```
range(startingNumber, stoppingNumber, stepNumber)
```
```
range(1, 10, 3)
# [1, 4, 7]
```
#### regex
`regex` is a function that extracts one or more substrings from a given string by applying a regular expression pattern, describing the first match.
The return type depends on the composition of the capture groups (if any) in the pattern:
If there are no capture groups at all, the result is a single string representing the entire matched pattern.
If all of the capture groups are named, the result is an object whose keys are the named groups and whose values are their sub-matches, or null if a particular sub-group was inside another group that didn't match.
If none of the capture groups are named, the result is a tuple whose elements are the sub-groups in order and whose values are their sub-matches, or null if a particular sub-group was inside another group that didn't match.
It is invalid to use both named and un-named capture groups together in the same pattern.
If the pattern doesn't match, this function returns an error. To test for a match, call `regexall` and check if the length of the result is greater than zero.
```
regex(pattern, string)
```
```
regex("[0-9]+", "v1.2.3")
# 1
```
#### regexall
`regexall` is similar to Regex but it finds all of the non-overlapping matches in the given string and returns a list of them.
The result type is always a list, whose element type is deduced from the pattern in the same way as the return type for Regex is decided.
If the pattern doesn't match at all, this function returns an empty list.
```
regexall(pattern, string)
```
```
regexall("[0-9]+", "v1.2.3")
# [1 2 3]
```
#### setintersection
`setintersection` returns a new set containing the elements that exist in all of the given sets, which must have element types that can all be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.
```
setintersection(sets...)
```
```
setintersection(["val1", "val2"], ["val1", "val3"], ["val1", "val2"])
# ["val1"]
```
#### setproduct
`setproduct` computes the Cartesian product of sets or sequences.
```
setproduct(sets...)
```
```
setproduct(["host1", "host2"], ["stg.domain", "prod.domain"])
###
[
[
"host1",
"stg.domain"
],
[
"host2",
"stg.domain"
],
[
"host1",
"prod.domain"
],
[
"host2",
"prod.domain"
],
]
```
#### setsubtract
`setsubtract` returns a new set containing the elements from the first set that are not present in the second set. The sets must have element types that can both be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.
```
setsubtract(sets...)
```
```
setsubtract(["a", "b", "c"], ["a", "b"])
###
["c"]
```
#### setunion
`setunion` returns a new set containing all of the elements from the given sets, which must have element types that can all be converted to some common type using the standard type unification rules. If conversion is not possible, an error is returned.
```
setunion(sets...)
```
```
setunion(["a", "b"], ["b", "c"], ["a", "d"])
###
["a", "b", "c", "d"]
```
#### signum
`signum` determines the sign of a number, returning a number between -1 and 1 to represent the sign.
```
signum(number)
```
```
signum(-182)
# -1
```
#### slice
`slice` extracts some consecutive elements from within a list.
startIndex is inclusive, endIndex is exclusive
```
slice(list, startIndex, endIndex)
```
```
slice([{"a" = "b"}, {"c" = "d"}, , {"e" = "f"}], 1, 1)
# []
slice([{"a" = "b"}, {"c" = "d"}, {"e" = "f"}], 1, 2)
# [{"c" = "d"}]
```
#### sort
`sort` re-orders the elements of a given list of strings so that they are in ascending lexicographical order.
```
sort(list)
```
```
sort(["1", "h", "r", "p", "word"])
# ["1", "h", "p", "r", "word"]
```
#### split
`split` divides a given string by a given separator, returning a list of strings containing the characters between the separator sequences.
```
split(separatorString, string)
```
```
split(".", "host.domain")
# ["host", "domain"]
```
#### strlen
`strlen` is a Function that returns the length of the given string in characters.
```
strlen(string)
```
```
strlen("yes")
# 3
```
#### strrev
`strrev` is a Function that reverses the order of the characters in the given string.
```
strrev(string)
```
```
strrev("yes")
# "sey"
```
#### substr
`substr` is a Function that extracts a sequence of characters from another string and creates a new string.
```
substr(string, offsetNumber, length)
```
```
substr("host.domain", 0, 4)
# "host"
```
#### timeadd
`timeadd` adds a duration to a timestamp, returning a new timestamp.
Only units "inferior" or equal to `h` are supported.
The duration can be negative.
```
substr(timestamp, duration)
```
```
timeadd("2024-01-01T00:00:00Z", "-2600h10m")
# 2023-09-14T15:50:00Z
```
#### trim
`trim` removes the specified characters from the start and end of the given string.
```
trim(string, string)
```
```
trim("Can you do that ? Yes ?", "?")
# "Can you do that ? Yes"
```
#### trimprefix
`trimprefix` removes the specified prefix from the start of the given string.
```
trimprefix(stringToTrim, trimmingString)
```
```
trimprefix("please, do it", "please, ")
# "do it"
```
#### trimspace
`trimspace` removes any space characters from the start and end of the given string.
```
trimspace(string)
```
```
trimspace(" Hello World ")
# "Hello World"
```
#### trimsuffix
`trimsuffix` removes the specified suffix from the end of the given string.
```
trimsuffix(stringToTrim, trimmingString)
```
```
trimsuffix("Hello World", " World")
# "Hello"
```
#### try
`try` is a variadic function that tries to evaluate all of is arguments in sequence until one succeeds, in which case it returns that result, or returns an error if none of them succeed.
```
try(expressions...)
```
```
values {
map = {
hello = "you"
world = "us"
}
try(hv.map.do_not_exist, hv.map.world)
}
# "us"
```
#### upper
`upper` is a Function that converts a given string to uppercase.
```
upper(string)
```
```
upper("up")
# "UP"
```
#### values
`values` returns a list of the map values, in the order of the sorted keys. This function only works on flat maps.
```
values(map)
```
```
values({"a" = 1,"b" = 2})
# [1, 2]
```
#### yamldecode
`yamldecode` parses the given JSON string and, if it is valid, returns the value it represents.
```
yamldecode(string)
```
```
yamldecode("hello: world\narray: [1, 2, 3]")
###
{
array = [1, 2, 3]
hello = "world"
}
```
#### yamlencode
`yamlencode` returns a JSON serialization of the given value.
```
yamlencode({array = [1, 2, 3], hello = "world"})
```
```
yamlencode({array = [1, 2, 3], hello = "world"})
###
"array":
- 1
- 2
- 3
"hello": "world"
```
#### zipmap
`zipmap` constructs a map from a list of keys and a corresponding list of values.
The lenght of each list must be equal
```
zipmap(keysList, valuesList)
```
```
zipmap(["key1", "key2"], ["val1", "val2"])
###
{
"key1" = "val1"
"key2" = "val2"
}
```

View File

@ -12,7 +12,7 @@
# Helmfile
[![Tests](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/helmfile/helmfile/actions/workflows/ci.yaml?query=branch%3Amain)
[![Container Image Repository on GHCR](https://ghcr-badge.egpl.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Container Image Repository on GHCR](https://ghcr-badge.deta.dev/helmfile/helmfile/latest_tag?trim=major&label=latest "Docker Repository on ghcr")](https://github.com/helmfile/helmfile/pkgs/container/helmfile)
[![Go Report Card](https://goreportcard.com/badge/github.com/helmfile/helmfile)](https://goreportcard.com/report/github.com/helmfile/helmfile)
[![Slack Community #helmfile](https://slack.sweetops.com/badge.svg)](https://slack.sweetops.com)
[![Documentation](https://readthedocs.org/projects/helmfile/badge/?version=latest&style=flat)](https://helmfile.readthedocs.io/en/latest/)
@ -24,10 +24,13 @@ Deploy Kubernetes Helm Charts
## Status
May 2025 Update
March 2022 Update - The helmfile project has been moved to [helmfile/helmfile](https://github.com/helmfile/helmfile) from the former home `roboll/helmfile`. Please see [roboll/helmfile#1824](https://github.com/roboll/helmfile/issues/1824) for more information.
* Helmfile v1.0 and v1.1 has been released. We recommend upgrading directly to v1.1 if you are still using v0.x.
* If you haven't already upgraded, please go over this v1 proposal [here](https://github.com/helmfile/helmfile/blob/main/docs/proposals/towards-1.0.md) to see a small list of breaking changes.
Even though Helmfile is used in production environments [across multiple organizations](users.md), it is still in its early stage of development, hence versioned 0.x.
Helmfile complies to Semantic Versioning 2.0.0 in which v0.x means that there could be backward-incompatible changes for every release.
Note that we will try our best to document any backward incompatibility. And in reality, helmfile had no breaking change for a year or so.
## About
@ -60,16 +63,27 @@ To avoid upgrades for each iteration of `helm`, the `helmfile` executable delega
### Running as a container
The [Helmfile Docker images are available in GHCR](https://github.com/helmfile/helmfile/pkgs/container/helmfile). There is no `latest` tag, since the `0.x` versions can contain breaking changes, so make sure you pick the right tag. Example using `helmfile 0.156.0`:
The [Helmfile Docker images are available in GHCR](https://github.com/helmfile/helmfile/pkgs/container/helmfile). There is no `latest` tag, since the `0.x` versions can contain breaking changes, so make sure you pick the right tag. Example using `helmfile 0.145.2`:
```sh-session
$ docker run --rm --net=host -v "${HOME}/.kube:/helm/.kube" -v "${HOME}/.config/helm:/helm/.config/helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v0.156.0 helmfile sync
# helm 2
$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.helm:/root/.helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v0.145.2 helmfile sync
# helm 3
$ docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.config/helm:/root/.config/helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:helm3-v0.145.2 helmfile sync
```
You can also use a shim to make calling the binary easier:
You can also use shims to make calling the binaries easier:
```sh-session
$ printf '%s\n' '#!/bin/sh' 'docker run --rm --net=host -v "${HOME}/.kube:/helm/.kube" -v "${HOME}/.config/helm:/helm/.config/helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v0.156.0 helmfile "$@"' |
# helm 2
$ printf '%s\n' '#!/bin/sh' 'docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.helm:/root/.helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:v0.145.2 helmfile "$@"' |
tee helmfile
$ chmod +x helmfile
$ ./helmfile sync
# helm 3
$ printf '%s\n' '#!/bin/sh' 'docker run --rm --net=host -v "${HOME}/.kube:/root/.kube" -v "${HOME}/.config/helm:/root/.config/helm" -v "${PWD}:/wd" --workdir /wd ghcr.io/helmfile/helmfile:helm3-v0.145.2 helmfile "$@"' |
tee helmfile
$ chmod +x helmfile
$ ./helmfile sync
@ -95,12 +109,6 @@ releases:
value: false
```
Install required dependencies using [init](https://helmfile.readthedocs.io/en/latest/#init):
```console
helmfile init
```
Sync your Kubernetes cluster state to the desired one by running:
```console
@ -113,7 +121,7 @@ Iterate on the `helmfile.yaml` by referencing:
* [Configuration](#configuration)
* [CLI reference](#cli-reference).
* [Helmfile Best Practices Guide](writing-helmfile.md)
* [Helmfile Best Practices Guide](https://github.com/roboll/helmfile/blob/master/docs/writing-helmfile.md)
## Configuration
@ -141,35 +149,27 @@ repositories:
url: roboll.io/charts
certFile: optional_client_cert
keyFile: optional_client_key
# username is retrieved from the environment with the format <registryNameUpperCase>_USERNAME for CI usage, here ROBOLL_USERNAME
# username is retrieve from the environment with the format <registryNameUpperCase>_USERNAME for CI usage, here ROBOLL_USERNAME
username: optional_username
# password is retrieved from the environment with the format <registryNameUpperCase>_PASSWORD for CI usage, here ROBOLL_PASSWORD
# username is retrieve from the environment with the format <registryNameUpperCase>_PASSWORD for CI usage, here ROBOLL_PASSWORD
password: optional_password
oci: true
passCredentials: true
verify: true
keyring: path/to/keyring.gpg
# Advanced configuration: You can use a ca bundle to use an https repo
# with a self-signed certificate
- name: insecure
url: https://charts.my-insecure-domain.com
caFile: optional_ca_crt
url: https://charts.my-insecure-domain.com
caFile: optional_ca_crt
# Advanced configuration: You can skip the verification of TLS for an https repo
- name: skipTLS
url: https://ss.my-insecure-domain.com
skipTLSVerify: true
# Advanced configuration: Connect to a repo served over plain http
- name: plainHTTP
url: http://just.http.domain.com
plainHttp: true
# context: kube-context # this directive is deprecated, please consider using helmDefaults.kubeContext
# Path to alternative helm binary (--helm-binary)
helmBinary: path/to/helm3
# Path to alternative kustomize binary (--kustomize-binary)
kustomizeBinary: path/to/kustomize
# Path to alternative lock file. The default is <state file name>.lock, i.e for helmfile.yaml it's helmfile.lock.
lockFilePath: path/to/lock.file
@ -183,20 +183,10 @@ helmDefaults:
# additional and global args passed to helm (default "")
args:
- "--set k=v"
diffArgs:
- "--suppress-secrets"
syncArgs:
- "--labels=app.kubernetes.io/managed-by=helmfile"
# verify the chart before upgrading (only works with packaged charts not directories) (default false)
verify: true
keyring: path/to/keyring.gpg
# --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint', starts with helm 3.16.0 (default false)
skipSchemaValidation: false
# wait for k8s resources via --wait. (default false)
wait: true
# 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)
@ -219,28 +209,10 @@ helmDefaults:
reuseValues: false
# propagate `--post-renderer` to helmv3 template and helm install
postRenderer: "path/to/postRenderer"
# propagate `--post-renderer-args` to helmv3 template and helm install. This allows using Powershell
# scripts on Windows as a post renderer
postRendererArgs:
- PowerShell
- "-Command"
- "theScript.ps1"
# cascade `--cascade` to helmv3 delete, available values: background, foreground, or orphan, default: background
cascade: "background"
# insecureSkipTLSVerify is true if the TLS verification should be skipped when fetching remote chart
insecureSkipTLSVerify: false
# plainHttp is true if fetching the remote chart should be done using HTTP
plainHttp: false
# --wait flag for destroy/delete, if set to true, will wait until all resources are deleted before mark delete command as successful
deleteWait: false
# Timeout is the time in seconds to wait for helmfile destroy/delete (default 300)
deleteTimeout: 300
# suppressOutputLineRegex is a list of regex patterns to suppress output lines from helm diff (default []), available in helmfile v0.162.0
suppressOutputLineRegex:
- "version"
# syncReleaseLabels is a list of labels to be added to the release when syncing.
syncReleaseLabels: false
# these labels will be applied to all releases in a Helmfile. Useful in templating if you have a helmfile per environment or customer and don't want to copy the same label to each release
commonLabels:
@ -287,16 +259,6 @@ releases:
domain: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com
scheme: {{ env "SCHEME" | default "https" }}
# Use `values` whenever possible!
# `setString` translates to helm's `--set-string key=val`
setString:
# set a single array value in an array, translates to --set-string bar[0]={1,2}
- name: bar[0]
values:
- 1
- 2
# set a templated value
- name: namespace
value: {{ .Namespace }}
# `set` translates to helm's `--set key=val`, that is known to suffer from type issues like https://github.com/roboll/helmfile/issues/608
set:
# single value loaded from a local file, translates to --set-file foo.config=path/to/file
@ -313,24 +275,15 @@ releases:
# will attempt to decrypt it using helm-secrets plugin
secrets:
- vault_secret.yaml
# Override helmDefaults options for verify, wait, waitForJobs, timeout, recreatePods, force and reuseValues.
# Override helmDefaults options for verify, wait, waitForJobs, timeout, recreatePods and force.
verify: true
keyring: path/to/keyring.gpg
# --skip-schema-validation flag to helm 'install', 'upgrade' and 'lint', starts with helm 3.16.0 (default false)
skipSchemaValidation: false
wait: true
# DEPRECATED: waitRetries is no longer supported - see documentation above
# waitRetries: 3
waitForJobs: true
timeout: 60
recreatePods: true
force: false
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)
@ -358,26 +311,10 @@ releases:
skipDeps: false
# propagate `--post-renderer` to helmv3 template and helm install
postRenderer: "path/to/postRenderer"
# propagate `--post-renderer-args` to helmv3 template and helm install. This allows using Powershell
# scripts on Windows as a post renderer
postRendererArgs:
- PowerShell
- "-Command"
- "theScript.ps1"
# cascade `--cascade` to helmv3 delete, available values: background, foreground, or orphan, default: background
cascade: "background"
# insecureSkipTLSVerify is true if the TLS verification should be skipped when fetching remote chart
insecureSkipTLSVerify: false
# plainHttp is true if fetching the remote chart should be done using HTTP
plainHttp: false
# suppressDiff skip the helm diff output. Useful for charts which produces large not helpful diff, default: false
suppressDiff: false
# suppressOutputLineRegex is a list of regex patterns to suppress output lines from helm diff (default []), available in helmfile v0.162.0
suppressOutputLineRegex:
- "version"
# syncReleaseLabels is a list of labels to be added to the release when syncing.
syncReleaseLabels: false
# Local chart example
- name: grafana # name of this release
@ -414,16 +351,9 @@ helmfiles:
# The nested-state file is locally checked-out along with the remote directory containing it.
# Therefore all the local paths in the file are resolved relative to the file
path: git::https://github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=0.40.0
- # By default git repositories aren't updated unless the ref is updated.
# Alternatively, refer to a named ref and disable the caching.
path: git::ssh://git@github.com/cloudposse/helmfiles.git@releases/kiam.yaml?ref=main&cache=false
# If set to "Error", return an error when a subhelmfile points to a
# non-existent path. The default behavior is to print a warning and continue.
missingFileHandler: Error
missingFileHandlerConfig:
# Ignores missing git branch error so that the Debug/Info/Warn handler can treat a missing branch as non-error.
# See https://github.com/helmfile/helmfile/issues/392
ignoreMissingGitBranch: true
#
# Advanced Configuration: Environments
@ -443,9 +373,6 @@ environments:
# `{{ .Values.foo.bar }}` is evaluated to `1`.
values:
- environments/default/values.yaml
# Everything from the values.hcl in the `values` block is available via `{{ .Values.KEY }}`.
# More details in its dedicated section
- environments/default/values.hcl
# Each entry in values can be either a file path or inline values.
# The below is an example of inline values, which is merged to the `.Values`
- myChartVer: 1.0.0-dev
@ -514,26 +441,22 @@ Helmfile uses [Go templates](https://godoc.org/text/template) for templating you
We also added the following functions:
* [`env`](templating_funcs.md#env)
* [`requiredEnv`](templating_funcs.md#requiredenv)
* [`exec`](templating_funcs.md#exec)
* [`envExec`](templating_funcs.md#envexec)
* [`readFile`](templating_funcs.md#readfile)
* [`readDir`](templating_funcs.md#readdir)
* [`readDirEntries`](templating_funcs.md#readdirentries)
* [`toYaml`](templating_funcs.md#toyaml)
* [`fromYaml`](templating_funcs.md#fromyaml)
* [`setValueAtPath`](templating_funcs.md#setvalueatpath)
* [`get`](templating_funcs.md#get) (Sprig's original `get` is available as `sprigGet`)
* [`getOrNil`](templating_funcs.md#getornil)
* [`tpl`](templating_funcs.md#tpl)
* [`required`](templating_funcs.md#required)
* [`fetchSecretValue`](templating_funcs.md#fetchsecretvalue)
* [`expandSecretRefs`](templating_funcs.md#expandsecretrefs)
* [`include`](templating_funcs.md#include)
More details on each function can be found at the ["Template Functions" page in our documentation](templating_funcs.md).
* `requiredEnv`
* `exec`
* `envExec`
* `readFile`
* `readDir`
* `readDirEntries`
* `toYaml`
* `fromYaml`
* `setValueAtPath`
* `get` (Sprig's original `get` is available as `sprigGet`)
* `tpl`
* `required`
* `fetchSecretValue`
* `expandSecretRefs`
More details on each function can be found at ["Template Functions" page in our documentation](templating_funcs.md).
## Using environment variables
@ -574,22 +497,20 @@ Helmfile uses some OS environment variables to override default behaviour:
* `HELMFILE_DISABLE_INSECURE_FEATURES` - disable insecure features, expecting `true` lower case
* `HELMFILE_DISABLE_RUNNER_UNIQUE_ID` - disable unique logging ID, expecting any non-empty value
* `HELMFILE_SKIP_INSECURE_TEMPLATE_FUNCTIONS` - disable insecure template functions, expecting `true` lower case
* `HELMFILE_USE_HELM_STATUS_TO_CHECK_RELEASE_EXISTENCE` - expecting non-empty value to use `helm status` to check release existence, instead of `helm list` which is the default behaviour
* `HELMFILE_EXPERIMENTAL` - enable experimental features, expecting `true` lower case
* `HELMFILE_ENVIRONMENT` - specify [Helmfile environment](https://helmfile.readthedocs.io/en/latest/#environment), it has lower priority than CLI argument `--environment`
* `HELMFILE_TEMPDIR` - specify directory to store temporary files
* `HELMFILE_UPGRADE_NOTICE_DISABLED` - expecting any non-empty value to skip the check for the latest version of Helmfile in [helmfile version](https://helmfile.readthedocs.io/en/latest/#version)
* `HELMFILE_GO_YAML_V3` - use *go.yaml.in/yaml/v3* instead of *go.yaml.in/yaml/v2*. It's `false` by default in Helmfile v0.x, and `true` in Helmfile v1.x.
* `HELMFILE_V1MODE` - Helmfile v0.x behaves like v1.x with `true`, Helmfile v1.x behaves like v0.x with `false` as value
* `HELMFILE_GOCCY_GOYAML` - use *goccy/go-yaml* instead of *gopkg.in/yaml.v2*. It's `false` by default in Helmfile v0.x and `true` by default for Helmfile v1.x.
* `HELMFILE_CACHE_HOME` - specify directory to store cached files for remote operations
* `HELMFILE_FILE_PATH` - specify the path to the helmfile.yaml file
* `HELMFILE_INTERACTIVE` - enable interactive mode, expecting `true` lower case. The same as `--interactive` CLI flag
## CLI Reference
```
Declaratively deploy your Kubernetes manifests, Kustomize configs, and Charts as Helm releases in one shot
V1 mode = false
YAML library = go.yaml.in/yaml/v3
YAML library = gopkg.in/yaml.v2
Usage:
helmfile [command]
@ -610,7 +531,6 @@ Available Commands:
lint Lint charts from state file (helm lint)
list List releases defined in state file
repos Add chart repositories defined in state file
show-dag It prints a table with 3 columns, GROUP, RELEASE, and DEPENDENCIES. GROUP is the unsigned, monotonically increasing integer starting from 1. All the releases with the same GROUP are deployed concurrently. Everything in GROUP 2 starts being deployed only after everything in GROUP 1 got successfully deployed. RELEASE is the release that belongs to the GROUP. DEPENDENCIES is the list of releases that the RELEASE depends on. It should always be empty for releases in GROUP 1. DEPENDENCIES for a release in GROUP 2 should have some or all dependencies appeared in GROUP 1. It can be "some" because Helmfile simplifies the DAGs of releases into a DAG of groups, so that Helmfile always produce a single DAG for everything written in helmfile.yaml, even when there are technically two or more independent DAGs of releases in it.
status Retrieve status of releases in state file
sync Sync releases defined in state file
template Template releases defined in state file
@ -619,40 +539,36 @@ Available Commands:
write-values Write values files for releases. Similar to `helmfile template`, write values files instead of manifests.
Flags:
--allow-no-matching-release Do not exit with an error code if the provided selector has no matching releases.
-c, --chart string Set chart. Uses the chart set in release by default, and is available in template as {{ .Chart }}
--color Output with color
--debug Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect
--disable-force-update do not force helm repos to update when executing "helm repo add"
--enable-live-output Show live output from the Helm binary Stdout/Stderr into Helmfile own Stdout/Stderr.
It only applies for the Helm CLI commands, Stdout/Stderr for Hooks are still displayed only when it's execution finishes.
-e, --environment string specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"
-f, --file helmfile.yaml load config from file or directory. defaults to "helmfile.yaml" or "helmfile.yaml.gotmpl" or "helmfile.d" (means "helmfile.d/*.yaml" or "helmfile.d/*.yaml.gotmpl") in this preference. Specify - to load the config from the standard input.
-b, --helm-binary string Path to the helm binary (default "helm")
-h, --help help for helmfile
-i, --interactive Request confirmation before attempting to modify clusters
--kube-context string Set kubectl context. Uses current context by default
-k, --kustomize-binary string Path to the kustomize binary (default "kustomize")
--log-level string Set log level, default info (default "info")
-n, --namespace string Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}
--no-color Output without color
-q, --quiet Silence output. Equivalent to log-level warn
-l, --selector stringArray Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar.
A release must match all labels in a group in order to be used. Multiple groups can be specified at once.
"--selector tier=frontend,tier!=proxy --selector tier=backend" will match all frontend, non-proxy releases AND all backend releases.
The name of a release can be used as a label: "--selector name=myrelease"
--skip-deps skip running "helm repo update" and "helm dependency build"
--state-values-file stringArray specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).
--state-values-set stringArray set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).
--state-values-set-string stringArray set state STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).
--strip-args-values-on-exit-error Strip the potential secret values of the helm command args contained in a helmfile error message (default true)
-v, --version version for helmfile
--allow-no-matching-release Do not exit with an error code if the provided selector has no matching releases.
-c, --chart string Set chart. Uses the chart set in release by default, and is available in template as {{ .Chart }}
--color Output with color
--debug Enable verbose output for Helm and set log-level to debug, this disables --quiet/-q effect
--disable-force-update do not force helm repos to update when executing "helm repo add"
--enable-live-output Show live output from the Helm binary Stdout/Stderr into Helmfile own Stdout/Stderr.
It only applies for the Helm CLI commands, Stdout/Stderr for Hooks are still displayed only when it's execution finishes.
-e, --environment string specify the environment name. Overrides "HELMFILE_ENVIRONMENT" OS environment variable when specified. defaults to "default"
-f, --file helmfile.yaml load config from file or directory. defaults to "helmfile.yaml" or "helmfile.yaml.gotmpl" or "helmfile.d" (means "helmfile.d/*.yaml" or "helmfile.d/*.yaml.gotmpl") in this preference. Specify - to load the config from the standard input.
-b, --helm-binary string Path to the helm binary (default "helm")
-h, --help help for helmfile
-i, --interactive Request confirmation before attempting to modify clusters
--kube-context string Set kubectl context. Uses current context by default
--log-level string Set log level, default info (default "info")
-n, --namespace string Set namespace. Uses the namespace set in the context by default, and is available in templates as {{ .Namespace }}
--no-color Output without color
-q, --quiet Silence output. Equivalent to log-level warn
-l, --selector stringArray Only run using the releases that match labels. Labels can take the form of foo=bar or foo!=bar.
A release must match all labels in a group in order to be used. Multiple groups can be specified at once.
"--selector tier=frontend,tier!=proxy --selector tier=backend" will match all frontend, non-proxy releases AND all backend releases.
The name of a release can be used as a label: "--selector name=myrelease"
--skip-deps skip running "helm repo update" and "helm dependency build"
--state-values-file stringArray specify state values in a YAML file. Used to override .Values within the helmfile template (not values template).
--state-values-set stringArray set state values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2). Used to override .Values within the helmfile template (not values template).
--strip-args-values-on-exit-error On exit error, strip the values of the args
-v, --version version for helmfile
Use "helmfile [command] --help" for more information about a command.
```
**Note:** Each command has its own specific flags. Use `helmfile [command] --help` to see command-specific options. For example, `helmfile sync --help` shows operational flags like `--timeout`, `--wait`, and `--wait-for-jobs`.
### init
The `helmfile init` sub-command checks the dependencies required for helmfile operation, such as `helm`, `helm diff plugin`, `helm secrets plugin`, `helm helm-git plugin`, `helm s3 plugin`. When it does not exist or the version is too low, it can be installed automatically.
@ -668,25 +584,6 @@ The `helmfile sync` sub-command sync your cluster state as described in your `he
Under the covers, Helmfile executes `helm upgrade --install` for each `release` declared in the manifest, by optionally decrypting [secrets](#secrets) to be consumed as helm chart values. It also updates specified chart repositories and updates the
dependencies of any referenced local charts.
#### Common sync flags
* `--timeout SECONDS` - Override the default timeout for all releases in this sync operation. This takes precedence over `helmDefaults.timeout` and per-release `timeout` settings.
* `--wait` - Override the default wait behavior for all releases
* `--wait-for-jobs` - Override the default wait-for-jobs behavior for all releases
Examples:
```bash
# Override timeout for all releases to 10 minutes
helmfile sync --timeout 600
# Combine timeout with wait flags
helmfile sync --timeout 900 --wait --wait-for-jobs
# Target specific releases with custom timeout
helmfile sync --selector tier=backend --timeout 1200
```
For Helm 2.9+ you can use a username and password to authenticate to a remote repository.
### deps
@ -727,7 +624,7 @@ The `helmfile destroy` sub-command uninstalls and purges all the releases define
`helmfile --interactive destroy` instructs Helmfile to request your confirmation before actually deleting releases.
`destroy` basically runs `helm uninstall --purge` on all the targeted releases. If you don't want purging, use `helmfile delete` instead.
If `--skip-charts` flag is not set, destroy would prepare all releases, by fetching charts and templating them.
If `--skip-charts` flag is not set, destory would prepare all releases, by fetching charts and templating them.
### delete (DEPRECATED)
@ -736,7 +633,7 @@ The `helmfile delete` sub-command deletes all the releases defined in the manife
`helmfile --interactive delete` instructs Helmfile to request your confirmation before actually deleting releases.
Note that `delete` doesn't purge releases. So `helmfile delete && helmfile sync` results in sync failed due to that releases names are not deleted but preserved for future references. If you really want to remove releases for reuse, add `--purge` flag to run it like `helmfile delete --purge`.
If `--skip-charts` flag is not set, destroy would prepare all releases, by fetching charts and templating them.
If `--skip-charts` flag is not set, destory would prepare all releases, by fetching charts and templating them.
### secrets
@ -773,16 +670,6 @@ The `helmfile version` sub-command prints the version of Helmfile.Optional `-o`
default it will check for the latest version of Helmfile and print a tip if the current version is not the latest. To disable this behavior, set environment variable `HELMFILE_UPGRADE_NOTICE_DISABLED` to any non-empty value.
### show-dag
It prints a table with 3 columns, GROUP, RELEASE, and DEPENDENCIES.
GROUP is the unsigned, monotonically increasing integer starting from 1. All the releases with the same GROUP are deployed concurrently. Everything in GROUP 2 starts being deployed only after everything in GROUP 1 got successfully deployed.
RELEASE is the release that belongs to the GROUP.
DEPENDENCIES is the list of releases that the RELEASE depends on. It should always be empty for releases in GROUP 1. DEPENDENCIES for a release in GROUP 2 should have some or all dependencies appeared in GROUP 1. It can be "some" because Helmfile simplifies the DAGs of releases into a DAG of groups, so that Helmfile always produce a single DAG for everything written in helmfile.yaml, even when there are technically two or more independent DAGs of releases in it.
## Paths Overview
Using manifest files in conjunction with command line argument can be a bit confusing.
@ -800,7 +687,7 @@ For additional context, take a look at [paths examples](paths.md).
A selector can be used to only target a subset of releases when running Helmfile. This is useful for large helmfiles with releases that are logically grouped together.
Labels are simple key value pairs that are an optional field of the release spec. When selecting by label, the search can be inverted. `tier!=backend` would match all releases that do NOT have the `tier: backend` label. `tier=frontend` would only match releases with the `tier: frontend` label.
Labels are simple key value pairs that are an optional field of the release spec. When selecting by label, the search can be inverted. `tier!=backend` would match all releases that do NOT have the `tier: backend` label. `tier=fronted` would only match releases with the `tier: frontend` label.
Multiple labels can be specified using `,` as a separator. A release must match all selectors in order to be selected for the final helm command.
@ -948,7 +835,6 @@ proxy:
When you want to customize the contents of `helmfile.yaml` or `values.yaml` files per environment, use this feature.
You can define as many environments as you want under `environments` in `helmfile.yaml`.
`environments` section should be separated from `releases` with `---`.
The environment name defaults to `default`, that is, `helmfile sync` implies the `default` environment.
The selected environment name can be referenced from `helmfile.yaml` and `values.yaml.gotmpl` by `{{ .Environment.Name }}`.
@ -972,13 +858,9 @@ releases:
# snip
```
### Environment Values
Helmfile supports 3 values languages :
- Straight yaml
- Go templates to generate straight yaml
- HCL
## Environment Values
Environment Values allows you to inject a set of values specific to the selected environment, into `values.yaml` templates.
Environment Values allows you to inject a set of values specific to the selected environment, into values.yaml templates.
Use it to inject common values from the environment to multiple values files, to make your configuration DRY.
Suppose you have three files `helmfile.yaml`, `production.yaml` and `values.yaml.gotmpl`:
@ -1047,45 +929,7 @@ releases:
...
```
#### HCL specifications
Since Helmfile v0.164.0, HCL language is supported for environment values only.
HCL values supports interpolations and sharing values across files
* Only `.hcl` suffixed files will be interpreted as is
* Helmfile supports 2 differents blocks: `values` and `locals`
* `values` block is a shared block where all values are accessible everywhere in all loaded files
* `locals` block can't reference external values apart from the ones in the block itself, and where its defined values are only accessible in its local file
* Only values in `values` blocks are made available to the final root `.Values` (e.g : ` values { myvar = "var" }` is accessed through `{{ .Values.myvar }}`)
* There can only be 1 `locals` block per file
* Helmfile hcl `values` are referenced using the `hv` accessor.
* Helmfile hcl `locals` are referenced using the `local` accessor.
* Duplicated variables across .hcl `values` blocks are forbidden (An error will pop up specifying where are the duplicates)
* All cty [standard library functions](`https://pkg.go.dev/github.com/zclconf/go-cty@v1.14.3/cty/function/stdlib`) are available and custom functions could be created in the future
Consider the following example :
```terraform
# values1.hcl
locals {
hostname = "host1"
}
values {
domain = "DEV.EXAMPLE.COM"
hostnameV1 = "${local.hostname}.${lower(hv.domain)}" # "host1.dev.example.com"
}
```
```terraform
# values2.hcl
locals {
hostname = "host2"
}
values {
hostnameV2 = "${local.hostname}.${hv.domain}" # "host2.DEV.EXAMPLE.COM"
}
```
#### Note on Environment.Values vs Values
### Note on Environment.Values vs Values
The `{{ .Values.foo }}` syntax is the recommended way of using environment values.
@ -1094,7 +938,37 @@ This is still working but is **deprecated** and the new `{{ .Values.foo }}` synt
You can read more infos about the feature proposal [here](https://github.com/roboll/helmfile/issues/640).
### Environment Secrets
### Loading remote Environment values files
Since Helmfile v0.118.8, you can use `go-getter`-style URLs to refer to remote values files:
```yaml
environments:
cluster-azure-us-west:
values:
- git::https://git.company.org/helmfiles/global/azure.yaml?ref=master
- git::https://git.company.org/helmfiles/global/us-west.yaml?ref=master
- git::https://gitlab.com/org/repository-name.git@/config/config.test.yaml?ref=main # Public Gilab Repo
cluster-gcp-europe-west:
values:
- git::https://git.company.org/helmfiles/global/gcp.yaml?ref=master
- git::https://git.company.org/helmfiles/global/europe-west.yaml?ref=master
- git::https://ci:{{ env "CI_JOB_TOKEN" }}@gitlab.com/org/repository-name.git@/config.dev.yaml?ref={{ env "APP_COMMIT_SHA" }} # Private Gitlab Repo
staging:
values:
- git::https://{{ env "GITHUB_PAT" }}@github.com/[$GITHUB_ORGorGITHUB_USER]/repository-name.git@/values.dev.yaml?ref=main #Github Private repo
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@values.yaml #Artifactory url
---
releases:
- ...
```
For more information about the supported protocols see: [go-getter Protocol-Specific Options](https://github.com/hashicorp/go-getter#protocol-specific-options-1).
This is particularly useful when you co-locate helmfiles within your project repo but want to reuse the definitions in a global repo.
## Environment Secrets
Environment Secrets *(not to be confused with Kubernetes Secrets)* are encrypted versions of `Environment Values`.
You can list any number of `secrets.yaml` files created using `helm secrets` or `sops`, so that
@ -1135,7 +1009,7 @@ Then the environment secret `foo.bar` can be referenced by the below template ex
{{ .Values.foo.bar }}
```
#### Loading remote Environment secrets files
### Loading remote Environment secrets files
Since Helmfile v0.149.0, you can use `go-getter`-style URLs to refer to remote secrets files, the same way as in values files:
```yaml
@ -1150,91 +1024,6 @@ environments:
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@environments/production.secret.yaml
```
### Loading remote Environment values files
Since Helmfile v0.118.8, you can use `go-getter`-style URLs to refer to remote values files:
```yaml
environments:
cluster-azure-us-west:
values:
- git::https://git.company.org/helmfiles/global/azure.yaml?ref=master
- git::https://git.company.org/helmfiles/global/us-west.yaml?ref=master
- git::https://gitlab.com/org/repository-name.git@/config/config.test.yaml?ref=main # Public Gilab Repo
cluster-gcp-europe-west:
values:
- git::https://git.company.org/helmfiles/global/gcp.yaml?ref=master
- git::https://git.company.org/helmfiles/global/europe-west.yaml?ref=master
- git::https://ci:{{ env "CI_JOB_TOKEN" }}@gitlab.com/org/repository-name.git@/config.dev.yaml?ref={{ env "APP_COMMIT_SHA" }} # Private Gitlab Repo
staging:
values:
- git::https://{{ env "GITHUB_PAT" }}@github.com/[$GITHUB_ORGorGITHUB_USER]/repository-name.git@/values.dev.yaml?ref=main #Github Private repo
- http://$HOSTNAME/artifactory/example-repo-local/test.tgz@values.yaml #Artifactory url
---
releases:
- ...
```
Since Helmfile v0.158.0, support more protocols, such as: s3, https, http
```
values:
- s3::https://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
- s3://helm-s3-values-example/subdir/values.yaml
- https://john:doe@helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
- http://helm-s3-values-example.s3.us-east-2.amazonaws.com/values.yaml
```
For more information about the supported protocols see: [go-getter Protocol-Specific Options](https://github.com/hashicorp/go-getter#protocol-specific-options-1).
This is particularly useful when you co-locate helmfiles within your project repo but want to reuse the definitions in a global repo.
### Environment values precedence
With the introduction of HCL, a new value precedence was introduced over environment values.
Here is the order of precedence from least to greatest (the last one overrides all others)
1. `yaml` / `yaml.gotmpl`
2. `hcl`
3. `yaml` secrets
Example:
---
```yaml
# values1.yaml
domain: "dev.example.com"
```
```terraform
# values2.hcl
values {
domain = "overdev.example.com"
willBeOverriden = "override_me"
}
```
```yaml
# secrets.yml (assuming this one has been encrypted)
willBeOverriden: overrided
```
```
# helmfile.yaml.gotmpl
environments:
default:
values:
- value1.yaml
- value2.hcl
secrets:
- secrets.yml
---
releases:
- name: random-release
[...]
values:
domain: "{{ .Values.domain }}" # == "overdev.example.com"
willBeOverriden: "{{ .Values.willBeOverriden }}" # == "overrided"
```
## DAG-aware installation/deletion ordering with `needs`
`needs` controls the order of the installation/deletion of the release:
@ -1399,7 +1188,7 @@ helmfiles:
- name=prometheus
- tier=frontend
- path: apps/b-helmfile.yaml # no selector, so all releases are used
selectors: []
selectors: []
- path: apps/c-helmfile.yaml # parent selector to be used or cli selector for the initial helmfile
selectorsInherited: true
```
@ -1710,9 +1499,6 @@ Use it when you're running `helmfile` manually on your local machine or a kind o
For your local use-case, aliasing it like `alias hi='helmfile --interactive'` would be convenient.
Another way to use it is to set the environment variable `HELMFILE_INTERACTIVE=true` to enable the interactive mode by default.
Anything other than `true` will disable the interactive mode. The precedence has the `--interactive` flag.
## Running Helmfile without an Internet connection
Once you download all required charts into your machine, you can run `helmfile sync --skip-deps` to deploy your apps.

View File

@ -4,22 +4,6 @@ I'd like to make 3 breaking changes to Helmfile and mark it as 1.0, so that we c
Note that every breaking change should have an easy alternative way to achieve the same goal achieved using the removed functionality.
## Backward compatibility
v1 is backward-compatible with v0.x, except for the following breaking changes.
Each breaking change has an easy alternative way to achieve the same goal achieved using the removed functionality.
We also provide the alternative way in the latest v0.x release before v1.0. That way you can start using the alternative way today and be ready for v1.0. Note that in v0.x, some of those alternative ways are enabled only when `HELMFILE_V1MODE=true` is set.
> Context:
>
> Even though Helmfile had been used in production environments [across multiple organizations](USERS.md), it had been considered to be in its early stage of development, hence versioned 0.x.
>
> Helmfile complies to Semantic Versioning 2.0.0 in which v0.x means that there could be backward-incompatible changes for every release. However, Helmfile has been very conservative about breaking changes, and we had no breaking change for a year or so before start thinking about v1.
>
> That said, you can expect Helmfile v1 to be backward-compatible as much as it was in v0.x.
## The changes in 1.0
1. [Forbid the use of `environments` and `releases` within a single helmfile.yaml.gotmpl part](#forbid-the-use-of-environments-and-releases-within-a-single-helmfileyamlgotmpl-part)
@ -27,8 +11,6 @@ We also provide the alternative way in the latest v0.x release before v1.0. That
3. [Remove the `--args` flag from the `helmfile` command](#remove-the---args-flag-from-the-helmfile-command)
4. [Remove `HELMFILE_SKIP_INSECURE_TEMPLATE_FUNCTIONS` in favor of `HELMFILE_DISABLE_INSECURE_FEATURES`](#remove-helmfile_skip_insecure_template_functions-in-favor-of-helmfile_disable_insecure_features)
5. [The long deprecated `charts.yaml` has been finally removed](#the-long-deprecated-chartsyaml-has-been-finally-removed)
6. [List experimental features](#list-experimental-features)
7. [Remove charts and delete sub-commands](#remove-charts-and-delete-subcommands)
### Forbid the use of `environments` and `releases` within a single helmfile.yaml.gotmpl part
@ -93,27 +75,13 @@ We also provide the alternative way in the latest v0.x release before v1.0. That
### Remove `HELMFILE_SKIP_INSECURE_TEMPLATE_FUNCTIONS` in favor of `HELMFILE_DISABLE_INSECURE_FEATURES`
- This option didn't make much sense in practice. Generally, you'd want to disable all the insecure features altogether to make your deployment secure, or not disable any features. Disabling all is already possible via `HELMFILE_DISABLE_INSECURE_FEATURES `. In addition, `HELMFILE_SKIP_INSECURE_TEMPLATE_FUNCTIONS` literally made every insecure template function to silently skipped without any error or warning, which made debugging unnecessarily hard when the user accidentally used an insecure function.
- This option didn't make much sense in practical. Generally, you'd want to disable all the insecure features altogether to make your deployment secure, or not disable any features. Disabling all is already possible via `HELMFILE_DISABLE_INSECURE_FEATURES `. In addition, `HELMFILE_SKIP_INSECURE_TEMPLATE_FUNCTIONS` literally made every insecure template function to silently skipped without any error or warning, which made debugging unnecessarily hard when the user accidentally used an insecure function.
- See https://github.com/helmfile/helmfile/pull/564 for more context.
### The long deprecated `charts.yaml` has been finally removed
Helmfile used to load `helmfile.yaml` or `charts.yaml` when you omitted the `-f` flag. `charts.yaml` has been deprecated for a long time but never been removed. We take v1 as a chance to finally remove it.
### List experimental features
We have some experimental features that are not stable yet. We should list them in a list and mark them as experimental.
In Helmfile v1.x, all features should be backward-compatible within v1.x as we follow semver. We can't fix features in a backward incompatible way by default. To do so, we need a list of experimental features and say "anything in the experimentals can be modified backward-incompatible ways", and include features that are consireded experimental into the list beforehand.
Why now?
In Helmfile v0.x, all features considered experimental as we follow semver. However, we "ended up" preserving backward-compatibility within v0 and between v0 and v1 "by chance". This doesn't mean anything
introduced in v0 is stable. For example, we might have some features implemented in a very later stage of v0 that are not stable yet. We should mark them as experimental, or we can't fix them in a backward-incompatible way in v1.x. That's why we need to list experimental features now.
### remove-charts-and-delete-subcommands
Now we remove `helmfile charts` and `helmfile delete` subcommands. you can use `helmfile destroy` and `helmfile sync` instead.
## After 1.0
We won't add any backward-incompatible changes while in 1.x, as long as it's inevitable to fix unseen important bug(s).

View File

@ -17,8 +17,8 @@ To fetch single key from remote secret storage you can use `fetchSecretValue` te
repositories:
- name: stable
url: https://charts.helm.sh/stable
---
url: https://kubernetes-charts.storage.googleapis.com
environments:
default:
values:
@ -55,3 +55,4 @@ service:
password: pass
```

View File

@ -1,22 +1,22 @@
Babel==2.17.0
Babel==2.9.1
click==8.1.2
ghp-import==2.0.2
gitdb==4.0.9
GitPython==3.1.41
GitPython==3.1.30
importlib-metadata==4.11.3
Jinja2==3.1.6
Markdown==3.6
Jinja2==3.1.1
Markdown==3.3.6
MarkupSafe==2.1.1
mergedeep==1.3.4
mkdocs==1.6.0
mkdocs==1.3.0
mkdocs-git-revision-date-localized-plugin==1.0.1
packaging==21.3
pyparsing==3.0.7
python-dateutil==2.8.2
pytz==2022.1
PyYAML==6.0.2
PyYAML==6.0
pyyaml_env_tag==0.1
six==1.16.0
smmap==5.0.0
watchdog==2.1.7
zipp==3.19.1
zipp==3.7.0

View File

@ -1,6 +1,6 @@
# Shared Configuration Across Teams
Assume you have two or more teams, each work for a different internal or external service, like:
Assume you have a two or more teams, each works for a different internal or external service, like:
- Product 1
- Product 2
@ -30,7 +30,7 @@ releases:
# snip
```
This works, but what if you wanted to a separate cluster per service to achieve a smaller blast radius?
This works, but what if you wanted to a separate cluster per service to achieve smaller blast radius?
Let's start by creating a `helmfile.yaml` for each service.

View File

@ -32,20 +32,6 @@ The `envExec` function allows you to run a command with environment variables de
{{ $cmdOutpot := envExec (dict "envKey" "envValue") "./mycmd" (list "arg1" "arg2" "--flag1") }}
```
#### `isFile`
The `isFile` function allows you to check if a file exists. On failure, the template rendering will fail with an error message.
```yaml
{{ if isFile "./myfile" }}
```
#### `isDir`
The `isDir` function allows you to check if a directory exists. On failure, the template rendering will fail with an error message.
```yaml
{{ if isDir "./mydirectory" }}
```
#### `readFile`
The `readFile` function allows you to read a file and return its content as the function output. On failure, the template rendering will fail with an error message.
@ -100,14 +86,14 @@ The `setValueAtPath` function allows you to set a value at a path. When has fail
```
#### `get`
The `get` function allows you to get a value at a path. you can set a default value when the path is not found. When has failed, the template rendering will fail with an error message.
The `get` function allows you to get a value at a path. when defaultValue not set. It will return nil. When has failed, the template rendering will fail with an error message.
```yaml
{{ $Getvalue := $value | get "path.key" "defaultValue" }}
```
#### `getOrNil`
The `getOrNil` function allows you to get a value at a path. it will return nil when the value of path is not found. When has failed, the template rendering will fail with an error message.
The `getOrNil` function allows you to get a value at a path. when defaultValue not set. It will return nil. When has failed, the template rendering will fail with an error message.
```yaml
{{ $GetOrNlvalue := $value | getOrNil "path.key" }}
@ -140,11 +126,3 @@ The `expandSecretRefs` function takes an object as the argument and expands ever
```yaml
{{ $expandSecretRefs := $value | expandSecretRefs }}
```
#### `include`
The 'include' function allows including and rendering nested templates. The function returns the created template or an error if any occurred. It will load functions from `_*.tpl` files in the directory where the helmfile.yaml is located.
For nested helmfile.yaml files, it will load `_*.tpl` files in the directory where each nested helmfile.yaml is located. example: [include](https://github.com/helmfile/helmfile/tree/main/test/integration/test-cases/include-template-func/input)
```yaml
{{ include "my-template" . }}
```

View File

@ -104,8 +104,6 @@ releases:
version: 0.3.2
inherit:
- template: default
except:
- secrets
- name: kubernetes-dashboard
version: 0.10.0
inherit:
@ -155,7 +153,7 @@ Thus we added a new way for inheritance, which uses the `inherit` field we intro
See [issue helmfile/helmfile#435](https://github.com/helmfile/helmfile/issues/435#issuecomment-1362177510) for more context.
You might also find [issue roboll/helmfile#428](https://github.com/roboll/helmfile/issues/428) useful for more context on how we originally designed the release template and what it's supposed to solve.
You might also find [issue roboll/helmfile#428](https://github.com/roboll/helmfile/issues/428) useful for more context on how we originally designed the relase template and what it's supposed to solve.
## Layering Release Values

View File

@ -17,7 +17,7 @@ releases:
# DB host, port, and connection opts for the environment
values:
- "deploy/environments/{{ env "RAILS_ENV" }}/values.yaml"
# DB username and password encrypted with helm-secrets (getsops/sops)
# DB username and password encrypted with helm-secrets(mozilla/sops)
secrets:
- "deploy/environments/{{ env "RAILS_ENV" }}/secrets.yaml"
```

456
go.mod
View File

@ -1,326 +1,250 @@
module github.com/helmfile/helmfile
go 1.24.6
go 1.20
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.89.0
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/go-test/deep v1.1.1
github.com/Masterminds/semver/v3 v3.2.1
github.com/Masterminds/sprig/v3 v3.2.3
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
github.com/davecgh/go-spew v1.1.1
github.com/go-test/deep v1.1.0
github.com/goccy/go-yaml v1.11.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.7.0
github.com/google/go-cmp v0.5.9
github.com/gosuri/uitable v0.0.4
github.com/hashicorp/go-getter v1.8.2
github.com/hashicorp/hcl/v2 v2.24.0
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.10
github.com/stretchr/testify v1.11.1
github.com/hashicorp/go-getter v1.7.1
github.com/helmfile/chartify v0.14.0
github.com/helmfile/vals v0.25.0
github.com/imdario/mergo v0.3.16
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
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.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.3
go.yaml.in/yaml/v3 v3.0.4
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
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.24.0
golang.org/x/sync v0.3.0
golang.org/x/term v0.9.0
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.12.1
k8s.io/apimachinery v0.27.3
)
replace gopkg.in/yaml.v3 => github.com/colega/go-yaml-yaml v0.0.0-20220720070545-aaba007ebc22
require (
cloud.google.com/go v0.121.6 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/storage v1.57.0 // indirect
filippo.io/age v1.2.1 // indirect
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/iam v0.12.0 // indirect
cloud.google.com/go/storage v1.28.1 // indirect
filippo.io/age v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
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/a8m/envsubst v1.3.0 // indirect
github.com/aws/aws-sdk-go v1.44.122 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/fatih/color v1.18.0
github.com/fujiwara/tfstate-lookup v1.7.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fujiwara/tfstate-lookup v1.1.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
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-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.22.0 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-slug v0.8.1 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-tfe v1.2.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
github.com/hashicorp/vault/api v1.5.0 // indirect
github.com/hashicorp/vault/sdk v0.4.1 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/gojq v0.12.16 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/itchyny/gojq v0.12.11 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/otiai10/copy v1.14.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/otiai10/copy v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a // indirect
go.mozilla.org/sops/v3 v3.7.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.9.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.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
golang.org/x/net v0.8.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.110.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/urfave/cli.v1 v1.20.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
require (
al.essio.dev/pkg/shellescape v1.6.0 // indirect
cel.dev/expr v0.24.0 // 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.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
filippo.io/edwards25519 v1.1.0 // indirect
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.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.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.5.0 // indirect
github.com/DopplerHQ/cli v0.5.11-0.20230908185655-7aef4713e1a4 // 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.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
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/secretmanager v1.10.0 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets v0.10.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.9.0 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
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.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/armon/go-metrics v0.3.10 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.17.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.54 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.24 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.23 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.4 // indirect
github.com/aws/smithy-go v1.13.5 // 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
github.com/cespare/xxhash/v2 v2.3.0 // indirect
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.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.7 // indirect
github.com/cyberark/conjur-api-go v0.13.7 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/containerd v1.7.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/docker/cli v20.10.21+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v20.10.24+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/extism/go-sdk v1.7.0 // indirect
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.11.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // 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.1.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // 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
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.1 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-jsonnet v0.20.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
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/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/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.3 // 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.162.0 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // 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/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect
github.com/itchyny/timefmt-go v0.1.5 // 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
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
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/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
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/termenv v0.15.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
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
github.com/tetratelabs/wazero v1.9.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
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.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.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.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.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.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-20251002232023-7c0ddcbb5797 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/gookit/color.v1 v1.1.6 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/xlab/treeprint v1.1.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
go.uber.org/goleak v1.1.12 // indirect
golang.org/x/crypto v0.7.0 // 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.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.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.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
k8s.io/api v0.27.2 // indirect
k8s.io/cli-runtime v0.27.2 // indirect
k8s.io/client-go v0.27.2 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
oras.land/oras-go v1.2.2 // indirect
sigs.k8s.io/kustomize/api v0.13.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
)

2052
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -17,15 +17,13 @@ nav:
- Home: index.md
- Getting Started:
- Paths Overview: paths.md
- Templating Funcs: templating_funcs.md
- HCL Funcs: hcl_funcs.md
- Templating Funs: templating_funcs.md
- Built-in Objects: builtin-objects.md
- Advanced Features:
- Best Practices Guide: writing-helmfile.md
- Advanced Features: advanced-features.md
- Secrets: remote-secrets.md
- Shared Configuration Across Teams: shared-configuration-across-teams.md
- Experimental Features: experimental-features.md
- About:
- Users: users.md
- License: license.md

View File

@ -15,11 +15,11 @@ import (
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/argparser"
"github.com/helmfile/helmfile/pkg/envvar"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/plugins"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/runtime"
"github.com/helmfile/helmfile/pkg/state"
)
@ -30,13 +30,11 @@ var Cancel goContext.CancelFunc
type App struct {
OverrideKubeContext string
OverrideHelmBinary string
OverrideKustomizeBinary string
EnableLiveOutput bool
StripArgsValuesOnExitError bool
DisableForceUpdate bool
Logger *zap.SugaredLogger
Kubeconfig string
Env string
Namespace string
Chart string
@ -76,12 +74,10 @@ func New(conf ConfigProvider) *App {
return Init(&App{
OverrideKubeContext: conf.KubeContext(),
OverrideHelmBinary: conf.HelmBinary(),
OverrideKustomizeBinary: conf.KustomizeBinary(),
EnableLiveOutput: conf.EnableLiveOutput(),
StripArgsValuesOnExitError: conf.StripArgsValuesOnExitError(),
DisableForceUpdate: conf.DisableForceUpdate(),
Logger: conf.Logger(),
Kubeconfig: conf.Kubeconfig(),
Env: conf.Env(),
Namespace: conf.Namespace(),
Chart: conf.Chart(),
@ -121,7 +117,19 @@ func (a *App) Init(c InitConfigProvider) error {
func (a *App) Deps(c DepsConfigProvider) error {
return a.ForEachState(func(run *Run) (_ bool, errs []error) {
errs = run.Deps(c)
prepErr := run.withPreparedCharts("deps", state.ChartPrepareOptions{
SkipRepos: c.SkipRepos(),
SkipDeps: true,
SkipResolve: true,
Concurrency: c.Concurrency(),
}, func() {
errs = run.Deps(c)
})
if prepErr != nil {
errs = append(errs, prepErr)
}
return
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
@ -138,6 +146,25 @@ func (a *App) Repos(c ReposConfigProvider) error {
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
// TODO: Remove this function once Helmfile v0.x
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
return a.ForEachState(func(run *Run) (_ bool, errs []error) {
err := run.withPreparedCharts("charts", state.ChartPrepareOptions{
SkipRepos: true,
SkipDeps: true,
Concurrency: 2,
}, func() {
errs = run.DeprecatedSyncCharts(c)
})
if err != nil {
errs = append(errs, err)
}
return
}, c.IncludeTransitiveNeeds(), SetFilter(true))
}
func (a *App) Diff(c DiffConfigProvider) error {
var allDiffDetectedErrs []error
@ -155,8 +182,7 @@ func (a *App) Diff(c DiffConfigProvider) error {
includeCRDs := !c.SkipCRDs()
prepErr := run.withPreparedCharts("diff", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
IncludeCRDs: &includeCRDs,
Validate: c.Validate(),
@ -199,7 +225,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 deferred errs should have exit status 2)
// We take the first release error w/ exit status 2 (although all the defered 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
@ -220,13 +246,8 @@ func (a *App) Template(c TemplateConfigProvider) error {
// Live output should never be enabled for the "template" subcommand to avoid breaking `helmfile template | kubectl apply -f -`
run.helm.SetEnableLiveOutput(false)
// Reset helm extra args to not pollute BuildDeps() and AddRepo() on subsequent helmfiles
// https://github.com/helmfile/helmfile/issues/1749
run.helm.SetExtraArgs()
prepErr := run.withPreparedCharts("template", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
IncludeCRDs: &includeCRDs,
SkipCleanup: c.SkipCleanup(),
@ -234,8 +255,6 @@ func (a *App) Template(c TemplateConfigProvider) error {
Concurrency: c.Concurrency(),
IncludeTransitiveNeeds: c.IncludeNeeds(),
Set: c.Set(),
Values: c.Values(),
KubeVersion: c.KubeVersion(),
}, func() {
ok, errs = a.template(run, c)
})
@ -251,8 +270,7 @@ func (a *App) Template(c TemplateConfigProvider) error {
func (a *App) WriteValues(c WriteValuesConfigProvider) error {
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
prepErr := run.withPreparedCharts("write-values", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
SkipCleanup: c.SkipCleanup(),
Concurrency: c.Concurrency(),
@ -303,8 +321,7 @@ func (a *App) Lint(c LintConfigProvider) error {
// `helm lint` on helm v2 and v3 does not support remote charts, that we need to set `forceDownload=true` here
prepErr := run.withPreparedCharts("lint", state.ChartPrepareOptions{
ForceDownload: true,
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
SkipCleanup: c.SkipCleanup(),
Concurrency: c.Concurrency(),
@ -339,13 +356,13 @@ func (a *App) Fetch(c FetchConfigProvider) error {
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
prepErr := run.withPreparedCharts("pull", state.ChartPrepareOptions{
ForceDownload: true,
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
OutputDir: c.OutputDir(),
OutputDirTemplate: c.OutputDirTemplate(),
Concurrency: c.Concurrency(),
}, func() {})
}, func() {
})
if prepErr != nil {
errs = append(errs, prepErr)
@ -360,11 +377,9 @@ func (a *App) Sync(c SyncConfigProvider) error {
includeCRDs := !c.SkipCRDs()
prepErr := run.withPreparedCharts("sync", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Wait: c.Wait(),
WaitRetries: c.WaitRetries(),
WaitForJobs: c.WaitForJobs(),
IncludeCRDs: &includeCRDs,
IncludeTransitiveNeeds: c.IncludeNeeds(),
@ -389,20 +404,18 @@ func (a *App) Apply(c ApplyConfigProvider) error {
var opts []LoadOption
opts = append(opts, SetRetainValuesFiles(c.SkipCleanup()))
opts = append(opts, SetRetainValuesFiles(c.RetainValuesFiles() || c.SkipCleanup()))
err := a.ForEachState(func(run *Run) (ok bool, errs []error) {
includeCRDs := !c.SkipCRDs()
prepErr := run.withPreparedCharts("apply", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Wait: c.Wait(),
WaitRetries: c.WaitRetries(),
WaitForJobs: c.WaitForJobs(),
IncludeCRDs: &includeCRDs,
SkipCleanup: c.SkipCleanup(),
SkipCleanup: c.RetainValuesFiles() || c.SkipCleanup(),
Validate: c.Validate(),
Concurrency: c.Concurrency(),
IncludeTransitiveNeeds: c.IncludeNeeds(),
@ -454,16 +467,35 @@ func (a *App) Status(c StatusesConfigProvider) error {
}, false, SetFilter(true))
}
// TODO: Remove this function once Helmfile v0.x
func (a *App) Delete(c DeleteConfigProvider) error {
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
if !c.SkipCharts() {
err := run.withPreparedCharts("delete", state.ChartPrepareOptions{
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Concurrency: c.Concurrency(),
}, func() {
ok, errs = a.delete(run, c.Purge(), c)
})
if err != nil {
errs = append(errs, err)
}
} else {
ok, errs = a.delete(run, c.Purge(), c)
}
return
}, false, SetReverse(true))
}
func (a *App) Destroy(c DestroyConfigProvider) error {
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
if !c.SkipCharts() {
err := run.withPreparedCharts("destroy", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipDeps: c.SkipDeps(),
Concurrency: c.Concurrency(),
DeleteWait: c.DeleteWait(),
DeleteTimeout: c.DeleteTimeout(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Concurrency: c.Concurrency(),
}, func() {
ok, errs = a.delete(run, true, c)
})
@ -486,8 +518,7 @@ func (a *App) Test(c TestConfigProvider) error {
}
err := run.withPreparedCharts("test", state.ChartPrepareOptions{
SkipRepos: c.SkipRefresh() || c.SkipDeps(),
SkipRefresh: c.SkipRefresh(),
SkipRepos: c.SkipDeps(),
SkipDeps: c.SkipDeps(),
Concurrency: c.Concurrency(),
}, func() {
@ -502,23 +533,6 @@ func (a *App) Test(c TestConfigProvider) error {
}, false, SetFilter(true))
}
func (a *App) PrintDAGState(c DAGConfigProvider) error {
var err error
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
err = run.withPreparedCharts("show-dag", state.ChartPrepareOptions{
SkipRepos: true,
SkipDeps: true,
Concurrency: 2,
}, func() {
err = a.dag(run)
if err != nil {
errs = append(errs, err)
}
})
return ok, errs
}, false, SetFilter(true))
}
func (a *App) PrintState(c StateConfigProvider) error {
return a.ForEachState(func(run *Run) (_ bool, errs []error) {
err := run.withPreparedCharts("build", state.ChartPrepareOptions{
@ -572,19 +586,6 @@ func (a *App) PrintState(c StateConfigProvider) error {
}, false, SetFilter(true))
}
func (a *App) dag(r *Run) error {
st := r.state
batches, err := st.PlanReleases(state.PlanOptions{SelectedReleases: st.Releases, Reverse: false, SkipNeeds: false, IncludeNeeds: true, IncludeTransitiveNeeds: true})
if err != nil {
return err
}
fmt.Print(printDAG(batches))
return nil
}
func (a *App) ListReleases(c ListConfigProvider) error {
var releases []*HelmRelease
@ -659,10 +660,11 @@ func (a *App) list(run *Run) ([]*HelmRelease, error) {
return nil, err
}
installed := r.Installed == nil || *r.Installed
releases = append(releases, &HelmRelease{
Name: r.Name,
Namespace: r.Namespace,
Installed: r.Desired(),
Installed: installed,
Enabled: enabled,
Labels: labels,
Chart: r.Chart,
@ -680,7 +682,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 directory: %v", err)
return fmt.Errorf("failed getting current working direcotyr: %v", err)
}
absDir, err := a.fs.Abs(dir)
@ -756,12 +758,11 @@ func (a *App) loadDesiredStateFromYaml(file string, opts ...LoadOpts) (*state.He
logger: a.Logger,
remote: a.remote,
overrideKubeContext: a.OverrideKubeContext,
overrideHelmBinary: a.OverrideHelmBinary,
overrideKustomizeBinary: a.OverrideKustomizeBinary,
enableLiveOutput: a.EnableLiveOutput,
getHelm: a.getHelm,
valsRuntime: a.valsRuntime,
overrideKubeContext: a.OverrideKubeContext,
overrideHelmBinary: a.OverrideHelmBinary,
enableLiveOutput: a.EnableLiveOutput,
getHelm: a.getHelm,
valsRuntime: a.valsRuntime,
}
return ld.Load(file, op)
@ -784,7 +785,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, error) {
func (a *App) getHelm(st *state.HelmState) helmexec.Interface {
a.helmsMutex.Lock()
defer a.helmsMutex.Unlock()
@ -793,27 +794,19 @@ func (a *App) getHelm(st *state.HelmState) (helmexec.Interface, error) {
}
bin := st.DefaultHelmBinary
if bin == "" {
bin = state.DefaultHelmBinary
}
kubeconfig := a.Kubeconfig
kubectx := st.HelmDefaults.KubeContext
key := createHelmKey(bin, kubectx)
if _, ok := a.helms[key]; !ok {
exec, err := helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, kubeconfig, kubectx, &helmexec.ShellRunner{
a.helms[key] = helmexec.New(bin, helmexec.HelmExecOptions{EnableLiveOutput: a.EnableLiveOutput, DisableForceUpdate: a.DisableForceUpdate}, a.Logger, 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], nil
return a.helms[key]
}
func (a *App) visitStates(fileOrDir string, defOpts LoadOpts, converge func(*state.HelmState) (bool, []error)) error {
@ -965,10 +958,7 @@ 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, err := a.getHelm(st)
if err != nil {
return false, []error{err}
}
helm := a.getHelm(st)
run, err := NewRun(st, helm, ctx)
if err != nil {
@ -987,36 +977,14 @@ func printBatches(batches [][]state.Release) string {
w.Init(buf, 0, 1, 1, ' ', 0)
_, _ = fmt.Fprintln(w, "GROUP\tRELEASES")
fmt.Fprintln(w, "GROUP\tRELEASES")
for i, batch := range batches {
ids := []string{}
for _, r := range batch {
ids = append(ids, state.ReleaseToID(&r.ReleaseSpec))
}
_, _ = fmt.Fprintf(w, "%d\t%s\n", i+1, strings.Join(ids, ", "))
}
_ = w.Flush()
return buf.String()
}
func printDAG(batches [][]state.Release) string {
buf := &bytes.Buffer{}
w := new(tabwriter.Writer)
w.Init(buf, 0, 1, 1, ' ', 0)
_, _ = fmt.Fprintln(w, "GROUP\tRELEASE\tDEPENDENCIES")
for i, batch := range batches {
for _, r := range batch {
id := state.ReleaseToID(&r.ReleaseSpec)
needs := r.Needs
_, _ = fmt.Fprintf(w, "%d\t%s\t%s\n", i+1, id, strings.Join(needs, ", "))
}
fmt.Fprintf(w, "%d\t%s\n", i+1, strings.Join(ids, ", "))
}
_ = w.Flush()
@ -1115,17 +1083,9 @@ func (a *App) visitStatesWithSelectorsAndRemoteSupport(fileOrDir string, converg
}
}
// pre-handles HelmState
// pre-overrides HelmState
fHelmStatsWithOverrides := func(st *state.HelmState) (bool, []error) {
var err error
// override release settings
st.Releases, err = st.GetReleasesWithOverrides()
if err != nil {
return false, []error{err}
}
// override release labels
st.Releases = st.GetReleasesWithLabels()
st.Releases = st.GetReleasesWithOverrides()
return f(st)
}
@ -1153,7 +1113,7 @@ func processFilteredReleases(st *state.HelmState, converge func(st *state.HelmSt
func checkDuplicates(releases []state.ReleaseSpec) error {
type Key struct {
Namespace, Name, KubeContext string
TillerNamespace, Name, KubeContext string
}
releaseNameCounts := map[Key]int{}
@ -1165,8 +1125,8 @@ func checkDuplicates(releases []state.ReleaseSpec) error {
if c > 1 {
var msg string
if name.Namespace != "" {
msg += fmt.Sprintf(" in namespace %q", name.Namespace)
if name.TillerNamespace != "" {
msg += fmt.Sprintf(" in namespace %q", name.TillerNamespace)
}
if name.KubeContext != "" {
@ -1228,6 +1188,16 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
case a.fs.FileExistsAt(DefaultGotmplHelmfile):
defaultFile = DefaultGotmplHelmfile
// TODO: Remove this block when we remove v0 code
case !runtime.V1Mode && a.fs.FileExistsAt(DeprecatedHelmfile):
a.Logger.Warnf(
"warn: %s is being loaded: %s is deprecated in favor of %s. See https://github.com/roboll/helmfile/issues/25 for more information",
DeprecatedHelmfile,
DeprecatedHelmfile,
DefaultHelmfile,
)
defaultFile = DeprecatedHelmfile
}
switch {
@ -1240,7 +1210,7 @@ func (a *App) findDesiredStateFiles(specifiedPath string, opts LoadOpts) ([]stri
case defaultFile != "":
return []string{defaultFile}, nil
default:
return []string{}, fmt.Errorf("no state file found. It must be named %s/*.{yaml,yml,yaml.gotmpl,yml.gotmpl}, %s, or %s, otherwise specified with the --file flag or %s environment variable", DefaultHelmfileDirectory, DefaultHelmfile, DefaultGotmplHelmfile, envvar.FilePath)
return []string{}, fmt.Errorf("no state file found. It must be named %s/*.{yaml,yml,yaml.gotmpl,yml.gotmpl}, %s, or %s, otherwise specified with the --file flag", DefaultHelmfileDirectory, DefaultHelmfile, DefaultGotmplHelmfile)
}
}
@ -1282,6 +1252,7 @@ func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state.
selectedIds := map[string]state.ReleaseSpec{}
selectedCounts := map[string]int{}
for _, r := range selected {
r := r
id := state.ReleaseToID(&r)
selectedIds[id] = r
selectedCounts[id]++
@ -1295,6 +1266,7 @@ func (a *App) getSelectedReleases(r *Run, includeTransitiveNeeds bool) ([]state.
groupsByID := map[string][]*state.ReleaseSpec{}
for _, r := range allReleases {
r := r
groupsByID[state.ReleaseToID(&r)] = append(groupsByID[state.ReleaseToID(&r)], &r)
}
@ -1348,7 +1320,7 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
st := r.state
helm := r.helm
helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
selectedReleases, selectedAndNeededReleases, err := a.getSelectedReleases(r, c.IncludeTransitiveNeeds())
if err != nil {
@ -1385,21 +1357,16 @@ func (a *App) apply(r *Run, c ApplyConfigProvider) (bool, bool, []error) {
detailedExitCode := true
diffOpts := &state.DiffOpts{
Color: c.Color(),
NoColor: c.NoColor(),
Context: c.Context(),
Output: c.DiffOutput(),
Set: c.Set(),
SkipCleanup: c.SkipCleanup(),
SkipDiffOnInstall: c.SkipDiffOnInstall(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
DiffArgs: c.DiffArgs(),
PostRenderer: c.PostRenderer(),
PostRendererArgs: c.PostRendererArgs(),
SkipSchemaValidation: c.SkipSchemaValidation(),
SuppressOutputLineRegex: c.SuppressOutputLineRegex(),
TakeOwnership: c.TakeOwnership(),
Color: c.Color(),
NoColor: c.NoColor(),
Context: c.Context(),
Output: c.DiffOutput(),
Set: c.Set(),
SkipCleanup: c.RetainValuesFiles() || c.SkipCleanup(),
SkipDiffOnInstall: c.SkipDiffOnInstall(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
}
infoMsg, releasesToBeUpdated, releasesToBeDeleted, errs := r.diff(false, detailedExitCode, c, diffOpts)
@ -1502,21 +1469,14 @@ Do you really want to apply?
subst.Releases = rs
syncOpts := &state.SyncOpts{
Set: c.Set(),
SkipCleanup: c.SkipCleanup(),
SkipCRDs: c.SkipCRDs(),
Wait: c.Wait(),
WaitRetries: c.WaitRetries(),
WaitForJobs: c.WaitForJobs(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
PostRendererArgs: c.PostRendererArgs(),
SkipSchemaValidation: c.SkipSchemaValidation(),
SyncArgs: c.SyncArgs(),
HideNotes: c.HideNotes(),
TakeOwnership: c.TakeOwnership(),
SyncReleaseLabels: c.SyncReleaseLabels(),
Set: c.Set(),
SkipCleanup: c.RetainValuesFiles() || c.SkipCleanup(),
SkipCRDs: c.SkipCRDs(),
Wait: c.Wait(),
WaitForJobs: c.WaitForJobs(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
}
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), syncOpts)
}))
@ -1601,7 +1561,7 @@ Do you really want to delete?
`, strings.Join(names, "\n"))
interactive := c.Interactive()
if !interactive || interactive && r.askForConfirmation(msg) {
r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
if len(releasesToDelete) > 0 {
_, deletionErrs := withDAG(st, helm, a.Logger, state.PlanOptions{SelectedReleases: toDelete, Reverse: true, SkipNeeds: true}, a.WrapWithoutSelector(func(subst *state.HelmState, helm helmexec.Interface) []error {
@ -1626,25 +1586,20 @@ func (a *App) diff(r *Run, c DiffConfigProvider) (*string, bool, bool, []error)
ok, errs := a.withNeeds(r, c, true, func(st *state.HelmState) []error {
helm := r.helm
helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
var errs []error
helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
opts := &state.DiffOpts{
Context: c.Context(),
Output: c.DiffOutput(),
Color: c.Color(),
NoColor: c.NoColor(),
Set: c.Set(),
DiffArgs: c.DiffArgs(),
SkipDiffOnInstall: c.SkipDiffOnInstall(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
PostRendererArgs: c.PostRendererArgs(),
SkipSchemaValidation: c.SkipSchemaValidation(),
SuppressOutputLineRegex: c.SuppressOutputLineRegex(),
TakeOwnership: c.TakeOwnership(),
Context: c.Context(),
Output: c.DiffOutput(),
Color: c.Color(),
NoColor: c.NoColor(),
Set: c.Set(),
SkipDiffOnInstall: c.SkipDiffOnInstall(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
}
filtered := &Run{
@ -1667,7 +1622,7 @@ func (a *App) lint(r *Run, c LintConfigProvider) (bool, []error, []error) {
ok, errs := a.withNeeds(r, c, false, func(st *state.HelmState) []error {
helm := r.helm
args := GetArgs(c.Args(), st)
args := argparser.GetArgs(c.Args(), st)
// Reset the extra args if already set, not to break `helm fetch` by adding the args intended for `lint`
helm.SetExtraArgs()
@ -1717,9 +1672,10 @@ func (a *App) status(r *Run, c StatusesConfigProvider) (bool, []error) {
var toStatus []state.ReleaseSpec
for _, r := range selectedReleases {
if r.Desired() {
toStatus = append(toStatus, r)
if r.Installed != nil && !*r.Installed {
continue
}
toStatus = append(toStatus, r)
}
var errs []error
@ -1727,7 +1683,7 @@ func (a *App) status(r *Run, c StatusesConfigProvider) (bool, []error) {
// Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies
st.Releases = allReleases
args := GetArgs(c.Args(), st)
args := argparser.GetArgs(c.Args(), st)
// Reset the extra args if already set, not to break `helm fetch` by adding the args intended for `lint`
helm.SetExtraArgs()
@ -1799,7 +1755,7 @@ func (a *App) sync(r *Run, c SyncConfigProvider) (bool, []error) {
for _, r := range toSyncWithNeeds {
release := r
if _, deleted := releasesToDelete[state.ReleaseToID(&release)]; !deleted {
if r.Desired() {
if release.Installed == nil || *release.Installed {
toUpdate = append(toUpdate, release)
}
// TODO Emit error when the user opted to fail when the needed release is disabled,
@ -1860,7 +1816,7 @@ Do you really want to sync?
var errs []error
r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
// Traverse DAG of all the releases so that we don't suffer from false-positive missing dependencies
st.Releases = selectedAndNeededReleases
@ -1903,20 +1859,13 @@ Do you really want to sync?
subst.Releases = rs
opts := &state.SyncOpts{
Set: c.Set(),
SkipCRDs: c.SkipCRDs(),
Wait: c.Wait(),
WaitRetries: c.WaitRetries(),
WaitForJobs: c.WaitForJobs(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
PostRendererArgs: c.PostRendererArgs(),
SyncArgs: c.SyncArgs(),
HideNotes: c.HideNotes(),
TakeOwnership: c.TakeOwnership(),
SkipSchemaValidation: c.SkipSchemaValidation(),
SyncReleaseLabels: c.SyncReleaseLabels(),
Set: c.Set(),
SkipCRDs: c.SkipCRDs(),
Wait: c.Wait(),
WaitForJobs: c.WaitForJobs(),
ReuseValues: c.ReuseValues(),
ResetValues: c.ResetValues(),
PostRenderer: c.PostRenderer(),
}
return subst.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency(), opts)
}))
@ -1934,7 +1883,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
return a.withNeeds(r, c, false, func(st *state.HelmState) []error {
helm := r.helm
args := GetArgs(c.Args(), st)
args := argparser.GetArgs(c.Args(), st)
// Reset the extra args if already set, not to break `helm fetch` by adding the args intended for `lint`
helm.SetExtraArgs()
@ -1944,17 +1893,13 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
}
opts := &state.TemplateOpts{
Set: c.Set(),
IncludeCRDs: c.IncludeCRDs(),
NoHooks: c.NoHooks(),
OutputDirTemplate: c.OutputDirTemplate(),
SkipCleanup: c.SkipCleanup(),
SkipTests: c.SkipTests(),
PostRenderer: c.PostRenderer(),
PostRendererArgs: c.PostRendererArgs(),
KubeVersion: c.KubeVersion(),
ShowOnly: c.ShowOnly(),
SkipSchemaValidation: c.SkipSchemaValidation(),
Set: c.Set(),
IncludeCRDs: c.IncludeCRDs(),
OutputDirTemplate: c.OutputDirTemplate(),
SkipCleanup: c.SkipCleanup(),
SkipTests: c.SkipTests(),
PostRenderer: c.PostRenderer(),
KubeVersion: c.KubeVersion(),
}
return st.TemplateReleases(helm, c.OutputDir(), c.Values(), args, c.Concurrency(), c.Validate(), opts)
})
@ -2001,7 +1946,7 @@ func (a *App) withNeeds(r *Run, c DAGConfig, includeDisabled bool, f func(*state
for _, r := range selectedReleasesWithNeeds {
release := r
id := state.ReleaseToID(&release)
if !release.Desired() {
if release.Installed != nil && !*release.Installed {
releasesToUninstall[id] = release
} else {
toRender = append(toRender, release)
@ -2061,7 +2006,7 @@ func (a *App) test(r *Run, c TestConfigProvider) []error {
// with conditions and selectors
st.Releases = toTest
r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
return st.TestReleases(r.helm, cleanup, timeout, concurrency, state.Logs(c.Logs()))
}
@ -2086,9 +2031,10 @@ func (a *App) writeValues(r *Run, c WriteValuesConfigProvider) (bool, []error) {
for _, r := range toRender {
release := r
id := state.ReleaseToID(&release)
if release.Desired() {
releasesToWrite[id] = release
if release.Installed != nil && !*release.Installed {
continue
}
releasesToWrite[id] = release
}
var errs []error
@ -2265,18 +2211,3 @@ func (a *App) CleanCacheDir(c CacheConfigProvider) error {
return nil
}
func GetArgs(args string, state *state.HelmState) []string {
baseArgs := []string{}
stateArgs := []string{}
if len(args) > 0 {
baseArgs = argparser.CollectArgs(args)
}
if len(state.HelmDefaults.Args) > 0 {
stateArgs = argparser.CollectArgs(strings.Join(state.HelmDefaults.Args, " "))
}
state.HelmDefaults.Args = append(baseArgs, stateArgs...)
return state.HelmDefaults.Args
}

View File

@ -6,11 +6,11 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestApply_hooks(t *testing.T) {
@ -133,7 +133,10 @@ func TestApply_hooks(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
} else {
assertLogEqualsToSnapshot(t, bs.String())
}
@ -161,7 +164,7 @@ releases:
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -192,7 +195,7 @@ releases:
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -223,7 +226,7 @@ releases:
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -252,7 +255,7 @@ releases:
},
selectors: []string{"app=test"},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: nil,
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: nil,
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -299,8 +302,8 @@ releases:
{Name: "bar"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -346,7 +349,7 @@ releases:
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -396,7 +399,7 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 default
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -444,7 +447,7 @@ foo 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 default
{Name: "foo"},
},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
error: "",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result

View File

@ -6,12 +6,12 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestApply_3(t *testing.T) {
@ -134,7 +134,10 @@ func TestApply_3(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
} else {
assertLogEqualsToSnapshot(t, bs.String())
}
@ -146,7 +149,7 @@ func TestApply_3(t *testing.T) {
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -177,8 +180,8 @@ releases:
{Name: "my-release", Flags: []string{"--namespace", "default"}},
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^external-secrets$", Flags: listFlags("default", "")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -199,7 +202,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -234,8 +237,8 @@ releases:
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: nil,
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -250,7 +253,7 @@ releases:
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -278,9 +281,9 @@ releases:
selectors: []string{"app=test"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--namespace kube-system --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--namespacekube-system--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^kubernetes-external-secrets$", Flags: listFlags("kube-system", "")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -306,7 +309,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -334,9 +337,9 @@ releases:
selectors: []string{"app=test"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--namespace kube-system --reset-values --detailed-exitcode"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--namespacekube-system--detailed-exitcode--reset-values"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^external-secrets$", Flags: listFlags("default", "")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -359,7 +362,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -399,8 +402,8 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -415,7 +418,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -453,8 +456,8 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -464,7 +467,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
t.Run("bad --selector", func(t *testing.T) {
check(t, testcase{
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -494,6 +497,72 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"
`,
})
})
}

View File

@ -6,12 +6,12 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestApply_2(t *testing.T) {
@ -134,7 +134,10 @@ func TestApply_2(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
} else {
assertLogEqualsToSnapshot(t, bs.String())
}
@ -146,7 +149,7 @@ func TestApply_2(t *testing.T) {
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -177,8 +180,8 @@ releases:
{Name: "my-release", Flags: []string{"--kube-context", "default", "--namespace", "default"}},
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^external-secrets$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -199,7 +202,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -234,8 +237,8 @@ releases:
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: nil,
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -250,7 +253,7 @@ releases:
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -278,9 +281,9 @@ releases:
selectors: []string{"app=test"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace kube-system --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacekube-system--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^kubernetes-external-secrets$", Flags: listFlags("kube-system", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -306,7 +309,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -334,9 +337,9 @@ releases:
selectors: []string{"app=test"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace kube-system --reset-values --detailed-exitcode"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "kubernetes-external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacekube-system--detailed-exitcode--reset-values"}: nil,
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^external-secrets$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -359,7 +362,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -399,8 +402,8 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -415,7 +418,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -453,8 +456,8 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
`,
},
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
@ -470,7 +473,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -494,9 +497,9 @@ releases:
selectors: []string{"name=serviceA"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "serviceA", Chart: "my/chart", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "serviceB", Chart: "my/chart", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "serviceC", Chart: "my/chart", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "serviceA", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "serviceB", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "serviceC", Chart: "my/chart", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^serviceA$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -517,7 +520,7 @@ serviceC 4 Fri Nov 1 08:40:07 2019 DEPLOYED chart-3.1.0 3.1.0 def
t.Run("bad --selector", func(t *testing.T) {
check(t, testcase{
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -547,6 +550,72 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"
`,
})
})
@ -575,7 +644,7 @@ releases:
selectors: []string{"index=1"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -641,7 +710,7 @@ releases:
selectors: []string{"name=foo"},
upgraded: []exectest.Release{},
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("default", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE

View File

@ -280,8 +280,8 @@ releases:
},
selectors: []string{"app=test"},
diffed: []exectest.Release{
{Name: "external-secrets", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values", "--no-hooks"}},
{Name: "my-release", Flags: []string{"--kube-context", "default", "--namespace", "default", "--reset-values", "--no-hooks"}},
{Name: "external-secrets", Flags: []string{"--kube-context", "default", "--namespace", "default", "--no-hooks", "--reset-values"}},
{Name: "my-release", Flags: []string{"--kube-context", "default", "--namespace", "default", "--no-hooks", "--reset-values"}},
},
})
})
@ -415,28 +415,4 @@ 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"}},
},
})
})
}

View File

@ -1,51 +0,0 @@
package app
import (
goContext "context"
"testing"
"github.com/stretchr/testify/require"
"github.com/helmfile/helmfile/pkg/state"
)
// TestGetHelmWithEmptyDefaultHelmBinary tests that getHelm properly defaults to "helm"
// when st.DefaultHelmBinary is empty. This addresses the issue where base files with
// environment secrets would fail with "exec: no command" error.
//
// Background: When a base file has environment secrets but doesn't specify helmBinary,
// the state.DefaultHelmBinary would be empty, causing helmexec.New to be called with
// an empty string, which results in "error determining helm version: exec: no command".
//
// The fix in app.getHelm() ensures that when st.DefaultHelmBinary is empty, it defaults
// to state.DefaultHelmBinary ("helm").
func TestGetHelmWithEmptyDefaultHelmBinary(t *testing.T) {
// Test that app.getHelm() handles empty DefaultHelmBinary correctly by applying a default
st := &state.HelmState{
ReleaseSetSpec: state.ReleaseSetSpec{
DefaultHelmBinary: "", // Empty, as would be the case for base files
},
}
logger := newAppTestLogger()
app := &App{
OverrideHelmBinary: "",
OverrideKubeContext: "",
Logger: logger,
Env: "default",
ctx: goContext.Background(),
}
// This should NOT fail because app.getHelm() defaults empty DefaultHelmBinary to "helm"
helm, err := app.getHelm(st)
// Verify that no error occurred - the fix in app.getHelm() prevents the "exec: no command" error
require.NoError(t, err, "getHelm should not fail when DefaultHelmBinary is empty (fix should apply default)")
// Verify that a valid helm execer was returned
require.NotNil(t, helm, "getHelm should return a valid helm execer")
// Verify that the helm version is accessible (confirms the helm binary is valid)
version := helm.GetVersion()
require.NotNil(t, version, "helm version should be accessible")
}

View File

@ -9,13 +9,13 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestLint(t *testing.T) {
@ -167,7 +167,10 @@ releases:
}
}
assert.Equal(t, wantLog, gotLog)
diff, exists := testhelper.Diff(wantLog, gotLog, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
}
t.Run("fail on unselected need by default", func(t *testing.T) {

View File

@ -41,7 +41,7 @@ func testListWithEnvironment(t *testing.T, cfg configImpl) {
environments:
development: {}
shared: {}
---
releases:
- name: logging
chart: incubator/raw
@ -90,7 +90,7 @@ releases:
environments:
test: {}
shared: {}
---
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
@ -159,17 +159,17 @@ releases:
t.Run("default environment includes all releases", func(t *testing.T) {
check(t, testcase{
environment: "default",
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true chart:raw,name:logging,namespace:kube-system incubator/raw
kubernetes-external-secrets kube-system true true chart:raw,name:kubernetes-external-secrets,namespace:kube-system incubator/raw
external-secrets default true true app:test,chart:raw,name:external-secrets,namespace:default incubator/raw
my-release default true true app:test,chart:raw,name:my-release,namespace:default incubator/raw
disabled kube-system true false chart:raw,name:disabled,namespace:kube-system incubator/raw
test2 true true chart:raw,name:test2,namespace: incubator/raw
test3 true true chart:raw,name:test3,namespace: incubator/raw
cache my-app true true app:test,chart:redis,name:cache,namespace:my-app bitnami/redis 17.0.7
database my-app true true chart:postgres,name:database,namespace:my-app bitnami/postgres 11.6.22
global kube-system true true chart:raw,name:global,namespace:kube-system incubator/raw
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true incubator/raw
kubernetes-external-secrets kube-system true true incubator/raw
external-secrets default true true app:test incubator/raw
my-release default true true app:test incubator/raw
disabled kube-system true false incubator/raw
test2 true true incubator/raw
test3 true true incubator/raw
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
global kube-system true true incubator/raw
`,
}, cfg)
})
@ -195,9 +195,9 @@ my-release default true true app:test,chart:raw,name:my-release,
t.Run("filters releases for environment used in one file only", func(t *testing.T) {
check(t, testcase{
environment: "test",
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
cache my-app true true app:test,chart:redis,name:cache,namespace:my-app bitnami/redis 17.0.7
database my-app true true chart:postgres,name:database,namespace:my-app bitnami/postgres 11.6.22
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
`,
}, cfg)
})
@ -206,16 +206,16 @@ database my-app true true chart:postgres,name:database,namespace:my-a
check(t, testcase{
environment: "shared",
// 'global' release has no environments, so is still excluded
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true chart:raw,name:logging,namespace:kube-system incubator/raw
kubernetes-external-secrets kube-system true true chart:raw,name:kubernetes-external-secrets,namespace:kube-system incubator/raw
external-secrets default true true app:test,chart:raw,name:external-secrets,namespace:default incubator/raw
my-release default true true app:test,chart:raw,name:my-release,namespace:default incubator/raw
disabled kube-system true false chart:raw,name:disabled,namespace:kube-system incubator/raw
test2 true true chart:raw,name:test2,namespace: incubator/raw
test3 true true chart:raw,name:test3,namespace: incubator/raw
cache my-app true true app:test,chart:redis,name:cache,namespace:my-app bitnami/redis 17.0.7
database my-app true true chart:postgres,name:database,namespace:my-app bitnami/postgres 11.6.22
expected: `NAME NAMESPACE ENABLED INSTALLED LABELS CHART VERSION
logging kube-system true true incubator/raw
kubernetes-external-secrets kube-system true true incubator/raw
external-secrets default true true app:test incubator/raw
my-release default true true app:test incubator/raw
disabled kube-system true false incubator/raw
test2 true true incubator/raw
test3 true true incubator/raw
cache my-app true true app:test bitnami/redis 17.0.7
database my-app true true bitnami/postgres 11.6.22
`,
}, cfg)
})
@ -240,7 +240,6 @@ environments:
values:
- myrelease2:
enabled: false
---
releases:
- name: myrelease1
chart: mychart1
@ -285,7 +284,7 @@ releases:
})
assert.NoError(t, err)
expected := `[{"name":"myrelease1","namespace":"testNamespace","enabled":true,"installed":false,"labels":"chart:mychart1,id:myrelease1,name:myrelease1,namespace:testNamespace","chart":"mychart1","version":""},{"name":"myrelease2","namespace":"testNamespace","enabled":false,"installed":true,"labels":"chart:mychart1,name:myrelease2,namespace:testNamespace","chart":"mychart1","version":""},{"name":"myrelease3","namespace":"testNamespace","enabled":true,"installed":true,"labels":"chart:mychart1,name:myrelease3,namespace:testNamespace","chart":"mychart1","version":""},{"name":"myrelease4","namespace":"testNamespace","enabled":true,"installed":true,"labels":"chart:mychart1,id:myrelease1,name:myrelease4,namespace:testNamespace","chart":"mychart1","version":""}]
expected := `[{"name":"myrelease1","namespace":"testNamespace","enabled":true,"installed":false,"labels":"id:myrelease1","chart":"mychart1","version":""},{"name":"myrelease2","namespace":"testNamespace","enabled":false,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease3","namespace":"testNamespace","enabled":true,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease4","namespace":"testNamespace","enabled":true,"installed":true,"labels":"id:myrelease1","chart":"mychart1","version":""}]
`
assert.Equal(t, expected, out)
}

View File

@ -6,12 +6,12 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestSync(t *testing.T) {
@ -132,7 +132,10 @@ func TestSync(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
}
}
@ -142,7 +145,7 @@ func TestSync(t *testing.T) {
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -182,6 +185,89 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
processing 2 groups of releases in this order:
GROUP RELEASES
1 default/default/external-secrets
2 default/default/my-release
processing releases in group 1/2: default/default/external-secrets
processing releases in group 2/2: default/default/my-release
UPDATED RELEASES:
NAME CHART VERSION DURATION
external-secrets incubator/raw 3.1.0 0s
my-release incubator/raw 3.1.0 0s
changing working directory back to "/path/to"
`,
})
})
@ -191,7 +277,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
skipNeeds: true,
},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -238,6 +324,111 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23: hooks:
24: - name: my-release
25: events:
26: - postsync
27: showlogs: true
28: command: sleep
29: args: [5s]
30:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23: hooks:
24: - name: my-release
25: events:
26: - postsync
27: showlogs: true
28: command: sleep
29: args: [5s]
30:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
processing 2 groups of releases in this order:
GROUP RELEASES
1 default/default/external-secrets
2 default/default/my-release
processing releases in group 1/2: default/default/external-secrets
processing releases in group 2/2: default/default/my-release
hook[my-release]: stateFilePath=helmfile.yaml, basePath=.
hook[my-release]: triggered by event "postsync"
hook[my-release]:
hook[postsync] logs |
UPDATED RELEASES:
NAME CHART VERSION DURATION
external-secrets incubator/raw 3.1.0 0s
my-release incubator/raw 3.1.0 5s
changing working directory back to "/path/to"
`,
})
})
@ -249,7 +440,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -289,6 +480,93 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
kubernetes-external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
processing 3 groups of releases in this order:
GROUP RELEASES
1 default/kube-system/kubernetes-external-secrets
2 default/default/external-secrets
3 default/default/my-release
processing releases in group 1/3: default/kube-system/kubernetes-external-secrets
processing releases in group 2/3: default/default/external-secrets
processing releases in group 3/3: default/default/my-release
UPDATED RELEASES:
NAME CHART VERSION DURATION
kubernetes-external-secrets incubator/raw 3.1.0 0s
external-secrets incubator/raw 3.1.0 0s
my-release incubator/raw 3.1.0 0s
changing working directory back to "/path/to"
`,
})
})
@ -300,7 +578,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -336,6 +614,85 @@ serviceA 4 Fri Nov 1 08:40:07 2019 DEPLOYED chart-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: serviceA
5: chart: my/chart
6: needs:
7: - serviceB
8:
9: - name: serviceB
10: chart: my/chart
11: needs:
12: - serviceC
13:
14: - name: serviceC
15: chart: my/chart
16:
17: - name: serviceD
18: chart: my/chart
19:
merged environment: &{default map[] map[]}
3 release(s) matching name=serviceA found in helmfile.yaml
Affected releases are:
serviceA (my/chart) UPDATED
serviceB (my/chart) UPDATED
serviceC (my/chart) UPDATED
processing 3 groups of releases in this order:
GROUP RELEASES
1 default//serviceC
2 default//serviceB
3 default//serviceA
processing releases in group 1/3: default//serviceC
processing releases in group 2/3: default//serviceB
processing releases in group 3/3: default//serviceA
UPDATED RELEASES:
NAME CHART VERSION DURATION
serviceC my/chart 3.1.0 0s
serviceB my/chart 3.1.0 0s
serviceA my/chart 3.1.0 0s
changing working directory back to "/path/to"
`,
})
})
@ -348,7 +705,7 @@ serviceA 4 Fri Nov 1 08:40:07 2019 DEPLOYED chart-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -389,6 +746,102 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7: installed: false
8:
9: - name: external-secrets
10: chart: incubator/raw
11: namespace: default
12: labels:
13: app: test
14: needs:
15: - kube-system/kubernetes-external-secrets
16:
17: - name: my-release
18: chart: incubator/raw
19: namespace: default
20: labels:
21: app: test
22: needs:
23: - default/external-secrets
24:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7: installed: false
8:
9: - name: external-secrets
10: chart: incubator/raw
11: namespace: default
12: labels:
13: app: test
14: needs:
15: - kube-system/kubernetes-external-secrets
16:
17: - name: my-release
18: chart: incubator/raw
19: namespace: default
20: labels:
21: app: test
22: needs:
23: - default/external-secrets
24:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
kubernetes-external-secrets (incubator/raw) DELETED
my-release (incubator/raw) UPDATED
processing 1 groups of releases in this order:
GROUP RELEASES
1 default/kube-system/kubernetes-external-secrets
processing releases in group 1/1: default/kube-system/kubernetes-external-secrets
processing 2 groups of releases in this order:
GROUP RELEASES
1 default/default/external-secrets
2 default/default/my-release
processing releases in group 1/2: default/default/external-secrets
processing releases in group 2/2: default/default/my-release
UPDATED RELEASES:
NAME CHART VERSION DURATION
external-secrets incubator/raw 3.1.0 0s
my-release incubator/raw 3.1.0 0s
DELETED RELEASES:
NAME DURATION
kubernetes-external-secrets 0s
changing working directory back to "/path/to"
`,
})
})
@ -401,7 +854,7 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
error: ``,
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -441,13 +894,98 @@ my-release 4 Fri Nov 1 08:40:07 2019 DEPLOYED raw-3.1.0 3.1.0 def
},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7: installed: false
8:
9: - name: external-secrets
10: chart: incubator/raw
11: namespace: default
12: labels:
13: app: test
14: needs:
15: - kube-system/kubernetes-external-secrets
16:
17: - name: my-release
18: chart: incubator/raw
19: namespace: default
20: labels:
21: app: test
22: needs:
23: - default/external-secrets
24:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7: installed: false
8:
9: - name: external-secrets
10: chart: incubator/raw
11: namespace: default
12: labels:
13: app: test
14: needs:
15: - kube-system/kubernetes-external-secrets
16:
17: - name: my-release
18: chart: incubator/raw
19: namespace: default
20: labels:
21: app: test
22: needs:
23: - default/external-secrets
24:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
Affected releases are:
external-secrets (incubator/raw) UPDATED
my-release (incubator/raw) UPDATED
processing 2 groups of releases in this order:
GROUP RELEASES
1 default/default/external-secrets
2 default/default/my-release
processing releases in group 1/2: default/default/external-secrets
processing releases in group 2/2: default/default/my-release
UPDATED RELEASES:
NAME CHART VERSION DURATION
external-secrets incubator/raw 3.1.0 0s
my-release incubator/raw 3.1.0 0s
changing working directory back to "/path/to"
`,
})
})
t.Run("bad --selector", func(t *testing.T) {
check(t, testcase{
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -477,6 +1015,72 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"
`,
})
})
}

View File

@ -9,7 +9,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
@ -17,6 +16,7 @@ import (
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/runtime"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestTemplate(t *testing.T) {
@ -24,8 +24,6 @@ func TestTemplate(t *testing.T) {
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
noHooks bool
showOnly []string
}
type testcase struct {
@ -133,8 +131,6 @@ releases:
skipNeeds: tc.fields.skipNeeds,
includeNeeds: tc.fields.includeNeeds,
includeTransitiveNeeds: tc.fields.includeTransitiveNeeds,
showOnly: tc.fields.showOnly,
noHooks: tc.fields.noHooks,
})
var gotErr string
@ -172,7 +168,10 @@ releases:
}
}
assert.Equal(t, wantLog, gotLog)
diff, exists := testhelper.Diff(wantLog, gotLog, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
}
t.Run("fail on unselected need by default", func(t *testing.T) {
@ -238,7 +237,7 @@ releases:
},
selectors: []string{"name=test2"},
templated: []exectest.Release{
{Name: "test2"},
{Name: "test2", Flags: []string{}},
},
})
})
@ -251,8 +250,8 @@ releases:
},
selectors: []string{"name=test3"},
templated: []exectest.Release{
{Name: "test2"},
{Name: "test3"},
{Name: "test2", Flags: []string{}},
{Name: "test3", Flags: []string{}},
},
})
})
@ -266,8 +265,8 @@ releases:
},
selectors: []string{"name=test3"},
templated: []exectest.Release{
{Name: "test2"},
{Name: "test3"},
{Name: "test2", Flags: []string{}},
{Name: "test3", Flags: []string{}},
},
})
})
@ -281,7 +280,7 @@ releases:
},
selectors: []string{"name=test2"},
templated: []exectest.Release{
{Name: "test2"},
{Name: "test2", Flags: []string{}},
},
})
})
@ -295,22 +294,8 @@ releases:
},
selectors: []string{"name=test3"},
templated: []exectest.Release{
{Name: "test2"},
{Name: "test3"},
},
})
})
t.Run("no-hooks", func(t *testing.T) {
check(t, testcase{
fields: fields{
skipNeeds: true,
noHooks: true,
},
selectors: []string{"app=test"},
templated: []exectest.Release{
{Name: "external-secrets", Flags: []string{"--namespace", "default", "--no-hooks"}},
{Name: "my-release", Flags: []string{"--namespace", "default", "--no-hooks"}},
{Name: "test2", Flags: []string{}},
{Name: "test3", Flags: []string{}},
},
})
})
@ -322,34 +307,22 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
})
})
t.Run("show-only", func(t *testing.T) {
check(t, testcase{
fields: fields{
showOnly: []string{"templates/resources.yaml"},
},
selectors: []string{"name=logging"},
templated: []exectest.Release{
{Name: "logging", Flags: []string{"--show-only", "templates/resources.yaml", "--namespace", "kube-system"}},
},
})
})
}
func TestTemplate_StrictParsing(t *testing.T) {
type testcase struct {
GoYamlV3 bool
ns string
error string
goccyGoYaml bool
ns string
error string
}
check := func(t *testing.T, tc testcase) {
t.Helper()
v := runtime.GoYamlV3
runtime.GoYamlV3 = tc.GoYamlV3
v := runtime.GoccyGoYaml
runtime.GoccyGoYaml = tc.goccyGoYaml
t.Cleanup(func() {
runtime.GoYamlV3 = v
runtime.GoccyGoYaml = v
})
var helm = &exectest.Helm{
@ -411,18 +384,22 @@ releases:
})
}
t.Run("fail due to unknown field with go.yaml.in/yaml/v3", func(t *testing.T) {
t.Run("fail due to unknown field with goccy/go-yaml", func(t *testing.T) {
check(t, testcase{
GoYamlV3: true,
error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: yaml: unmarshal errors:
line 4: field foobar not found in type state.ReleaseSpec`,
goccyGoYaml: true,
error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1: [4:3] unknown field "foobar"
2 | releases:
3 | - name: app1
> 4 | foobar: FOOBAR
^
5 | chart: incubator/raw`,
})
})
t.Run("fail due to unknown field with go.yaml.in/yaml/v2", func(t *testing.T) {
t.Run("fail due to unknown field with gopkg.in/yaml.v2", func(t *testing.T) {
check(t, testcase{
GoYamlV3: false,
error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1. Started seeing this since Helmfile v1? Add the .gotmpl file extension: yaml: unmarshal errors:
goccyGoYaml: false,
error: `in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1: yaml: unmarshal errors:
line 4: field foobar not found in type state.ReleaseSpec`,
})
})

File diff suppressed because it is too large Load Diff

View File

@ -24,10 +24,9 @@ func AskForConfirmation(s string) bool {
response = strings.ToLower(strings.TrimSpace(response))
switch response {
case "y", "yes":
if response == "y" || response == "yes" {
return true
case "n", "no":
} else if response == "n" || response == "no" {
return false
}
}

View File

@ -5,12 +5,10 @@ import "go.uber.org/zap"
type ConfigProvider interface {
Args() string
HelmBinary() string
KustomizeBinary() string
EnableLiveOutput() bool
StripArgsValuesOnExitError() bool
DisableForceUpdate() bool
SkipDeps() bool
SkipRefresh() bool
FileOrDir() string
KubeContext() string
@ -19,12 +17,20 @@ type ConfigProvider interface {
Selectors() []string
StateValuesSet() map[string]any
StateValuesFiles() []string
Kubeconfig() string
Env() string
loggingConfig
}
// TODO: Remove this function once Helmfile v0.x
type DeprecatedChartsConfigProvider interface {
Values() []string
concurrencyConfig
loggingConfig
IncludeTransitiveNeeds() bool
}
type DepsConfigProvider interface {
Args() string
SkipRepos() bool
@ -41,20 +47,13 @@ type ReposConfigProvider interface {
type ApplyConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
Cascade() string
HideNotes() bool
TakeOwnership() bool
SuppressOutputLineRegex() []string
Values() []string
Set() []string
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
Wait() bool
WaitRetries() int
WaitForJobs() bool
IncludeTests() bool
@ -73,15 +72,13 @@ type ApplyConfigProvider interface {
Context() int
DiffOutput() string
// TODO: Remove this function once Helmfile v0.x
RetainValuesFiles() bool
Validate() bool
SkipCleanup() bool
SkipDiffOnInstall() bool
DiffArgs() string
SyncArgs() string
SyncReleaseLabels() bool
DAGConfig
concurrencyConfig
@ -93,30 +90,20 @@ type ApplyConfigProvider interface {
type SyncConfigProvider interface {
Args() string
PostRenderer() string
SkipSchemaValidation() bool
PostRendererArgs() []string
HideNotes() bool
TakeOwnership() bool
Cascade() string
Values() []string
Set() []string
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
Wait() bool
WaitRetries() int
WaitForJobs() bool
SyncArgs() string
Validate() bool
SkipNeeds() bool
IncludeNeeds() bool
IncludeTransitiveNeeds() bool
SyncReleaseLabels() bool
DAGConfig
concurrencyConfig
@ -128,16 +115,12 @@ type SyncConfigProvider interface {
type DiffConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
SuppressOutputLineRegex() []string
Values() []string
Set() []string
Validate() bool
SkipCRDs() bool
SkipDeps() bool
SkipRefresh() bool
IncludeTests() bool
@ -147,7 +130,6 @@ type DiffConfigProvider interface {
NoHooks() bool
SuppressDiff() bool
SkipDiffOnInstall() bool
DiffArgs() string
DAGConfig
@ -157,21 +139,31 @@ type DiffConfigProvider interface {
NoColor() bool
Context() int
DiffOutput() string
TakeOwnership() bool
concurrencyConfig
valuesControlMode
}
// TODO: Remove this function once Helmfile v0.x
type DeleteConfigProvider interface {
Args() string
Cascade() string
Purge() bool
SkipDeps() bool
SkipCharts() bool
interactive
loggingConfig
concurrencyConfig
}
type DestroyConfigProvider interface {
Args() string
Cascade() string
SkipDeps() bool
SkipRefresh() bool
SkipCharts() bool
DeleteWait() bool
DeleteTimeout() int
interactive
loggingConfig
@ -182,7 +174,6 @@ type TestConfigProvider interface {
Args() string
SkipDeps() bool
SkipRefresh() bool
Timeout() int
Cleanup() bool
Logs() bool
@ -196,7 +187,6 @@ type LintConfigProvider interface {
Values() []string
Set() []string
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
DAGConfig
@ -206,7 +196,6 @@ type LintConfigProvider interface {
type FetchConfigProvider interface {
SkipDeps() bool
SkipRefresh() bool
OutputDir() string
OutputDirTemplate() string
@ -216,22 +205,17 @@ type FetchConfigProvider interface {
type TemplateConfigProvider interface {
Args() string
PostRenderer() string
PostRendererArgs() []string
SkipSchemaValidation() bool
Values() []string
Set() []string
OutputDirTemplate() string
Validate() bool
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
SkipTests() bool
OutputDir() string
IncludeCRDs() bool
NoHooks() bool
KubeVersion() string
ShowOnly() []string
DAGConfig
@ -249,7 +233,6 @@ type WriteValuesConfigProvider interface {
Set() []string
OutputFileTemplate() string
SkipDeps() bool
SkipRefresh() bool
SkipCleanup() bool
IncludeTransitiveNeeds() bool
@ -266,8 +249,6 @@ type StateConfigProvider interface {
EmbedValues() bool
}
type DAGConfigProvider any
type concurrencyConfig interface {
Concurrency() int
}

View File

@ -8,7 +8,10 @@ import (
)
const (
DefaultHelmfile = "helmfile.yaml"
DefaultHelmfile = "helmfile.yaml"
// TODO: Remove this function once Helmfile v0.x
DeprecatedHelmfile = "charts.yaml"
DefaultHelmfileDirectory = "helmfile.d"
ExperimentalSelectorExplicit = "explicit-selector-inheritance" // value to remove default selector inheritance to sub-helmfiles and use the explicit one
)

View File

@ -1,147 +0,0 @@
package app
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/testhelper"
"github.com/helmfile/helmfile/pkg/testutil"
)
func testDAG(t *testing.T, cfg configImpl) {
type testcase struct {
environment string
ns string
error string
selectors []string
expected string
}
check := func(t *testing.T, tc testcase, cfg configImpl) {
t.Helper()
bs := runWithLogCapture(t, "debug", func(t *testing.T, logger *zap.SugaredLogger) {
t.Helper()
valsRuntime, err := vals.New(vals.Options{CacheSize: 32})
if err != nil {
t.Errorf("unexpected error creating vals runtime: %v", err)
}
files := map[string]string{
"/path/to/helmfile.yaml": `
environments:
development: {}
shared: {}
---
releases:
- name: logging
chart: incubator/raw
namespace: kube-system
- name: kubernetes-external-secrets
chart: incubator/raw
namespace: kube-system
needs:
- kube-system/logging
- name: external-secrets
chart: incubator/raw
namespace: default
labels:
app: test
needs:
- kube-system/kubernetes-external-secrets
- name: my-release
chart: incubator/raw
namespace: default
labels:
app: test
needs:
- default/external-secrets
# Disabled releases are treated as missing
- name: disabled
chart: incubator/raw
namespace: kube-system
installed: false
- name: test2
chart: incubator/raw
needs:
- kube-system/disabled
- name: test3
chart: incubator/raw
needs:
- test2
- name: test4
chart: incubator/raw
needs:
- test2
- test3
`,
}
app := appWithFs(&App{
OverrideHelmBinary: DefaultHelmBinary,
fs: ffs.DefaultFileSystem(),
OverrideKubeContext: "default",
Env: tc.environment,
Logger: logger,
valsRuntime: valsRuntime,
}, files)
expectNoCallsToHelm(app)
if tc.ns != "" {
app.Namespace = tc.ns
}
if tc.selectors != nil {
app.Selectors = tc.selectors
}
var dagErr error
out, err := testutil.CaptureStdout(func() {
dagErr = app.PrintDAGState(cfg)
})
assert.NoError(t, err)
var gotErr string
if dagErr != nil {
gotErr = dagErr.Error()
}
if d := cmp.Diff(tc.error, gotErr); d != "" {
t.Fatalf("unexpected error: want (-), got (+): %s", d)
}
assert.Equal(t, tc.expected, out)
})
testhelper.RequireLog(t, "dag_test", bs)
}
t.Run("DAG lists dependencies in order", func(t *testing.T) {
check(t, testcase{
environment: "default",
expected: `GROUP RELEASE DEPENDENCIES
1 default/kube-system/logging
1 default/kube-system/disabled
2 default/kube-system/kubernetes-external-secrets default/kube-system/logging
2 default//test2 default/kube-system/disabled
3 default/default/external-secrets default/kube-system/kubernetes-external-secrets
3 default//test3 default//test2
4 default/default/my-release default/default/external-secrets
4 default//test4 default//test2, default//test3
`,
}, cfg)
})
}
func TestDAG(t *testing.T) {
t.Run("DAG", func(t *testing.T) {
testDAG(t, configImpl{})
})
}

View File

@ -5,37 +5,34 @@ import (
"errors"
"fmt"
"path/filepath"
"slices"
"dario.cat/mergo"
"github.com/helmfile/vals"
"github.com/imdario/mergo"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/environment"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/policy"
"github.com/helmfile/helmfile/pkg/remote"
"github.com/helmfile/helmfile/pkg/runtime"
"github.com/helmfile/helmfile/pkg/state"
)
const (
DefaultHelmBinary = state.DefaultHelmBinary
DefaultKustomizeBinary = state.DefaultKustomizeBinary
DefaultHelmBinary = state.DefaultHelmBinary
)
type desiredStateLoader struct {
overrideKubeContext string
overrideHelmBinary string
overrideKustomizeBinary string
enableLiveOutput bool
overrideKubeContext string
overrideHelmBinary string
enableLiveOutput bool
env string
namespace string
chart string
fs *filesystem.FileSystem
getHelm func(*state.HelmState) (helmexec.Interface, error)
getHelm func(*state.HelmState) helmexec.Interface
remote *remote.Remote
logger *zap.SugaredLogger
@ -154,7 +151,7 @@ func (ld *desiredStateLoader) loadFileWithOverrides(inheritedEnv, overrodeEnv *e
}
func (a *desiredStateLoader) underlying() *state.StateCreator {
c := state.NewCreator(a.logger, a.fs, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.overrideKustomizeBinary, a.remote, a.enableLiveOutput, a.lockFilePath)
c := state.NewCreator(a.logger, a.fs, a.valsRuntime, a.getHelm, a.overrideHelmBinary, a.remote, a.enableLiveOutput, a.lockFilePath)
c.LoadFile = a.loadFile
return c
}
@ -162,14 +159,21 @@ func (a *desiredStateLoader) underlying() *state.StateCreator {
func (a *desiredStateLoader) rawLoad(yaml []byte, baseDir, file string, evaluateBases bool, env, overrodeEnv *environment.Environment) (*state.HelmState, error) {
var st *state.HelmState
var err error
merged, err := env.Merge(overrodeEnv)
if err != nil {
return nil, err
}
if runtime.V1Mode {
st, err = a.underlying().ParseAndLoad(yaml, baseDir, file, a.env, false, evaluateBases, env, overrodeEnv)
if err != nil {
return nil, err
}
} else {
merged, err := env.Merge(overrodeEnv)
if err != nil {
return nil, err
}
st, err = a.underlying().ParseAndLoad(yaml, baseDir, file, a.env, false, evaluateBases, merged, nil)
if err != nil {
return nil, err
st, err = a.underlying().ParseAndLoad(yaml, baseDir, file, a.env, false, evaluateBases, merged, nil)
if err != nil {
return nil, err
}
}
helmfiles, err := st.ExpandedHelmfiles()
if err != nil {
@ -183,16 +187,8 @@ func (a *desiredStateLoader) rawLoad(yaml []byte, baseDir, file string, evaluate
func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, baseDir, filename string, content []byte, evaluateBases bool) (*state.HelmState, error) {
// Allows part-splitting to work with CLRF-ed content
normalizedContent := bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n"))
isStrict, err := policy.Checker(filename, normalizedContent)
if err != nil {
if isStrict {
return nil, err
}
ld.logger.Warnf("WARNING: %v", err)
}
parts := bytes.Split(normalizedContent, []byte("\n---\n"))
hasEnv := env != nil || overrodeEnv != nil
var finalState *state.HelmState
for i, part := range parts {
@ -200,7 +196,7 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba
var rawContent []byte
if filepath.Ext(filename) == ".gotmpl" {
if filepath.Ext(filename) == ".gotmpl" || !runtime.V1Mode {
var yamlBuf *bytes.Buffer
var err error
@ -242,62 +238,23 @@ func (ld *desiredStateLoader) load(env, overrodeEnv *environment.Environment, ba
finalState.RenderedValues = currentState.RenderedValues
}
if len(finalState.HelmDefaults.PostRendererArgs) > 0 {
for i := range finalState.Releases {
if len(finalState.Releases[i].PostRendererArgs) == 0 {
finalState.Releases[i].PostRendererArgs = finalState.HelmDefaults.PostRendererArgs
}
}
finalState.HelmDefaults.PostRendererArgs = nil
}
env = &finalState.Env
ld.logger.Debugf("merged environment: %v", env)
if len(finalState.Environments) == 0 {
continue
}
// At this point, we are sure that the env has been
// read from the vanilla or rendered YAML document.
// We can now check if the env is defined in it and fail accordingly.
// See https://github.com/helmfile/helmfile/issues/913
// We defer the missing env detection and failure until
// all the helmfile parts are loaded and merged.
// Otherwise, any single helmfile part missing the env would fail the whole helmfile run.
// That's problematic, because each helmfile part is supposed to be incomplete, and
// they become complete only after merging all the parts.
// See https://github.com/helmfile/helmfile/issues/807 for the rationale of this.
if _, ok := finalState.Environments[env.Name]; evaluateBases && env.Name != state.DefaultEnv && !ok {
return nil, &state.StateLoadError{
Msg: fmt.Sprintf("failed to read %s", finalState.FilePath),
Cause: &state.UndefinedEnvError{Env: env.Name},
}
}
}
// If environments are not defined in the helmfile at all although the env is specified,
// it's a missing env situation. Let's fail.
if len(finalState.Environments) == 0 && evaluateBases && !hasEnv && env.Name != state.DefaultEnv {
// We defer the missing env detection and failure until
// all the helmfile parts are loaded and merged.
// Otherwise, any single helmfile part missing the env would fail the whole helmfile run.
// That's problematic, because each helmfile part is supposed to be incomplete, and
// they become complete only after merging all the parts.
// See https://github.com/helmfile/helmfile/issues/807 for the rationale of this.
if _, ok := finalState.Environments[env.Name]; evaluateBases && env.Name != state.DefaultEnv && !ok {
return nil, &state.StateLoadError{
Msg: fmt.Sprintf("failed to read %s", finalState.FilePath),
Cause: &state.UndefinedEnvError{Env: env.Name},
}
}
// 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
}

View File

@ -5,28 +5,26 @@ import (
"testing"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
func TestDestroy_2(t *testing.T) {
type testcase struct {
ns string
concurrency int
error string
files map[string]string
selectors []string
lists map[exectest.ListKey]string
diffs map[exectest.DiffKey]error
upgraded []exectest.Release
deleted []exectest.Release
log string
deleteWait bool
deleteTimeout int
ns string
concurrency int
error string
files map[string]string
selectors []string
lists map[exectest.ListKey]string
diffs map[exectest.DiffKey]error
upgraded []exectest.Release
deleted []exectest.Release
log string
}
check := func(t *testing.T, tc testcase) {
@ -79,8 +77,6 @@ func TestDestroy_2(t *testing.T) {
concurrency: tc.concurrency,
logger: logger,
includeTransitiveNeeds: false,
deleteWait: tc.deleteWait,
deleteTimeout: tc.deleteTimeout,
})
switch {
@ -126,9 +122,10 @@ func TestDestroy_2(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
} else {
assertLogEqualsToSnapshot(t, bs.String())
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
}
}
@ -255,6 +252,171 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
{Name: "front-proxy", Flags: []string{}},
{Name: "logging", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
processing 5 groups of releases in this order:
GROUP RELEASES
1 frontend-v3, frontend-v2, frontend-v1
2 backend-v2, backend-v1
3 anotherbackend
4 servicemesh, database
5 front-proxy, logging
processing releases in group 1/5: frontend-v3, frontend-v2, frontend-v1
release "frontend-v3" processed
release "frontend-v2" processed
release "frontend-v1" processed
processing releases in group 2/5: backend-v2, backend-v1
release "backend-v2" processed
release "backend-v1" processed
processing releases in group 3/5: anotherbackend
release "anotherbackend" processed
processing releases in group 4/5: servicemesh, database
release "servicemesh" processed
release "database" processed
processing releases in group 5/5: front-proxy, logging
release "front-proxy" processed
release "logging" processed
DELETED RELEASES:
NAME DURATION
frontend-v3 0s
frontend-v2 0s
frontend-v1 0s
backend-v2 0s
backend-v1 0s
anotherbackend 0s
servicemesh 0s
database 0s
front-proxy 0s
logging 0s
changing working directory back to "/path/to"
`,
})
})
@ -293,15 +455,151 @@ database 4 Fri Nov 1 08:40:07 2019 DEPLOYED mysql-3.1.0 3.1.0 def
anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0 3.1.0 default
`,
},
// Enable wait and set timeout for destroy
deleteWait: true,
deleteTimeout: 300,
// Disable concurrency to avoid in-deterministic result
concurrency: 1,
upgraded: []exectest.Release{},
deleted: []exectest.Release{
{Name: "logging", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
1 release(s) matching name=logging found in helmfile.yaml
processing 1 groups of releases in this order:
GROUP RELEASES
1 logging
processing releases in group 1/1: logging
release "logging" processed
DELETED RELEASES:
NAME DURATION
logging 0s
changing working directory back to "/path/to"
`,
})
})
@ -321,6 +619,59 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
deleted: []exectest.Release{
{Name: "frontend-v1", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
GROUP RELEASES
1 frontend-v1
2 backend-v1
processing releases in group 1/2: frontend-v1
release "frontend-v1" processed
processing releases in group 2/2: backend-v1
release "backend-v1" processed
DELETED RELEASES:
NAME DURATION
frontend-v1 0s
backend-v1 0s
changing working directory back to "/path/to"
`,
})
})
@ -340,6 +691,59 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
deleted: []exectest.Release{
{Name: "frontend-v1", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
GROUP RELEASES
1 frontend-v1
2 backend-v1
processing releases in group 1/2: frontend-v1
release "frontend-v1" processed
processing releases in group 2/2: backend-v1
release "backend-v1" processed
DELETED RELEASES:
NAME DURATION
frontend-v1 0s
backend-v1 0s
changing working directory back to "/path/to"
`,
})
})
}

View File

@ -6,17 +6,17 @@ import (
"testing"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
ffs "github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/testhelper"
)
const (
helmV3ListFlags = "--kube-context default --uninstalling --deployed --failed --pending"
helmV3ListFlagsWithoutKubeContext = "--uninstalling --deployed --failed --pending"
helmV3ListFlags = "--kube-contextdefault--uninstalling--deployed--failed--pending"
helmV3ListFlagsWithoutKubeContext = "--uninstalling--deployed--failed--pending"
)
func listFlags(namespace, kubeContext string) string {
@ -27,9 +27,9 @@ func listFlags(namespace, kubeContext string) string {
if namespace != "" {
flags = append(flags, "--namespace", namespace)
}
flags = append(flags, "--uninstalling --deployed --failed --pending")
flags = append(flags, "--uninstalling--deployed--failed--pending")
return strings.Join(flags, " ")
return strings.Join(flags, "")
}
type destroyConfig struct {
@ -38,12 +38,9 @@ type destroyConfig struct {
concurrency int
interactive bool
skipDeps bool
skipRefresh bool
logger *zap.SugaredLogger
includeTransitiveNeeds bool
skipCharts bool
deleteWait bool
deleteTimeout int
}
func (d destroyConfig) Args() string {
@ -74,36 +71,22 @@ func (d destroyConfig) SkipDeps() bool {
return d.skipDeps
}
func (d destroyConfig) SkipRefresh() bool {
return d.skipRefresh
}
func (d destroyConfig) IncludeTransitiveNeeds() bool {
return d.includeTransitiveNeeds
}
func (d destroyConfig) DeleteWait() bool {
return d.deleteWait
}
func (d destroyConfig) DeleteTimeout() int {
return d.deleteTimeout
}
func TestDestroy(t *testing.T) {
type testcase struct {
ns string
concurrency int
error string
files map[string]string
selectors []string
lists map[exectest.ListKey]string
diffs map[exectest.DiffKey]error
upgraded []exectest.Release
deleted []exectest.Release
log string
deleteWait bool
deleteTimeout int
ns string
concurrency int
error string
files map[string]string
selectors []string
lists map[exectest.ListKey]string
diffs map[exectest.DiffKey]error
upgraded []exectest.Release
deleted []exectest.Release
log string
}
check := func(t *testing.T, tc testcase) {
@ -200,9 +183,10 @@ func TestDestroy(t *testing.T) {
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
} else {
assertLogEqualsToSnapshot(t, bs.String())
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log:\nDIFF\n%s\nEOD", diff)
}
}
}
@ -316,10 +300,7 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
},
// Disable concurrency to avoid in-deterministic result
concurrency: 1,
// Enable wait and set timeout for destroy
deleteWait: true,
deleteTimeout: 300,
upgraded: []exectest.Release{},
upgraded: []exectest.Release{},
deleted: []exectest.Release{
{Name: "frontend-v3", Flags: []string{}},
{Name: "frontend-v2", Flags: []string{}},
@ -332,6 +313,171 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
{Name: "front-proxy", Flags: []string{}},
{Name: "logging", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
10 release(s) found in helmfile.yaml
processing 5 groups of releases in this order:
GROUP RELEASES
1 default//frontend-v3, default//frontend-v2, default//frontend-v1
2 default//backend-v2, default//backend-v1
3 default//anotherbackend
4 default//servicemesh, default//database
5 default//front-proxy, default//logging
processing releases in group 1/5: default//frontend-v3, default//frontend-v2, default//frontend-v1
release "frontend-v3" processed
release "frontend-v2" processed
release "frontend-v1" processed
processing releases in group 2/5: default//backend-v2, default//backend-v1
release "backend-v2" processed
release "backend-v1" processed
processing releases in group 3/5: default//anotherbackend
release "anotherbackend" processed
processing releases in group 4/5: default//servicemesh, default//database
release "servicemesh" processed
release "database" processed
processing releases in group 5/5: default//front-proxy, default//logging
release "front-proxy" processed
release "logging" processed
DELETED RELEASES:
NAME DURATION
frontend-v3 0s
frontend-v2 0s
frontend-v1 0s
backend-v2 0s
backend-v1 0s
anotherbackend 0s
servicemesh 0s
database 0s
front-proxy 0s
logging 0s
changing working directory back to "/path/to"
`,
})
})
@ -376,6 +522,145 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
deleted: []exectest.Release{
{Name: "logging", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: database
3: chart: charts/mysql
4: needs:
5: - logging
6: - name: frontend-v1
7: chart: charts/frontend
8: installed: false
9: needs:
10: - servicemesh
11: - logging
12: - backend-v1
13: - name: frontend-v2
14: chart: charts/frontend
15: needs:
16: - servicemesh
17: - logging
18: - backend-v2
19: - name: frontend-v3
20: chart: charts/frontend
21: needs:
22: - servicemesh
23: - logging
24: - backend-v2
25: - name: backend-v1
26: chart: charts/backend
27: installed: false
28: needs:
29: - servicemesh
30: - logging
31: - database
32: - anotherbackend
33: - name: backend-v2
34: chart: charts/backend
35: needs:
36: - servicemesh
37: - logging
38: - database
39: - anotherbackend
40: - name: anotherbackend
41: chart: charts/anotherbackend
42: needs:
43: - servicemesh
44: - logging
45: - database
46: - name: servicemesh
47: chart: charts/istio
48: needs:
49: - logging
50: - name: logging
51: chart: charts/fluent-bit
52: - name: front-proxy
53: chart: stable/envoy
54:
merged environment: &{default map[] map[]}
1 release(s) matching name=logging found in helmfile.yaml
processing 1 groups of releases in this order:
GROUP RELEASES
1 default//logging
processing releases in group 1/1: default//logging
release "logging" processed
DELETED RELEASES:
NAME DURATION
logging 0s
changing working directory back to "/path/to"
`,
})
})
@ -395,6 +680,59 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
deleted: []exectest.Release{
{Name: "frontend-v1", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
GROUP RELEASES
1 default//frontend-v1
2 default//backend-v1
processing releases in group 1/2: default//frontend-v1
release "frontend-v1" processed
processing releases in group 2/2: default//backend-v1
release "backend-v1" processed
DELETED RELEASES:
NAME DURATION
frontend-v1 0s
backend-v1 0s
changing working directory back to "/path/to"
`,
})
})
@ -410,13 +748,63 @@ anotherbackend 4 Fri Nov 1 08:40:07 2019 DEPLOYED anotherbackend-3.1.0
},
// Disable concurrency to avoid in-deterministic result
concurrency: 1,
// Enable wait and set timeout for destroy
deleteWait: true,
deleteTimeout: 300,
upgraded: []exectest.Release{},
upgraded: []exectest.Release{},
deleted: []exectest.Release{
{Name: "frontend-v1", Flags: []string{}},
},
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: backend-v1
3: chart: charts/backend
4: installed: false
5: - name: frontend-v1
6: chart: charts/frontend
7: needs:
8: - backend-v1
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
GROUP RELEASES
1 default//frontend-v1
2 default//backend-v1
processing releases in group 1/2: default//frontend-v1
release "frontend-v1" processed
processing releases in group 2/2: default//backend-v1
release "backend-v1" processed
DELETED RELEASES:
NAME DURATION
frontend-v1 0s
backend-v1 0s
changing working directory back to "/path/to"
`,
})
})
}

View File

@ -6,7 +6,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
@ -104,16 +103,16 @@ releases:
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
// noop on frontend-v2
{Name: "frontend-v2", Chart: "charts/frontend", Flags: "--reset-values --detailed-exitcode"}: nil,
{Name: "frontend-v2", Chart: "charts/frontend", Flags: "--detailed-exitcode--reset-values"}: nil,
// install frontend-v3
{Name: "frontend-v3", Chart: "charts/frontend", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "frontend-v3", Chart: "charts/frontend", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
// upgrades
{Name: "logging", Chart: "charts/fluent-bit", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "front-proxy", Chart: "stable/envoy", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "servicemesh", Chart: "charts/istio", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "database", Chart: "charts/mysql", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "backend-v2", Chart: "charts/backend", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "anotherbackend", Chart: "charts/anotherbackend", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "logging", Chart: "charts/fluent-bit", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "front-proxy", Chart: "stable/envoy", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "servicemesh", Chart: "charts/istio", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "database", Chart: "charts/mysql", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "backend-v2", Chart: "charts/backend", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "anotherbackend", Chart: "charts/anotherbackend", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
// delete frontend-v1 and backend-v1
@ -150,7 +149,7 @@ releases:
detailedExitcode: true,
error: "",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: nil,
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: nil,
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: ``,
@ -183,9 +182,9 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{},
upgraded: []exectest.Release{},
@ -212,8 +211,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -234,8 +233,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -257,8 +256,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -280,8 +279,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -304,8 +303,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -328,8 +327,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -352,8 +351,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -381,8 +380,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -413,8 +412,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -447,8 +446,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -479,8 +478,8 @@ releases:
detailedExitcode: true,
error: `in ./helmfile.yaml: release "foo" depends on "bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -514,8 +513,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -549,8 +548,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -581,8 +580,8 @@ releases:
detailedExitcode: true,
error: `in ./helmfile.yaml: release "bar" depends on "foo" which does not match the selectors. Please add a selector like "--selector name=foo", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -613,8 +612,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: helmV3ListFlagsWithoutKubeContext}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -636,7 +635,7 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau
loc: location(),
flags: flags{skipNeeds: true},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -664,8 +663,8 @@ releases:
selectors: []string{"app=test"},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -677,7 +676,7 @@ releases:
loc: location(),
flags: flags{skipNeeds: false},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -705,20 +704,87 @@ releases:
selectors: []string{"app=test"},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
error: `in ./helmfile.yaml.gotmpl: release "default/external-secrets" depends on "kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
error: `in ./helmfile.yaml: release "default/external-secrets" depends on "kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
err: release "default/external-secrets" depends on "kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"
`,
},
{
// see https://github.com/roboll/helmfile/issues/919#issuecomment-549831747
name: "upgrades with bad selector",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -750,6 +816,72 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"
`,
},
//
// error cases
@ -771,14 +903,53 @@ releases:
},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "baz", Chart: "mychart3", Flags: "--namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
concurrency: 1,
error: `in ./helmfile.yaml: release(s) "foo" depend(s) on an undefined release "bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?`,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: namespace: ns1
4: chart: mychart3
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: namespace: ns1
4: chart: mychart3
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
err: release(s) "foo" depend(s) on an undefined release "bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?
changing working directory back to "/path/to"
`,
},
}
@ -878,7 +1049,10 @@ releases:
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log for data defined %s:\nDIFF\n%s\nEOD", tc.loc, diff)
}
} else {
testhelper.RequireLog(t, "app_diff_test_2", bs)
}

View File

@ -6,7 +6,6 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/helmfile/vals"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/exectest"
@ -16,47 +15,38 @@ import (
)
type diffConfig struct {
args string
diffArgs string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
skipRefresh bool
includeTests bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
suppressOutputLineRegex []string
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
skipSchemaValidation bool
reuseValues bool
logger *zap.SugaredLogger
takeOwnership bool
args string
values []string
retainValuesFiles bool
set []string
validate bool
skipCRDs bool
skipDeps bool
includeTests bool
skipNeeds bool
includeNeeds bool
includeTransitiveNeeds bool
suppress []string
suppressSecrets bool
showSecrets bool
noHooks bool
suppressDiff bool
noColor bool
context int
diffOutput string
concurrency int
detailedExitcode bool
stripTrailingCR bool
interactive bool
skipDiffOnInstall bool
reuseValues bool
logger *zap.SugaredLogger
}
func (a diffConfig) Args() string {
return a.args
}
func (a diffConfig) DiffArgs() string {
return a.diffArgs
}
func (a diffConfig) Values() []string {
return a.values
}
@ -77,10 +67,6 @@ func (a diffConfig) SkipDeps() bool {
return a.skipDeps
}
func (a diffConfig) SkipRefresh() bool {
return a.skipRefresh
}
func (a diffConfig) IncludeTests() bool {
return a.includeTests
}
@ -173,21 +159,6 @@ func (a diffConfig) PostRenderer() string {
return ""
}
func (a diffConfig) PostRendererArgs() []string {
return nil
}
func (a diffConfig) SkipSchemaValidation() bool {
return a.skipSchemaValidation
}
func (a diffConfig) SuppressOutputLineRegex() []string {
return a.suppressOutputLineRegex
}
func (a diffConfig) TakeOwnership() bool {
return a.takeOwnership
}
func TestDiff(t *testing.T) {
type flags struct {
skipNeeds bool
@ -280,16 +251,16 @@ releases:
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
// noop on frontend-v2
{Name: "frontend-v2", Chart: "charts/frontend", Flags: "--kube-context default --reset-values --detailed-exitcode"}: nil,
{Name: "frontend-v2", Chart: "charts/frontend", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: nil,
// install frontend-v3
{Name: "frontend-v3", Chart: "charts/frontend", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "frontend-v3", Chart: "charts/frontend", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
// upgrades
{Name: "logging", Chart: "charts/fluent-bit", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "front-proxy", Chart: "stable/envoy", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "servicemesh", Chart: "charts/istio", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "database", Chart: "charts/mysql", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "backend-v2", Chart: "charts/backend", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "anotherbackend", Chart: "charts/anotherbackend", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "logging", Chart: "charts/fluent-bit", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "front-proxy", Chart: "stable/envoy", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "servicemesh", Chart: "charts/istio", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "database", Chart: "charts/mysql", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "backend-v2", Chart: "charts/backend", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "anotherbackend", Chart: "charts/anotherbackend", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
// delete frontend-v1 and backend-v1
@ -326,7 +297,7 @@ releases:
detailedExitcode: true,
error: "",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: nil,
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: nil,
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: ``,
@ -359,9 +330,9 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{},
upgraded: []exectest.Release{},
@ -388,8 +359,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -413,8 +384,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context hello/world --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context hello/world --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contexthello/world--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contexthello/world--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -440,8 +411,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "releaseB", Chart: "mychart2", Flags: "--kube-context arn:aws:eks:us-east-1:1234567890:cluster/myekscluster --namespace namespaceA --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "releaseA", Chart: "mychart1", Flags: "--kube-context arn:aws:eks:us-east-1:1234567890:cluster/myekscluster --namespace namespaceA --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "releaseB", Chart: "mychart2", Flags: "--kube-contextarn:aws:eks:us-east-1:1234567890:cluster/myekscluster--namespacenamespaceA--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "releaseA", Chart: "mychart1", Flags: "--kube-contextarn:aws:eks:us-east-1:1234567890:cluster/myekscluster--namespacenamespaceA--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -462,8 +433,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -485,8 +456,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -508,8 +479,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace testNamespace --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacetestNamespace--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -532,36 +503,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
{
name: "upgrade when ns1/foo needs ns1/bar and ns2/bar is disabled",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml": `
releases:
- name: bar
chart: mychart2
namespace: ns1
- name: bar
chart: mychart2
namespace: ns2
installed: false
- name: foo
chart: mychart1
namespace: ns1
needs:
- ns1/bar
`,
},
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -585,8 +528,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
},
@ -609,8 +552,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --namespace ns2 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--namespacens2--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -638,8 +581,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -670,8 +613,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -704,8 +647,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -736,8 +679,8 @@ releases:
detailedExitcode: true,
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -771,8 +714,8 @@ releases:
},
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -806,8 +749,8 @@ releases:
},
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -838,8 +781,8 @@ releases:
detailedExitcode: true,
error: `in ./helmfile.yaml: release "default//bar" depends on "default//foo" which does not match the selectors. Please add a selector like "--selector name=foo", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -873,8 +816,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -908,8 +851,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -940,8 +883,8 @@ releases:
detailedExitcode: true,
error: "Identified at least one change",
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart2", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart2", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{
{Filter: "^foo$", Flags: listFlags("", "default")}: `NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
@ -963,7 +906,7 @@ bar 4 Fri Nov 1 08:40:07 2019 DEPLOYED mychart2-3.1.0 3.1.0 defau
loc: location(),
flags: flags{skipNeeds: true},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -991,8 +934,8 @@ releases:
selectors: []string{"app=test"},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
@ -1004,7 +947,7 @@ releases:
loc: location(),
flags: flags{skipNeeds: false},
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -1032,20 +975,87 @@ releases:
selectors: []string{"app=test"},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-context default --namespace default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "external-secrets", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "my-release", Chart: "incubator/raw", Flags: "--kube-contextdefault--namespacedefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
upgraded: []exectest.Release{},
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
error: `in ./helmfile.yaml.gotmpl: release "default/default/external-secrets" depends on "default/kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
error: `in ./helmfile.yaml: release "default/default/external-secrets" depends on "default/kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
2 release(s) matching app=test found in helmfile.yaml
err: release "default/default/external-secrets" depends on "default/kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"
`,
},
{
// see https://github.com/roboll/helmfile/issues/919#issuecomment-549831747
name: "upgrades with bad selector",
loc: location(),
files: map[string]string{
"/path/to/helmfile.yaml.gotmpl": `
"/path/to/helmfile.yaml": `
{{ $mark := "a" }}
releases:
@ -1077,6 +1087,72 @@ releases:
error: "err: no releases found that matches specified selector(app=test_non_existent) and environment(default), in any helmfile",
// as we check for log output, set concurrency to 1 to avoid non-deterministic test result
concurrency: 1,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1:
2:
3: releases:
4: - name: kubernetes-external-secrets
5: chart: incubator/raw
6: namespace: kube-system
7:
8: - name: external-secrets
9: chart: incubator/raw
10: namespace: default
11: labels:
12: app: test
13: needs:
14: - kube-system/kubernetes-external-secrets
15:
16: - name: my-release
17: chart: incubator/raw
18: namespace: default
19: labels:
20: app: test
21: needs:
22: - default/external-secrets
23:
merged environment: &{default map[] map[]}
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"
`,
},
//
// error cases
@ -1099,14 +1175,51 @@ releases:
detailedExitcode: true,
selectors: []string{"name=foo"},
diffs: map[exectest.DiffKey]error{
{Name: "bar", Chart: "mychart3", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "bar", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
concurrency: 1,
error: `in ./helmfile.yaml: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies`,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart3
4: - name: foo
5: chart: mychart1
6: needs:
7: - bar
8:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart3
4: - name: foo
5: chart: mychart1
6: needs:
7: - bar
8:
merged environment: &{default map[] map[]}
1 release(s) matching name=foo found in helmfile.yaml
err: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"
`,
},
{
name: "non-existent release in needs",
@ -1125,14 +1238,53 @@ releases:
},
detailedExitcode: true,
diffs: map[exectest.DiffKey]error{
{Name: "baz", Chart: "mychart3", Flags: "--kube-context default --namespace ns1 --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-context default --reset-values --detailed-exitcode"}: helmexec.ExitError{Code: 2},
{Name: "baz", Chart: "mychart3", Flags: "--kube-contextdefault--namespacens1--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
{Name: "foo", Chart: "mychart1", Flags: "--kube-contextdefault--detailed-exitcode--reset-values"}: helmexec.ExitError{Code: 2},
},
lists: map[exectest.ListKey]string{},
upgraded: []exectest.Release{},
deleted: []exectest.Release{},
concurrency: 1,
error: `in ./helmfile.yaml: release(s) "default//foo" depend(s) on an undefined release "default//bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?`,
log: `processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: namespace: ns1
4: chart: mychart3
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: baz
3: namespace: ns1
4: chart: mychart3
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml
err: release(s) "default//foo" depend(s) on an undefined release "default//bar". Perhaps you made a typo in "needs" or forgot defining a release named "bar" with appropriate "namespace" and "kubeContext"?
changing working directory back to "/path/to"
`,
},
}
@ -1238,7 +1390,10 @@ releases:
if tc.log != "" {
actual := bs.String()
assert.Equal(t, tc.log, actual, 3)
diff, exists := testhelper.Diff(tc.log, actual, 3)
if exists {
t.Errorf("unexpected log for data defined %s:\nDIFF\n%s\nEOD", tc.loc, diff)
}
} else {
testhelper.RequireLog(t, "app_diff_test_1", bs)
}

View File

@ -17,12 +17,12 @@ import (
)
const (
HelmRequiredVersion = "v3.18.6"
HelmDiffRecommendedVersion = "v3.13.1"
HelmRecommendedVersion = "v3.19.0"
HelmSecretsRecommendedVersion = "v4.6.5"
HelmGitRecommendedVersion = "v1.3.0"
HelmS3RecommendedVersion = "v0.16.3"
HelmRequiredVersion = "v3.10.3"
HelmRecommendedVersion = "v3.12.1"
HelmDiffRecommendedVersion = "v3.8.1"
HelmSecretsRecommendedVersion = "v4.1.1"
HelmGitRecommendedVersion = "v0.12.0"
HelmS3RecommendedVersion = "v0.14.0"
HelmInstallCommand = "https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3"
)
@ -163,10 +163,7 @@ func (h *HelmfileInit) WhetherContinue(ask string) error {
func (h *HelmfileInit) CheckHelmPlugins() error {
settings := cli.New()
helm, err := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", "", h.runner)
if err != nil {
return err
}
helm := helmexec.New(h.helmBinary, helmexec.HelmExecOptions{}, h.logger, "", h.runner)
for _, p := range helmPlugins {
pluginVersion, err := helmexec.GetPluginVersion(p.name, settings.PluginsDirectory)
if err != nil {
@ -174,7 +171,7 @@ func (h *HelmfileInit) CheckHelmPlugins() error {
return err
}
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %q is not installed, do you want to install it?", p.name))
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %s is not installed, do you need to install it", p.name))
if err != nil {
return err
}
@ -187,7 +184,7 @@ func (h *HelmfileInit) CheckHelmPlugins() error {
}
requiredVersion, _ := semver.NewVersion(p.version)
if pluginVersion.LessThan(requiredVersion) {
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %q version is too low, do you want to update it?", p.name))
err = h.WhetherContinue(fmt.Sprintf("The helm plugin %s version is too low, do you need to update it", p.name))
if err != nil {
return err
}

View File

@ -5,31 +5,30 @@ import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"path"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDownloadfile(t *testing.T) {
var ts *httptest.Server
cases := []struct {
name string
handler func(http.ResponseWriter, *http.Request)
url string
filepath string
wantContent string
wantError string
}{
{
name: "successful download of file content",
name: "download success",
handler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "helmfile")
},
wantContent: "helmfile",
},
{
name: "404 error when file not found",
name: "download 404",
handler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprint(w, "not found")
@ -37,7 +36,7 @@ func TestDownloadfile(t *testing.T) {
wantError: "download .*? error, code: 404",
},
{
name: "500 error on server failure",
name: "download 500",
handler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, "server error")
@ -45,42 +44,46 @@ func TestDownloadfile(t *testing.T) {
wantError: "download .*? error, code: 500",
},
{
name: "error due to invalid file path",
name: "download path error",
handler: func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "helmfile")
},
filepath: "abc/down.txt",
wantError: "open .*? no such file or directory",
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
dir := t.TempDir()
downfile := filepath.Join(dir, "down.txt")
downfile := path.Join(dir, "down.txt")
if c.filepath != "" {
downfile = filepath.Join(dir, c.filepath)
downfile = path.Join(dir, c.filepath)
}
ts := httptest.NewServer(http.HandlerFunc(c.handler))
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.handler(w, r)
}))
defer ts.Close()
err := downloadfile(downfile, ts.URL)
url := ts.URL
if c.url != "" {
url = c.url
}
err := downloadfile(downfile, url)
if c.wantError != "" {
assert.Error(t, err)
if err != nil {
matched, regexErr := regexp.MatchString(c.wantError, err.Error())
assert.NoError(t, regexErr)
assert.True(t, matched, "expected error message to match regex: %s", c.wantError)
if err == nil {
t.Errorf("download got no error, want error: %v", c.wantError)
} else if matched, regexErr := regexp.MatchString(c.wantError, err.Error()); regexErr != nil || !matched {
t.Errorf("download got error: %v, want error: %v", err, c.wantError)
}
return
}
content, err := os.ReadFile(downfile)
assert.NoError(t, err)
assert.Equal(t, c.wantContent, string(content), "unexpected content in downloaded file")
if err != nil {
t.Errorf("read download file error: %v", err)
}
if string(content) != c.wantContent {
t.Errorf("download file content got: %v, want content: %v", string(content), c.wantContent)
}
})
}
}

View File

@ -3,12 +3,10 @@ package app
import (
"fmt"
"os"
"slices"
"sort"
"strings"
"github.com/fatih/color"
"github.com/helmfile/helmfile/pkg/argparser"
"github.com/helmfile/helmfile/pkg/helmexec"
"github.com/helmfile/helmfile/pkg/state"
)
@ -42,21 +40,6 @@ func (r *Run) askForConfirmation(msg string) bool {
return AskForConfirmation(msg)
}
func (r *Run) prepareChartsIfNeeded(helmfileCommand string, dir string, concurrency int, opts state.ChartPrepareOptions) (map[state.PrepareChartKey]string, error) {
// Skip chart preparation for certain commands
skipCommands := []string{"write-values", "list"}
if slices.Contains(skipCommands, strings.ToLower(helmfileCommand)) {
return nil, nil
}
releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, concurrency, helmfileCommand, opts)
if len(errs) > 0 {
return nil, fmt.Errorf("%v", errs)
}
return releaseToChart, nil
}
func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepareOptions, f func()) error {
if r.ReleaseToChart != nil {
panic("Run.PrepareCharts can be called only once")
@ -89,9 +72,12 @@ func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepare
return err
}
releaseToChart, err := r.prepareChartsIfNeeded(helmfileCommand, dir, opts.Concurrency, opts)
if err != nil {
return err
concurrency := opts.Concurrency
releaseToChart, errs := r.state.PrepareCharts(r.helm, dir, concurrency, helmfileCommand, opts)
if len(errs) > 0 {
return fmt.Errorf("%v", errs)
}
for i := range r.state.Releases {
@ -102,11 +88,9 @@ func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepare
KubeContext: rel.KubeContext,
}
if chart := releaseToChart[key]; chart != rel.Chart {
// The chart has been downloaded and modified by Helmfile (and chartify under the hood).
// We let the later step use the modified version of the chart, located under the `chart` variable,
// instead of the original chart path.
// This way, the later step can use the modified chart without knowing
// if it has been modified or not.
// In this case we assume that the chart is downloaded and modified by Helmfile and chartify.
// So we take note of the local filesystem path to the modified version of the chart
// and use it later via the Release.ChartPathOrName() func.
rel.ChartPath = chart
}
}
@ -115,28 +99,34 @@ func (r *Run) withPreparedCharts(helmfileCommand string, opts state.ChartPrepare
f()
_, err = r.state.TriggerGlobalCleanupEvent(helmfileCommand)
_, err := r.state.TriggerGlobalCleanupEvent(helmfileCommand)
return err
}
func (r *Run) Deps(c DepsConfigProvider) []error {
if !c.SkipRepos() {
if err := r.ctx.SyncReposOnce(r.state, r.helm); err != nil {
return []error{err}
}
}
r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
return r.state.UpdateDeps(r.helm, c.IncludeTransitiveNeeds())
}
func (r *Run) Repos(c ReposConfigProvider) error {
r.helm.SetExtraArgs(GetArgs(c.Args(), r.state)...)
r.helm.SetExtraArgs(argparser.GetArgs(c.Args(), r.state)...)
return r.ctx.SyncReposOnce(r.state, r.helm)
}
// TODO: Remove this function once Helmfile v0.x
func (r *Run) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) []error {
st := r.state
helm := r.helm
affectedReleases := state.AffectedReleases{}
errs := st.SyncReleases(&affectedReleases, helm, c.Values(), c.Concurrency())
affectedReleases.DisplayAffectedReleases(c.Logger())
return errs
}
func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfigProvider, diffOpts *state.DiffOpts) (*string, map[string]state.ReleaseSpec, map[string]state.ReleaseSpec, []error) {
st := r.state
helm := r.helm
@ -207,11 +197,7 @@ func (r *Run) diff(triggerCleanupEvent bool, detailedExitCode bool, c DiffConfig
names = append(names, fmt.Sprintf(" %s (%s) UPDATED", r.Name, r.Chart))
}
for _, r := range releasesToBeDeleted {
releaseToBeDeleted := fmt.Sprintf(" %s (%s) DELETED", r.Name, r.Chart)
if c.Color() {
releaseToBeDeleted = color.RedString(releaseToBeDeleted)
}
names = append(names, releaseToBeDeleted)
names = append(names, fmt.Sprintf(" %s (%s) DELETED", r.Name, r.Chart))
}
// Make the output deterministic for testing purpose
sort.Strings(names)

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
0 release(s) matching app=test_non_existent found in helmfile.yaml
changing working directory back to "/path/to"

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
2 release(s) matching app=test found in helmfile.yaml
err: release "default/default/external-secrets" depends on "default/kube-system/kubernetes-external-secrets" which does not match the selectors. Please add a selector like "--selector name=kubernetes-external-secrets", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
2 release(s) matching app=test found in helmfile.yaml
processing 4 groups of releases in this order:

View File

@ -0,0 +1,108 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
1 release(s) matching name=test2 found in helmfile.yaml
err: release "default//test2" depends on "default/kube-system/disabled" which does not match the selectors. Please add a selector like "--selector name=disabled", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"

View File

@ -0,0 +1,108 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
1 release(s) matching name=test3 found in helmfile.yaml
err: release "default//test2" depends on "default/kube-system/disabled" which does not match the selectors. Please add a selector like "--selector name=disabled", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
1 release(s) matching name=test2 found in helmfile.yaml
processing 2 groups of releases in this order:
@ -11,7 +111,6 @@ GROUP RELEASES
processing releases in group 1/2: default/kube-system/disabled
processing releases in group 2/2: default//test2
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
Affected releases are:
disabled (incubator/raw) DELETED

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
1 release(s) matching name=test3 found in helmfile.yaml
processing 3 groups of releases in this order:
@ -13,7 +113,6 @@ GROUP RELEASES
processing releases in group 1/3: default/kube-system/disabled
processing releases in group 2/3: default//test2
processing releases in group 3/3: default//test3
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
Affected releases are:
disabled (incubator/raw) DELETED

View File

@ -0,0 +1,108 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
2 release(s) matching name=test2 found in helmfile.yaml
err: release "default//test2" depends on "default/kube-system/disabled" which does not match the selectors. Please add a selector like "--selector name=disabled", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"

View File

@ -0,0 +1,108 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
3 release(s) matching name=test3 found in helmfile.yaml
err: release "default//test2" depends on "default/kube-system/disabled" which does not match the selectors. Please add a selector like "--selector name=disabled", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies
changing working directory back to "/path/to"

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
1 release(s) matching name=test2 found in helmfile.yaml
processing 2 groups of releases in this order:
@ -11,7 +111,6 @@ GROUP RELEASES
processing releases in group 1/2: default/kube-system/disabled
processing releases in group 2/2: default//test2
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
Affected releases are:
disabled (incubator/raw) DELETED

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
1 release(s) matching name=test3 found in helmfile.yaml
processing 3 groups of releases in this order:
@ -13,7 +113,6 @@ GROUP RELEASES
processing releases in group 1/3: default/kube-system/disabled
processing releases in group 2/3: default//test2
processing releases in group 3/3: default//test3
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
Affected releases are:
disabled (incubator/raw) DELETED

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
2 release(s) matching app=test found in helmfile.yaml
processing 4 groups of releases in this order:

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
1 release(s) matching name=test3 found in helmfile.yaml
processing 3 groups of releases in this order:
@ -13,7 +113,6 @@ GROUP RELEASES
processing releases in group 1/3: default/kube-system/disabled
processing releases in group 2/3: default//test2
processing releases in group 3/3: default//test3
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
Affected releases are:
disabled (incubator/raw) DELETED

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
2 release(s) matching app=test found in helmfile.yaml
processing 2 groups of releases in this order:

View File

@ -1,5 +1,34 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: a
3: chart: incubator/raw
4: namespace: default
5: - name: b
6: chart: incubator/raw
7: namespace: default
8:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: a
3: chart: incubator/raw
4: namespace: default
5: - name: b
6: chart: incubator/raw
7: namespace: default
8:
merged environment: &{default map[] map[]}
1 release(s) matching name=a found in helmfile.yaml

View File

@ -1,11 +0,0 @@
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"

View File

@ -1,5 +1,36 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: a
3: chart: incubator/raw
4: installed: false
5: namespace: default
6: - name: b
7: chart: incubator/raw
8: namespace: default
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: a
3: chart: incubator/raw
4: installed: false
5: namespace: default
6: - name: b
7: chart: incubator/raw
8: namespace: default
9:
merged environment: &{default map[] map[]}
1 release(s) matching name=a found in helmfile.yaml

View File

@ -1,7 +1,107 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: logging
3: chart: incubator/raw
4: namespace: kube-system
5:
6: - name: kubernetes-external-secrets
7: chart: incubator/raw
8: namespace: kube-system
9: needs:
10: - kube-system/logging
11:
12: - name: external-secrets
13: chart: incubator/raw
14: namespace: default
15: labels:
16: app: test
17: needs:
18: - kube-system/kubernetes-external-secrets
19:
20: - name: my-release
21: chart: incubator/raw
22: namespace: default
23: labels:
24: app: test
25: needs:
26: - default/external-secrets
27:
28:
29: # Disabled releases are treated as missing
30: - name: disabled
31: chart: incubator/raw
32: namespace: kube-system
33: installed: false
34:
35: - name: test2
36: chart: incubator/raw
37: needs:
38: - kube-system/disabled
39:
40: - name: test3
41: chart: incubator/raw
42: needs:
43: - test2
44:
merged environment: &{default map[] map[]}
WARNING: release test2 needs disabled, but disabled is not installed due to installed: false. Either mark disabled as installed or remove disabled from test2's needs
2 release(s) matching app=test found in helmfile.yaml
processing 2 groups of releases in this order:

View File

@ -1,5 +1,38 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: foo
3: chart: mychart1
4: namespace: ns1
5: needs:
6: - ns2/bar
7: - name: bar
8: chart: mychart2
9: namespace: ns2
10:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: foo
3: chart: mychart1
4: namespace: ns1
5: needs:
6: - ns2/bar
7: - name: bar
8: chart: mychart2
9: namespace: ns2
10:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml

View File

@ -1,18 +0,0 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
merged environment: &{default map[] map[]}
3 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
GROUP RELEASES
1 default/ns1/bar
2 default/ns1/foo
processing releases in group 1/2: default/ns1/bar
processing releases in group 2/2: default/ns1/foo
Affected releases are:
bar (mychart2) DELETED
bar (mychart2) UPDATED
foo (mychart1) UPDATED
changing working directory back to "/path/to"

View File

@ -1,5 +1,36 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: foo
3: chart: mychart1
4: - name: bar
5: chart: mychart2
6: installed: false
7: needs:
8: - foo
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: foo
3: chart: mychart1
4: - name: bar
5: chart: mychart2
6: installed: false
7: needs:
8: - foo
9:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml

View File

@ -1,7 +1,37 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
merged environment: &{default map[] map[]}
WARNING: release foo needs bar, but bar is not installed due to installed: false. Either mark bar as installed or remove bar from foo's needs
2 release(s) found in helmfile.yaml
err: release "default//foo" depends on "default//bar" which does not match the selectors. Please add a selector like "--selector name=bar", or indicate whether to skip (--skip-needs) or include (--include-needs) these dependencies

View File

@ -1,7 +1,37 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
merged environment: &{default map[] map[]}
WARNING: release foo needs bar, but bar is not installed due to installed: false. Either mark bar as installed or remove bar from foo's needs
2 release(s) found in helmfile.yaml
processing 2 groups of releases in this order:
@ -11,7 +41,6 @@ GROUP RELEASES
processing releases in group 1/2: default//bar
processing releases in group 2/2: default//foo
WARNING: release foo needs bar, but bar is not installed due to installed: false. Either mark bar as installed or remove bar from foo's needs
Affected releases are:
bar (mychart2) DELETED
foo (mychart1) UPDATED

View File

@ -1,7 +1,37 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: needs:
8: - bar
9:
merged environment: &{default map[] map[]}
WARNING: release foo needs bar, but bar is not installed due to installed: false. Either mark bar as installed or remove bar from foo's needs
2 release(s) found in helmfile.yaml
processing 1 groups of releases in this order:
@ -9,7 +39,6 @@ GROUP RELEASES
1 default//foo
processing releases in group 1/1: default//foo
WARNING: release foo needs bar, but bar is not installed due to installed: false. Either mark bar as installed or remove bar from foo's needs
Affected releases are:
bar (mychart2) DELETED
foo (mychart1) UPDATED

View File

@ -1,5 +1,38 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: needs:
6: - foo
7: - name: foo
8: chart: mychart1
9: installed: false
10:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: needs:
6: - foo
7: - name: foo
8: chart: mychart1
9: installed: false
10:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml

View File

@ -1,5 +1,38 @@
processing file "helmfile.yaml" in directory "."
changing working directory to "/path/to"
first-pass rendering starting for "helmfile.yaml.part.0": inherited=&{default map[] map[]}, overrode=<nil>
first-pass uses: &{default map[] map[]}
first-pass rendering output of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: installed: false
8: needs:
9: - bar
10:
first-pass produced: &{default map[] map[]}
first-pass rendering result of "helmfile.yaml.part.0": {default map[] map[]}
vals:
map[]
defaultVals:[]
second-pass rendering result of "helmfile.yaml.part.0":
0:
1: releases:
2: - name: bar
3: chart: mychart2
4: installed: false
5: - name: foo
6: chart: mychart1
7: installed: false
8: needs:
9: - bar
10:
merged environment: &{default map[] map[]}
2 release(s) found in helmfile.yaml

Some files were not shown because too many files have changed in this diff Show More