├── .github ├── FUNDING.yml └── workflows │ ├── go.yml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── Makefile ├── README.md ├── examples ├── .gitignore ├── Makefile ├── importing-existing-helmfile-managed-releases │ ├── Makefile │ ├── README.md │ ├── helmfile.yaml │ └── main.tf ├── issue50 │ ├── scenario1 │ │ └── platform │ │ │ └── helmfile.yaml │ └── scenario2 │ │ └── platform │ │ └── helmfile.d │ │ └── helmfile.yaml ├── temp.values.yaml ├── testdata │ ├── 01-bootstrap │ │ ├── helmfile.yaml │ │ ├── test.tf │ │ └── values.yaml │ ├── 02-more-resources │ │ ├── helmfile.yaml │ │ ├── test.tf │ │ └── values.yaml │ ├── 03-add-prom │ │ ├── helmfile.yaml │ │ └── test.tf │ └── 04-issue50 │ │ ├── helmfile.yaml │ │ └── test.tf └── values.yaml ├── go.mod ├── go.sum ├── issue50 ├── scenario1 │ └── platform │ │ └── helmfile.yaml │ │ └── temp.values-484e91c5bf5320485228cc155bb7badb28b1c0e660723c28bc954d09f15ce715.yaml └── scenario2 │ └── platform │ └── temp.values-673c7e32e5586887c39487b413038cdb58554ef15dd9c55edf2b8a713a68ddb0.yaml ├── main.go └── pkg ├── helmfile ├── config.go ├── context.go ├── hash.go ├── logging.go ├── provider.go ├── provider_test.go ├── readwrite.go ├── release.go ├── release_set.go ├── resource_helmfile_embedding_example.go ├── resource_release.go ├── resource_release_set.go ├── resource_release_set_test.go ├── safe_encode_string.go ├── schema.go ├── shoal.go ├── skip_diff.go └── utility.go └── profile └── profile.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 2 | - mumoshu 3 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push, pull_request] 3 | jobs: 4 | 5 | build: 6 | name: Build 7 | runs-on: ubuntu-latest 8 | steps: 9 | 10 | - name: Set up Go 1.16 11 | uses: actions/setup-go@v1 12 | with: 13 | go-version: 1.16 14 | id: go 15 | 16 | - name: Setup helmfile 17 | uses: mamezou-tech/setup-helmfile@v0.6.0 18 | with: 19 | helmfile-version: "v0.128.1" 20 | 21 | - name: Create k8s Kind Cluster 22 | uses: helm/kind-action@v1.0.0 23 | 24 | - name: Validate test dependencies 25 | run: | 26 | helmfile --version 27 | helm version 28 | kubectl version 29 | 30 | - name: Check out code into the Go module directory 31 | uses: actions/checkout@v1 32 | 33 | - name: Test 34 | run: make test 35 | 36 | - name: Build 37 | run: make build -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | branches: 6 | - "!*" 7 | tags: 8 | - "v*" 9 | 10 | jobs: 11 | goreleaser: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - 15 | name: Checkout 16 | uses: actions/checkout@v1 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v1 20 | with: 21 | go-version: 1.13.x 22 | # See https://goreleaser.com/ci/actions/#signing 23 | - 24 | name: Import GPG key 25 | id: import_gpg 26 | uses: crazy-max/ghaction-import-gpg@v3 27 | with: 28 | # See https://github.com/crazy-max/ghaction-import-gpg#prerequisites 29 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 30 | passphrase: ${{ secrets.PASSPHRASE }} 31 | - 32 | name: Run GoReleaser 33 | uses: goreleaser/goreleaser-action@v1 34 | with: 35 | version: latest 36 | args: release --rm-dist 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | terraform-artifactory.tf 2 | terraform-provider-artifactory 3 | *.dll 4 | *.exe 5 | .DS_Store 6 | example.tf 7 | terraform.tfplan 8 | terraform.tfstate 9 | bin/ 10 | modules-dev/ 11 | vendor/*/ 12 | website/.vagrant 13 | website/build 14 | website/node_modules 15 | .vagrant/ 16 | *.backup 17 | ./*.tfstate 18 | .terraform/ 19 | *.log 20 | *.bak 21 | *~ 22 | .*.swp 23 | .idea 24 | resources 25 | resources2 26 | src/ 27 | .vscode 28 | .vscode/ 29 | dist/ 30 | examples/terraform.d 31 | *.iml 32 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Visit https://goreleaser.com for documentation on how to customize this 2 | # behavior. 3 | before: 4 | hooks: 5 | # this is just an example and not a requirement for provider building/publishing 6 | - go mod tidy 7 | builds: 8 | - env: 9 | # goreleaser does not work with CGO, it could also complicate 10 | # usage by users in CI/CD systems like Terraform Cloud where 11 | # they are unable to install libraries. 12 | - CGO_ENABLED=0 13 | mod_timestamp: '{{ .CommitTimestamp }}' 14 | flags: 15 | - -trimpath 16 | ldflags: 17 | - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' 18 | goos: 19 | - freebsd 20 | - windows 21 | - linux 22 | - darwin 23 | goarch: 24 | - amd64 25 | - '386' 26 | - arm 27 | - arm64 28 | ignore: 29 | - goos: darwin 30 | goarch: '386' 31 | # Prevent "error=failed to build for freebsd_arm64: cmd/go: unsupported GOOS/GOARCH pair freebsd/arm64" 32 | - goos: freebsd 33 | goarch: arm64 34 | binary: '{{ .ProjectName }}_v{{ .Version }}' 35 | archives: 36 | - format: zip 37 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}' 38 | checksum: 39 | name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS' 40 | algorithm: sha256 41 | signs: 42 | - artifacts: checksum 43 | args: 44 | # if you are using this is a GitHub action or some other automated pipeline, you 45 | # need to pass the batch flag to indicate its not interactive. 46 | - "--batch" 47 | - "--local-user" 48 | - "{{ .Env.GPG_FINGERPRINT }}" # set this environment variable for your signing key 49 | - "--output" 50 | - "${signature}" 51 | - "--detach-sign" 52 | - "${artifact}" 53 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := build 2 | OS := $(shell go env GOOS) 3 | ARCH := $(shell go env GOARCH) 4 | PLUGIN_PATH ?= ${HOME}/.terraform.d/plugins/${OS}_${ARCH} 5 | PLUGIN_NAME := terraform-provider-helmfile 6 | DIST_PATH := dist/${OS}_${ARCH} 7 | GO_PACKAGES := $(shell go list ./... | grep -v /vendor/) 8 | GO_FILES := $(shell find . -type f -name '*.go') 9 | GO ?= go 10 | 11 | .PHONY: all 12 | all: test build 13 | 14 | .PHONY: test 15 | test: test-all 16 | 17 | .PHONY: test-all 18 | test-all: 19 | @TF_ACC=1 $(GO) test -v -race $(GO_PACKAGES) 20 | 21 | ${DIST_PATH}/${PLUGIN_NAME}: ${GO_FILES} 22 | mkdir -p $(DIST_PATH); \ 23 | $(GO) build -o $(DIST_PATH)/${PLUGIN_NAME} 24 | 25 | .PHONY: build 26 | build: ${DIST_PATH}/${PLUGIN_NAME} 27 | 28 | .PHONY: install 29 | install: clean build 30 | mkdir -p $(PLUGIN_PATH); \ 31 | rm -rf $(PLUGIN_PATH)/${PLUGIN_NAME}; \ 32 | install -m 0755 $(DIST_PATH)/${PLUGIN_NAME} $(PLUGIN_PATH)/${PLUGIN_NAME} 33 | 34 | # Set TF_LOG=DEBUG to enable debug logs from the provider 35 | # Setting TF_LOG_PATH would also help 36 | .PHONY: example/plan 37 | example/plan: 38 | mkdir -p examples/terraform.d/plugins 39 | env PLUGIN_PATH=examples/terraform.d/plugins/$(OS)_$(ARCH) make install 40 | cd examples; terraform init; terraform plan 41 | 42 | .PHONY: example/apply 43 | example/apply: 44 | mkdir -p examples/terraform.d/plugins 45 | env PLUGIN_PATH=examples/terraform.d/plugins/$(OS)_$(ARCH) make install 46 | cd examples; terraform init; terraform apply 47 | 48 | .PHONY: example/destroy 49 | example/destroy: 50 | mkdir -p examples/terraform.d/plugins 51 | env PLUGIN_PATH=examples/terraform.d/plugins/$(OS)_$(ARCH) make install 52 | cd examples; terraform init; terraform destroy 53 | 54 | .PHONY: clean 55 | clean: 56 | rm -rf ${DIST_PATH}/* 57 | 58 | .PHONY: release 59 | release: 60 | echo Please set GPG_FINGERPRINT 61 | gpg --armor --detach-sign 62 | goreleaser release --rm-dist 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-provider-helmfile 2 | 3 | Deploy [Helmfile](https://github.com/roboll/helmfile/) releases from within Terraform. 4 | 5 | Benefits: 6 | 7 | - Entry-point to your infrastructure and app deployments as a whole 8 | - Input terraform variables and outputs into Helmfile 9 | - Blue-green deployment of your whole stack with tf's `create_before_destroy` 10 | - [AWS authentication and AssumeRole support](#aws-authentication-and-assumerole-support) 11 | 12 | ## Prerequisites 13 | 14 | - Helmfile 15 | - v0.126.0 or greater is highly recommended due to #28 16 | 17 | ## Installation 18 | 19 | **For Terraform 0.12:** 20 | 21 | Install the `terraform-provider-helmfile` binary under `.terraform/plugins/${OS}_${ARCH}`, so that the binary is at e.g. `${WORKSPACE}/.terraform/plugins/darwin_amd64/terraform-provider-helmfile`. 22 | 23 | **For Terraform 0.13 and later:** 24 | 25 | The provider is [available at Terraform Registry](https://registry.terraform.io/providers/mumoshu/helmfile/latest?pollNotifications=true) so you can just add the following to your tf file for installation: 26 | 27 | ``` 28 | terraform { 29 | required_providers { 30 | helmfile = { 31 | source = "mumoshu/helmfile" 32 | version = "VERSION" 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | Please replace `VERSION` with the version number of the provider without the `v` prefix, like `0.3.14`. 39 | 40 | ## Examples 41 | 42 | There is nothing to configure for the provider, so you firstly declare the provider like: 43 | 44 | ``` 45 | provider "helmfile" {} 46 | ``` 47 | 48 | You can define a release in one of the four ways: 49 | 50 | - [Inline `helmfile_release`](https://github.com/mumoshu/terraform-provider-helmfile#inline-helmfile_release) 51 | - [External `helmfile_release_set`](https://github.com/mumoshu/terraform-provider-helmfile#external-helmfile_release_set) 52 | - [1:1 helmfile.yaml mapping](https://github.com/mumoshu/terraform-provider-helmfile#exsiting-helmfileyaml-11-mapping--) 53 | - [Using helmfile.d folder](https://github.com/mumoshu/terraform-provider-helmfile#existing-helmfiled-folder--) 54 | - [Inline `helmfile_release_set`](https://github.com/mumoshu/terraform-provider-helmfile#inline-helmfile_release_set) 55 | 56 | 57 | ### Inline `helmfile_release` 58 | `helmfile_release` would be a natural choice for users who are familiar with Terraform. It just map each Terraform `helmfile_release` resource to a Helm release 1-by-1: 59 | 60 | ```hcl 61 | resource "helmfile_release" "myapp" { 62 | # `name` is the optional release name. When omitted, it's set to the ID of the resource, "myapp". 63 | # name = "myapp-${var.somevar}" 64 | namespace = "default" 65 | chart = "sp/podinfo" 66 | helm_binary = "helm3" 67 | 68 | working_directory = path.module 69 | values = [ 70 | <") 94 | environment = "prod" 95 | values = [ 96 | < null 198 | ~ dirty = true -> false 199 | environment = "default" 200 | environment_variables = { 201 | "FOO" = "foo" 202 | } 203 | helm_binary = "helm3" 204 | id = "bnd30hkllhcvvgsrplo0" 205 | path = "./helmfile.yaml" 206 | selector = { 207 | "labelkey1" = "value1" 208 | } 209 | values = { 210 | "name" = "myapp" 211 | } 212 | working_directory = "." 213 | } 214 | 215 | Plan: 0 to add, 1 to change, 0 to destroy. 216 | ``` 217 | 218 | Running `terraform apply` runs `helmfile apply` to deploy your releases. 219 | 220 | The computed field `apply_output` is used to surface the output from Helmfile. You can use in the string interpolation to produce a useful Terraform output. 221 | 222 | In the example below, the output `mystack_apply` is generated from `apply_output` so that you can review what has actually changed on `helmfile apply`: 223 | 224 | ```console 225 | helmfile_release_set.mystack: Refreshing state... [id=bnd30hkllhcvvgsrplo0] 226 | 227 | Apply complete! Resources: 0 added, 0 changed, 0 destroyed. 228 | 229 | Outputs: 230 | 231 | mystack_apply = Comparing release=myapp-foo, chart=sp/podinfo 232 | ******************** 233 | 234 | Release was not present in Helm. Diff will show entire contents as new. 235 | 236 | ******************** 237 | ... 238 | 239 | mystack_diff = 240 | ``` 241 | 242 | `terraform apply` just succeeds without any effect when there's no change detected by `helmfile`: 243 | 244 | ```console 245 | helmfile_release_set.mystack: Refreshing state... [id=bnd30hkllhcvvgsrplo0] 246 | 247 | Apply complete! Resources: 0 added, 0 changed, 0 destroyed. 248 | 249 | Outputs: 250 | 251 | mystack_apply = 252 | mystack_diff = 253 | ``` 254 | 255 | ## Advanced Features 256 | 257 | - [Declarative binary version management](#declarative-binary-version-management) 258 | - [Importing existing Helmfile project](/examples/importing-existing-helmfile-managed-releases) 259 | - [AWS authencation and AssumeRole support](#aws-authentication-and-assumerole-support) 260 | 261 | ## Declarative binary version management 262 | 263 | `terraform-provider-helmfile` has a built-in package manager called [shoal](https://github.com/mumoshu/shoal). 264 | With that, you can specify the following `helmfile_release_set` attributes to let the provider install the executable binaries on demand: 265 | 266 | - `version` for installing `helmfile` 267 | - `helm_version` for installing `helm` 268 | - `helm_diff_version` for installing `helm-diff` 269 | 270 | `version` and `helm_version` uses the Go runtime and [go-git](https://github.com/go-git/go-git) so it should work without any dependency. 271 | 272 | `helm_diff_version` requires `helm plugin install` to be runnable. The plugin installation process can vary depending on the plugin and its `plugin.yaml`. 273 | 274 | With the below example, the provider installs `helmfile` v0.128.0, `helm` 3.2.1, and `helm-diff` 3.1.3, so that you don't need to install them beforehand. 275 | This should be handy when you're trying to use this provider on Terraform Cloud, whose runtime environment is [not available for customization by the user](https://www.terraform.io/docs/cloud/run/run-environment.html). 276 | 277 | ```hcl-terraform 278 | helmfile_release_set "mystack" { 279 | version = "0.128.0" 280 | helm_version = "3.2.1" 281 | helm_diff_version = "v3.1.3" 282 | 283 | // snip 284 | ``` 285 | 286 | ### AWS authentication and AssumeRole support 287 | 288 | Providing any combination of `aws_region`, `aws_profile`, and `aws_assume_role`, 289 | the provider obtains the following environment variables and provider it to any `helmfile` command. 290 | 291 | - `aws_region` attribute: `AWS_DEFAULT_REGION` 292 | - `aws_profile` attribute: `AWS_PROFILE` 293 | - `aws_assume_role` block: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` obtained by calling `sts:AssumeRole` 294 | 295 | ```hcl-terraform 296 | resource "helmfile_release_set" "mystack" { 297 | aws_region = var.region 298 | aws_profile = var.profile 299 | aws_assume_role { 300 | role_arn = "arn:aws:iam::${var.account_id}:role/${var.role_name}" 301 | } 302 | // snip 303 | ``` 304 | 305 | Those environment variables go from the provider to `helmfile`, `helm`, `client-go`, and finally the aws exec 306 | credentials provider that reads the environment variables to call `aws eks get-token` which internally calls 307 | `sts:GetCallerIdentity`(e.g. `aws sts get-caller-identity`) for authentication. 308 | 309 | See https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html for more information on how authentication 310 | works on EKS. 311 | 312 | ## Develop 313 | If you wish to build this yourself, follow the instructions: 314 | 315 | cd terraform-provider-helmfile 316 | go build 317 | 318 | ## Acknowledgement 319 | 320 | The implementation of this product is highly inspired from [terraform-provider-shell](https://github.com/scottwinkler/terraform-provider-shell). A lot of thanks to the author! 321 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /helmfile.yaml 2 | /kubeconfig 3 | /values.yaml 4 | /test.tf 5 | temp.values-*.yaml 6 | /terraform.tfstate* 7 | /helmfile-*.yaml 8 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | TF ?= terraform-v0.12.29 2 | TEST_OUT_DIR ?= testout/$(shell cat USE | tr -d '\n' || echo current) 3 | TF_LOG ?= TRACE 4 | 5 | .PHONY: dev 6 | dev: 7 | rm tf.log tf.*.log || true 8 | rm -rf ./tf.*.pprof || true 9 | kind get kubeconfig > kubeconfig 10 | make testdata/01-bootstrap init plan apply 11 | make testdata/02-more-resources plan apply 12 | make testdata/03-add-prom plan apply 13 | make testdata/04-issue50 plan apply 14 | make destroy 15 | 16 | .PHONY: init 17 | init: 18 | cd .. ; make build 19 | mv ../dist/darwin_amd64/terraform-provider-helmfile .terraform/plugins/darwin_amd64/terraform-provider-helmfile 20 | $(TF) init 21 | 22 | .PHONY: testout 23 | testout: 24 | mkdir -p $(TEST_OUT_DIR) 25 | 26 | .PHONY: apply 27 | apply: testout 28 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/apply.log time -l $(TF) apply -auto-approve 29 | 30 | .PHONY: destroy 31 | destroy: testout 32 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/destroy.log time -l $(TF) destroy -auto-approve 33 | 34 | .PHONY: plan 35 | plan: testout 36 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/plan.log time -l $(TF) plan 37 | 38 | STEPS = $(wildcard testdata/*) 39 | 40 | .PHONY: $(STEPS) 41 | .SECONDEXPANSION: 42 | $(STEPS):%:$$(wildcard %/*.yaml) 43 | @echo Preparing $@ 44 | cp $@/*.{tf,yaml} . 45 | @echo $(shell basename $@) > USE 46 | -------------------------------------------------------------------------------- /examples/importing-existing-helmfile-managed-releases/Makefile: -------------------------------------------------------------------------------- 1 | TF ?= terraform-v0.12.29 2 | TEST_OUT_DIR ?= testout 3 | TF_LOG ?= TRACE 4 | 5 | .PHONY: dev 6 | dev: 7 | rm tf.log tf.*.log || true 8 | rm -rf ./tf.*.pprof || true 9 | rm -rf terraform.tfstate 10 | make init import plan apply destroy 11 | 12 | .PHONY: init 13 | init: 14 | cd ../.. ; make build 15 | mkdir -p .terraform/plugins/darwin_amd64 16 | mv ../../dist/darwin_amd64/terraform-provider-helmfile .terraform/plugins/darwin_amd64/terraform-provider-helmfile 17 | $(TF) init 18 | 19 | .PHONY: testout 20 | testout: 21 | mkdir -p $(TEST_OUT_DIR) 22 | 23 | .PHONY: apply 24 | apply: testout 25 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/apply.log time -l $(TF) apply -auto-approve 26 | 27 | .PHONY: destroy 28 | destroy: testout 29 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/destroy.log time -l $(TF) destroy -auto-approve 30 | 31 | .PHONY: plan 32 | plan: testout 33 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/plan.log time -l $(TF) plan 34 | 35 | .PHONY: import 36 | import: testout 37 | TF_LOG=$(TF_LOG) TF_LOG_PATH=$(TEST_OUT_DIR)/import.log time -l $(TF) import helmfile_release_set.myapps helmfile.yaml 38 | -------------------------------------------------------------------------------- /examples/importing-existing-helmfile-managed-releases/README.md: -------------------------------------------------------------------------------- 1 | ## Importing existing Helmfile project into Terraform 2 | 3 | Let's say you have an existing releases that are managed by helmfile and `helmfile.yaml`: 4 | 5 | ``` 6 | $ helmfile -f helmfile.yaml apply 7 | ``` 8 | 9 | You can migrate the releases into your terraform project by using `terraform import`. 10 | 11 | First, you edit your .tf file to add a `helmfile_release_set`: 12 | 13 | ```hcl-terraform 14 | resource "helmfile_release_set" "myapps" { 15 | content = file("./helmfile.yaml") 16 | } 17 | ``` 18 | 19 | Run `terraform import` with the path to `helmfile.yaml` as the last argument: 20 | 21 | ``` 22 | $ terraform import helmfile_release_set.myapps ./helmfile.yaml 23 | ``` 24 | 25 | Run `terraform plan`: 26 | 27 | ``` 28 | $ terraform plan 29 | 30 | helmfile_release_set.myapps: Refreshing state... [id=btmjojkllhcl73m9no0g] 31 | 32 | ------------------------------------------------------------------------ 33 | 34 | No changes. Infrastructure is up-to-date. 35 | 36 | This means that Terraform did not detect any differences between your 37 | configuration and real physical resources that exist. As a result, no 38 | actions need to be performed. 39 | ``` 40 | 41 | Ensure that there's no diff shown in the plan result. 42 | 43 | If there's any, you should retry updating `resource "helmfile_releaset_set" "myapps"` in your .tf file and rerunning `terraform plan` until there is no diff anymore. 44 | -------------------------------------------------------------------------------- /examples/importing-existing-helmfile-managed-releases/helmfile.yaml: -------------------------------------------------------------------------------- 1 | repositories: 2 | - name: sp 3 | url: https://stefanprodan.github.io/podinfo 4 | 5 | releases: 6 | - name: podinfo 7 | chart: sp/podinfo 8 | -------------------------------------------------------------------------------- /examples/importing-existing-helmfile-managed-releases/main.tf: -------------------------------------------------------------------------------- 1 | provider "helmfile" {} 2 | 3 | resource "helmfile_release_set" "myapps" { 4 | content = file("./helmfile.yaml") 5 | } 6 | -------------------------------------------------------------------------------- /examples/issue50/scenario1/platform/helmfile.yaml: -------------------------------------------------------------------------------- 1 | repositories: 2 | - name: flagger 3 | url: https://flagger.app 4 | 5 | releases: 6 | - name: issue50-scenario1 7 | chart: flagger/podinfo 8 | values: 9 | - foo: bar 10 | -------------------------------------------------------------------------------- /examples/issue50/scenario2/platform/helmfile.d/helmfile.yaml: -------------------------------------------------------------------------------- 1 | repositories: 2 | - name: flagger 3 | url: https://flagger.app 4 | 5 | releases: 6 | - name: issue50-scenario2 7 | chart: flagger/podinfo 8 | values: 9 | - foo: bar 10 | -------------------------------------------------------------------------------- /examples/temp.values.yaml: -------------------------------------------------------------------------------- 1 | {"name":"myapp"} -------------------------------------------------------------------------------- /examples/testdata/01-bootstrap/helmfile.yaml: -------------------------------------------------------------------------------- 1 | repositories: 2 | - name: flagger 3 | url: https://flagger.app 4 | 5 | releases: 6 | - name: {{ .Values.name }}-{{ requiredEnv "FOO" }} 7 | chart: flagger/podinfo 8 | values: 9 | - values.yaml 10 | labels: 11 | labelkey1: value1 12 | labelkey2: value2 13 | -------------------------------------------------------------------------------- /examples/testdata/01-bootstrap/test.tf: -------------------------------------------------------------------------------- 1 | provider "helmfile" {} 2 | 3 | resource "helmfile_release_set" "mystack" { 4 | content = file("./helmfile.yaml") 5 | 6 | helm_binary = "helm3" 7 | 8 | version = "0.135.0" 9 | 10 | working_directory = path.module 11 | 12 | environment = "default" 13 | 14 | environment_variables = { 15 | FOO = "foo" 16 | } 17 | 18 | values = [ 19 | < github.com/mumoshu/gofish v0.13.1-0.20200908033248-ab2d494fb15c 17 | 18 | replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 19 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 11 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 12 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 13 | github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= 14 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 15 | github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= 16 | github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= 17 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 18 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 19 | github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= 20 | github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 21 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= 22 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= 23 | github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= 24 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 25 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 26 | github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6 h1:bZ28Hqta7TFAK3Q08CMvv8y3/8ATaEqv2nGoc6yff6c= 27 | github.com/andybalholm/brotli v0.0.0-20190621154722-5f990b63d2d6/go.mod h1:+lx6/Aqd1kLJ1GQfkvOnaZ1WGmLpMpbprPuIOOZX30U= 28 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 29 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 30 | github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= 31 | github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= 32 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA= 33 | github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= 34 | github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= 35 | github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= 36 | github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs= 37 | github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 38 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 39 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 40 | github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= 41 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 42 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 43 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 44 | github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= 45 | github.com/aws/aws-sdk-go v1.19.39/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 46 | github.com/aws/aws-sdk-go v1.34.16/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= 47 | github.com/aws/aws-sdk-go v1.38.35 h1:7AlAO0FC+8nFjxiGKEmq0QLpiA8/XFr6eIxgRTwkdTg= 48 | github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 49 | github.com/bacongobbler/browser v1.1.0/go.mod h1:T9AaY4DSJ61FNgVTlCP/FWPrJ36TMRwI0Z18eLZ3IKI= 50 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 51 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 52 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= 53 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 54 | github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= 55 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 56 | github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= 57 | github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= 58 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 59 | github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= 60 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 61 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 62 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 63 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 64 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 65 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 66 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 67 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 68 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 69 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 70 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 71 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 72 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 73 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 74 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 75 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 76 | github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= 77 | github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= 78 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 79 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 80 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 81 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 82 | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 83 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 84 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 85 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 86 | github.com/frankban/quicktest v1.9.0 h1:jfEA+Psfr/pHsRJYPpHiNu7PGJnGctNxvTaM3K1EyXk= 87 | github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= 88 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 89 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 90 | github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 91 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 92 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 93 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 94 | github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= 95 | github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 96 | github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc= 97 | github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= 98 | github.com/go-git/go-git/v5 v5.1.0 h1:HxJn9g/E7eYvKW3Fm7Jt4ee8LXfPOm/H1cdDu8vEssk= 99 | github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= 100 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 101 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 102 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 103 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 104 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 105 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 106 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 107 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 108 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 109 | github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw= 110 | github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= 111 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 112 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 113 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 114 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 115 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 116 | github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= 117 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 118 | github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 119 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 120 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 121 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 122 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 123 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 124 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 125 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 126 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 127 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 128 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 129 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 130 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 131 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 132 | github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= 133 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 134 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 135 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 136 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 137 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 138 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 139 | github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= 140 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 141 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 142 | github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= 143 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 144 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 145 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 146 | github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 147 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 148 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 149 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 150 | github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= 151 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 152 | github.com/hashicorp/go-getter v1.4.0 h1:ENHNi8494porjD0ZhIrjlAHnveSFhY7hvOJrV/fsKkw= 153 | github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= 154 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 155 | github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= 156 | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 157 | github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= 158 | github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= 159 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 160 | github.com/hashicorp/go-plugin v1.0.1 h1:4OtAfUGbnKC6yS48p0CtMX2oFYtzFZVv6rok3cRWgnE= 161 | github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= 162 | github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= 163 | github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= 164 | github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= 165 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 166 | github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 167 | github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= 168 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 169 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 170 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= 171 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 172 | github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= 173 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 174 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 175 | github.com/hashicorp/hcl2 v0.0.0-20190821123243-0c888d1241f6 h1:JImQpEeUQ+0DPFMaWzLA0GdUNPaUlCXLpfiqkSZBUfc= 176 | github.com/hashicorp/hcl2 v0.0.0-20190821123243-0c888d1241f6/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0= 177 | github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590 h1:2yzhWGdgQUWZUCNK+AoO35V+HTsgEmcM4J9IkArh7PI= 178 | github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE= 179 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= 180 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 181 | github.com/hashicorp/terraform-config-inspect v0.0.0-20190821133035-82a99dc22ef4 h1:fTkL0YwjohGyN7AqsDhz6bwcGBpT+xBqi3Qhpw58Juw= 182 | github.com/hashicorp/terraform-config-inspect v0.0.0-20190821133035-82a99dc22ef4/go.mod h1:JDmizlhaP5P0rYTTZB0reDMefAiJyfWPEtugV4in1oI= 183 | github.com/hashicorp/terraform-plugin-sdk v1.0.0 h1:3AjuuV1LJKs1NlG+heUgqWN6/QCSx2kDhyS6K7F0fTw= 184 | github.com/hashicorp/terraform-plugin-sdk v1.0.0/go.mod h1:NuwtLpEpPsFaKJPJNGtMcn9vlhe6Ofe+Y6NqXhJgV2M= 185 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 186 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= 187 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 188 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 189 | github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= 190 | github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 191 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 192 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 193 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 194 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 195 | github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 196 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 197 | github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= 198 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 199 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 200 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 201 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 202 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 203 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 204 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 205 | github.com/k-kinzal/progressived v0.0.0-20200911065552-afe494a1cc18/go.mod h1:i0T4wJE7XGUUjW2nK2XxzWJYeP0UNipFcBMLDzJOrA4= 206 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= 207 | github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 208 | github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= 209 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 210 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 211 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 212 | github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY= 213 | github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 214 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 215 | github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= 216 | github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 217 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 218 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 219 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 220 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 221 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 222 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 223 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 224 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 225 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 226 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 227 | github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= 228 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 229 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 230 | github.com/kyokomi/emoji v2.2.2+incompatible h1:gaQFbK2+uSxOR4iGZprJAbpmtqTrHhSdgOyIMD6Oidc= 231 | github.com/kyokomi/emoji v2.2.2+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA= 232 | github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H5HrkJ5CsN56HQ= 233 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 234 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 235 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= 236 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 237 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 238 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 239 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 240 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 241 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 242 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 243 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 244 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 245 | github.com/mholt/archiver/v3 v3.3.0 h1:vWjhY8SQp5yzM9P6OJ/eZEkmi3UAbRrxCq48MxjAzig= 246 | github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao= 247 | github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= 248 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 249 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 250 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 251 | github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= 252 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 253 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 254 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 255 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 256 | github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb h1:GRiLv4rgyqjqzxbhJke65IYUf4NCOOvrPOJbV/sPxkM= 257 | github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM= 258 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 259 | github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= 260 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 261 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 262 | github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= 263 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 264 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 265 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 266 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 267 | github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= 268 | github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 269 | github.com/mumoshu/gofish v0.13.1-0.20200908033248-ab2d494fb15c h1:psoQG0FYSJesI3ZNL6FDBQVMuS+xj7weacaEJ51Jzi4= 270 | github.com/mumoshu/gofish v0.13.1-0.20200908033248-ab2d494fb15c/go.mod h1:+tioljxX31bBiVquRFxuofNwXHDqeeJZrCXfsRUX7ec= 271 | github.com/mumoshu/shoal v0.2.18 h1:aazA6O1oXAbkJqyJSWdrVLwnj1vETeLf4fGW2QCZs9A= 272 | github.com/mumoshu/shoal v0.2.18/go.mod h1:TjMI6WkJ20NAhdP7xvXoDfd7EFpeSOh6xI34HMLRrHo= 273 | github.com/mumoshu/terraform-provider-eksctl v0.16.1 h1:SJbSfOrPt0TrWzt42xMVJbAIojCU2Hfi3pcMbC3MhKI= 274 | github.com/mumoshu/terraform-provider-eksctl v0.16.1/go.mod h1:uxtKUTodgIp2x/3zdDclIUARvdpToY9/4nRYYR9AIzg= 275 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 276 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 277 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 278 | github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 279 | github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= 280 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 281 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 282 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 283 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 284 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 285 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 286 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 287 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 288 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 289 | github.com/pierrec/lz4 v2.5.1+incompatible h1:Yq0up0149Hh5Ekhm/91lgkZuD1ZDnXNM26bycpTzYBM= 290 | github.com/pierrec/lz4 v2.5.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 291 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 292 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 293 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 294 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 295 | github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= 296 | github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 297 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 298 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 299 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 300 | github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6DI= 301 | github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E= 302 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 303 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 304 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 305 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 306 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 307 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 308 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 309 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 310 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 311 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 312 | github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= 313 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 314 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 315 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 316 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 317 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 318 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 319 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 320 | github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= 321 | github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= 322 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 323 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 324 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 325 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 326 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 327 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 328 | github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= 329 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 330 | github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 331 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 332 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 333 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 334 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 335 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 336 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 337 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 338 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 339 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 340 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 341 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 342 | github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 343 | github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= 344 | github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= 345 | github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 346 | github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= 347 | github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 348 | github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= 349 | github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= 350 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 351 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 352 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 353 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 354 | github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs= 355 | github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8= 356 | github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0= 357 | github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= 358 | github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 359 | github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= 360 | github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= 361 | github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8= 362 | github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= 363 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 364 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 365 | go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= 366 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 367 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 368 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 369 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 370 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 371 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 372 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 373 | golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 374 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 375 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 376 | golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 377 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= 378 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 379 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 380 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 381 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 382 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 383 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 384 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 385 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 386 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 387 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 388 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 389 | golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 390 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 391 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 392 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 393 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 394 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 395 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 396 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 397 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 398 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 399 | golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 400 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 401 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 402 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 403 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 404 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 405 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 406 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 407 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= 408 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 409 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 410 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 411 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= 412 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 413 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 414 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 415 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 416 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 417 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= 418 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 419 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 420 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 421 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 422 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 423 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 424 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 425 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 426 | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 427 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 428 | golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 429 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 430 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 431 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 432 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 433 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 434 | golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 435 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 436 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 437 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 438 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 439 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 440 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 441 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 442 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 443 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 444 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 445 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 446 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 447 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 448 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 449 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 450 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 451 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 452 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 453 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 454 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 455 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 456 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 457 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 458 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 459 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 460 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 461 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 462 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 463 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 464 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 465 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 466 | google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= 467 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 468 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 469 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 470 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 471 | google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= 472 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 473 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 474 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 475 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 476 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 477 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 478 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 479 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 480 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 481 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 482 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 483 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 484 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 485 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 486 | google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= 487 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 488 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 489 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 490 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 491 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 492 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 493 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 494 | gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 495 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 496 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 497 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 498 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 499 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 500 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 501 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 502 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 503 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 504 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 505 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 506 | gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 507 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 508 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 509 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 510 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 511 | howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= 512 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 513 | -------------------------------------------------------------------------------- /issue50/scenario1/platform/helmfile.yaml/temp.values-484e91c5bf5320485228cc155bb7badb28b1c0e660723c28bc954d09f15ce715.yaml: -------------------------------------------------------------------------------- 1 | case: issue50_scenario1 2 | -------------------------------------------------------------------------------- /issue50/scenario2/platform/temp.values-673c7e32e5586887c39487b413038cdb58554ef15dd9c55edf2b8a713a68ddb0.yaml: -------------------------------------------------------------------------------- 1 | case: issue50_scenario2 2 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/plugin" 5 | "github.com/mumoshu/terraform-provider-helmfile/pkg/helmfile" 6 | "github.com/mumoshu/terraform-provider-helmfile/pkg/profile" 7 | ) 8 | 9 | func main() { 10 | defer profile.Start().Stop() 11 | 12 | plugin.Serve(&plugin.ServeOpts{ 13 | ProviderFunc: helmfile.Provider}) 14 | } 15 | -------------------------------------------------------------------------------- /pkg/helmfile/config.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 4 | 5 | type ProviderInstance struct { 6 | MaxDiffOutputLen int 7 | } 8 | 9 | func New(d *schema.ResourceData) *ProviderInstance { 10 | return &ProviderInstance{ 11 | MaxDiffOutputLen: d.Get(KeyMaxDiffOutputLen).(int), 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/helmfile/context.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk" 5 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk/api" 6 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk/tfsdk" 7 | ) 8 | 9 | func newContext(d api.Getter) *sdk.Context { 10 | conf := tfsdk.ConfigFromResourceData(d, 11 | tfsdk.SchemaOptionAWSRegionKey(KeyAWSRegion), 12 | tfsdk.SchemaOptionAWSProfileKey(KeyAWSProfile), 13 | tfsdk.SchemaOptionAWSAssumeRole(KeyAWSAssumeRole), 14 | ) 15 | 16 | ctx := sdk.ContextConfig(conf) 17 | 18 | return ctx 19 | } 20 | -------------------------------------------------------------------------------- /pkg/helmfile/hash.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davecgh/go-spew/spew" 6 | "hash/fnv" 7 | ) 8 | 9 | func HashObject(obj interface{}) (string, error) { 10 | hash := fnv.New32a() 11 | 12 | hash.Reset() 13 | 14 | printer := spew.ConfigState{ 15 | Indent: " ", 16 | SortKeys: true, 17 | DisableMethods: true, 18 | SpewKeys: true, 19 | } 20 | printer.Fprintf(hash, "%#v", obj) 21 | 22 | sum := fmt.Sprint(hash.Sum32()) 23 | 24 | return SafeEncodeString(sum), nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/helmfile/logging.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | ) 8 | 9 | func dump(s string, entries []map[string]interface{}) { 10 | j, _ := json.Marshal(entries) 11 | if j == nil { 12 | j = []byte{} 13 | } 14 | log.Printf("DUMP[%s]: %s", s, string(j)) 15 | } 16 | 17 | func logf(msg string, args ...interface{}) { 18 | ppid := os.Getppid() 19 | pid := os.Getpid() 20 | log.Printf("[DEBUG] helmfile-provider(pid=%d,ppid=%d): "+msg, append([]interface{}{pid, ppid}, args...)...) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/helmfile/provider.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "github.com/hashicorp/terraform-plugin-sdk/helper/mutexkv" 5 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 6 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 7 | ) 8 | 9 | const ( 10 | KeyMaxDiffOutputLen = "max_diff_output_len" 11 | ) 12 | 13 | // Provider returns a terraform.ResourceProvider. 14 | func Provider() terraform.ResourceProvider { 15 | return &schema.Provider{ 16 | Schema: map[string]*schema.Schema{ 17 | KeyMaxDiffOutputLen: { 18 | Type: schema.TypeInt, 19 | Optional: true, 20 | ForceNew: false, 21 | Default: 4096, 22 | }, 23 | }, 24 | ResourcesMap: map[string]*schema.Resource{ 25 | "helmfile_release_set": resourceHelmfileReleaseSet(), 26 | "helmfile_release": resourceHelmfileRelease(), 27 | "helmfile_embedding_example": resourceHelmfileEmbeddingExample(), 28 | }, 29 | ConfigureFunc: providerConfigure, 30 | } 31 | } 32 | 33 | func providerConfigure(d *schema.ResourceData) (interface{}, error) { 34 | return New(d), nil 35 | } 36 | 37 | // This is a global MutexKV for use within this plugin. 38 | var mutexKV = mutexkv.NewMutexKV() 39 | -------------------------------------------------------------------------------- /pkg/helmfile/provider_test.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 7 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 8 | ) 9 | 10 | var testAccProviders map[string]terraform.ResourceProvider 11 | var testAccProvider *schema.Provider 12 | 13 | func init() { 14 | testAccProvider = Provider().(*schema.Provider) 15 | testAccProviders = map[string]terraform.ResourceProvider{ 16 | "helmfile": testAccProvider, 17 | } 18 | } 19 | 20 | func TestProvider(t *testing.T) { 21 | if err := Provider().(*schema.Provider).InternalValidate(); err != nil { 22 | t.Fatalf("err: %s", err) 23 | } 24 | } 25 | 26 | func TestProvider_impl(t *testing.T) { 27 | var _ terraform.ResourceProvider = Provider() 28 | } 29 | 30 | func testAccPreCheck(t *testing.T) { 31 | 32 | } 33 | -------------------------------------------------------------------------------- /pkg/helmfile/readwrite.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 4 | 5 | type ResourceRead interface { 6 | Id() string 7 | Get(string) interface{} 8 | } 9 | 10 | type ResourceReadWrite interface { 11 | ResourceRead 12 | Set(string, interface{}) error 13 | } 14 | 15 | type ResourceReadWriteEmbedded struct { 16 | m map[string]interface{} 17 | } 18 | 19 | func (m *ResourceReadWriteEmbedded) Id() string { 20 | return "" 21 | } 22 | 23 | func (m *ResourceReadWriteEmbedded) Get(k string) interface{} { 24 | return m.m[k] 25 | } 26 | 27 | func (m *ResourceReadWriteEmbedded) Set(k string, v interface{}) error { 28 | m.m[k] = v 29 | return nil 30 | } 31 | 32 | type ResourceReadWriteDiff struct { 33 | *schema.ResourceDiff 34 | } 35 | 36 | func (d *ResourceReadWriteDiff) Set(key string, value interface{}) error { 37 | return d.SetNew(key, value) 38 | } 39 | 40 | func resourceDiffToFields(d *schema.ResourceDiff) ResourceReadWrite { 41 | return &ResourceReadWriteDiff{ 42 | ResourceDiff: d, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/helmfile/release.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | type Release struct { 4 | Name string 5 | Namespace string 6 | Chart string 7 | Version string 8 | Values []interface{} 9 | WorkingDirectory string 10 | Verify bool 11 | Wait bool 12 | Force bool 13 | Atomic bool 14 | CleanupOnFail bool 15 | Timeout int 16 | Kubeconfig string 17 | Kubecontext string 18 | Bin string 19 | HelmBin string 20 | DiffOutput string 21 | ApplyOutput string 22 | } 23 | 24 | func NewRelease(d ResourceRead) *Release { 25 | f := Release{} 26 | f.Namespace = d.Get(KeyNamespace).(string) 27 | f.Name = d.Get(KeyName).(string) 28 | if f.Name == "" { 29 | f.Name = d.Id() 30 | } 31 | f.Chart = d.Get(KeyChart).(string) 32 | f.Version = d.Get(KeyVersion).(string) 33 | f.Values = d.Get(KeyValues).([]interface{}) 34 | f.WorkingDirectory = d.Get(KeyWorkingDirectory).(string) 35 | f.Verify = d.Get(KeyVerify).(bool) 36 | f.Wait = d.Get(KeyWait).(bool) 37 | f.Force = d.Get(KeyForce).(bool) 38 | f.Atomic = d.Get(KeyAtomic).(bool) 39 | f.CleanupOnFail = d.Get(KeyCleanupOnFail).(bool) 40 | f.Timeout = d.Get(KeyTimeout).(int) 41 | f.Kubeconfig = d.Get(KeyKubeconfig).(string) 42 | f.Kubecontext = d.Get(KeyKubecontext).(string) 43 | f.Bin = d.Get(KeyBin).(string) 44 | f.HelmBin = d.Get(KeyHelmBin).(string) 45 | f.DiffOutput = d.Get(KeyDiffOutput).(string) 46 | f.ApplyOutput = d.Get(KeyApplyOutput).(string) 47 | return &f 48 | } 49 | -------------------------------------------------------------------------------- /pkg/helmfile/release_set.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/sha256" 7 | "fmt" 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 9 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk" 10 | "golang.org/x/xerrors" 11 | "io/ioutil" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "strconv" 16 | "strings" 17 | 18 | "github.com/Masterminds/semver" 19 | ) 20 | 21 | type ReleaseSet struct { 22 | Bin string 23 | Values []interface{} 24 | ValuesFiles []interface{} 25 | HelmBin string 26 | Content string 27 | DiffOutput string 28 | ApplyOutput string 29 | Environment string 30 | TmpHelmFilePath string 31 | 32 | // Selector is a helmfile label selector that is a AND list of label key-value pairs 33 | Selector map[string]interface{} 34 | 35 | // Selectors is a OR list of selectors 36 | Selectors []interface{} 37 | 38 | EnvironmentVariables map[string]interface{} 39 | WorkingDirectory string 40 | ReleasesValues map[string]interface{} 41 | 42 | // Kubeconfig is the file path to kubeconfig which is set to the KUBECONFIG environment variable on running helmfile 43 | Kubeconfig string 44 | 45 | Concurrency int 46 | 47 | // Version is the version number or the semver version range for the helmfile version to use 48 | Version string 49 | 50 | // HelmVersion is the version number or the semver version range for the helm version to use 51 | HelmVersion string 52 | HelmDiffVersion string 53 | 54 | // SkipDiffOnMissingFiles is the list of local files. Any file contained in the list but missing on the file system 55 | // result in the provider to skip running `helmfile-diff`. Use with Terraform's `depends_on`, so that 56 | // you can let another dependent Terraform resource to created required files like kubeconfig or Helmfile values 57 | // files to be used by the helmfile_release_set. 58 | // 59 | // See https://github.com/mumoshu/terraform-provider-helmfile/issues/38 for more information on expected use-cases. 60 | SkipDiffOnMissingFiles []string 61 | } 62 | 63 | func NewReleaseSet(d ResourceRead) (*ReleaseSet, error) { 64 | f := ReleaseSet{} 65 | 66 | // environment defaults to "" for helmfile_release_set but it's always nil for helmfile_release. 67 | // This nil-check is required to handle the latter case. Otherwise it ends up with: 68 | // panic: interface conversion: interface {} is nil, not string 69 | if env := d.Get(KeyEnvironment); env != nil { 70 | f.Environment = env.(string) 71 | } 72 | 73 | if content := d.Get(KeyContent); content != nil { 74 | f.Content = content.(string) 75 | } 76 | 77 | f.DiffOutput = d.Get(KeyDiffOutput).(string) 78 | f.ApplyOutput = d.Get(KeyApplyOutput).(string) 79 | f.HelmBin = d.Get(KeyHelmBin).(string) 80 | 81 | if selector := d.Get(KeySelector); selector != nil { 82 | f.Selector = selector.(map[string]interface{}) 83 | } 84 | 85 | if selectors := d.Get(KeySelectors); selectors != nil { 86 | for _, s := range selectors.([]interface{}) { 87 | f.Selectors = append(f.Selectors, s) 88 | } 89 | } 90 | 91 | if valuesFiles := d.Get(KeyValuesFiles); valuesFiles != nil { 92 | f.ValuesFiles = valuesFiles.([]interface{}) 93 | } 94 | 95 | if vs := d.Get(KeySkipDiffOnMissingFiles); vs != nil { 96 | var ss []string 97 | 98 | for _, v := range vs.([]interface{}) { 99 | ss = append(ss, v.(string)) 100 | } 101 | 102 | f.SkipDiffOnMissingFiles = ss 103 | } 104 | 105 | f.Values = d.Get(KeyValues).([]interface{}) 106 | f.ReleasesValues = d.Get(KeyReleasesValues).(map[string]interface{}) 107 | f.Bin = d.Get(KeyBin).(string) 108 | f.WorkingDirectory = d.Get(KeyWorkingDirectory).(string) 109 | 110 | f.Kubeconfig = d.Get(KeyKubeconfig).(string) 111 | 112 | f.Version = d.Get(KeyVersion).(string) 113 | f.HelmVersion = d.Get(KeyHelmVersion).(string) 114 | f.HelmDiffVersion = d.Get(KeyHelmDiffVersion).(string) 115 | 116 | logf("Printing raw working directory for %q: %s", d.Id(), f.WorkingDirectory) 117 | 118 | if environmentVariables := d.Get(KeyEnvironmentVariables); environmentVariables != nil { 119 | f.EnvironmentVariables = environmentVariables.(map[string]interface{}) 120 | } 121 | 122 | if concurrency := d.Get(KeyConcurrency); concurrency != nil { 123 | f.Concurrency = concurrency.(int) 124 | } 125 | return &f, nil 126 | } 127 | 128 | func NewCommandWithKubeconfig(fs *ReleaseSet, args ...string) (*exec.Cmd, error) { 129 | if fs.WorkingDirectory != "" { 130 | if err := os.MkdirAll(fs.WorkingDirectory, 0755); err != nil { 131 | return nil, fmt.Errorf("creating working directory %q: %w", fs.WorkingDirectory, err) 132 | } 133 | } 134 | 135 | bs := []byte(fs.Content) 136 | first := sha256.New() 137 | first.Write(bs) 138 | fs.TmpHelmFilePath = fmt.Sprintf("helmfile-%x.yaml", first.Sum(nil)) 139 | if err := ioutil.WriteFile(filepath.Join(fs.WorkingDirectory, fs.TmpHelmFilePath), bs, 0700); err != nil { 140 | return nil, err 141 | } 142 | 143 | flags := []string{ 144 | "--file", fs.TmpHelmFilePath, 145 | "--no-color", 146 | } 147 | 148 | helmfileBin, helmBin, err := prepareBinaries(fs) 149 | if err != nil { 150 | return nil, err 151 | } 152 | 153 | if *helmBin != "" { 154 | flags = append(flags, "--helm-binary", *helmBin) 155 | } 156 | 157 | if fs.Environment != "" { 158 | flags = append(flags, "--environment", fs.Environment) 159 | } 160 | 161 | for k, v := range fs.Selector { 162 | flags = append(flags, "--selector", fmt.Sprintf("%s=%s", k, v)) 163 | } 164 | 165 | for _, selector := range fs.Selectors { 166 | flags = append(flags, "--selector", fmt.Sprintf("%s", selector)) 167 | } 168 | 169 | for _, f := range fs.ValuesFiles { 170 | flags = append(flags, "--state-values-file", fmt.Sprintf("%v", f)) 171 | } 172 | for _, vs := range fs.Values { 173 | js := []byte(fmt.Sprintf("%s", vs)) 174 | 175 | first := sha256.New() 176 | first.Write(js) 177 | 178 | relpath := filepath.Join( 179 | fs.WorkingDirectory, 180 | fmt.Sprintf("temp.values-%x.yaml", first.Sum(nil)), 181 | ) 182 | 183 | abspath, err := filepath.Abs(relpath) 184 | if err != nil { 185 | return nil, xerrors.Errorf("getting absolute path to %s: %w", abspath, err) 186 | } 187 | 188 | if err := ioutil.WriteFile(abspath, js, 0700); err != nil { 189 | return nil, err 190 | } 191 | 192 | flags = append(flags, "--state-values-file", abspath) 193 | } 194 | 195 | flags = append(flags, args...) 196 | 197 | logf("Running helmfile %s on %+v", strings.Join(flags, " "), *fs) 198 | 199 | cmd := exec.Command(*helmfileBin, flags...) 200 | cmd.Dir = fs.WorkingDirectory 201 | cmd.Env = append(os.Environ(), readEnvironmentVariables(fs.EnvironmentVariables, "KUBECONFIG")...) 202 | 203 | if kubeconfig, err := getKubeconfig(fs); err != nil { 204 | return nil, fmt.Errorf("creating command: %w", err) 205 | } else if *kubeconfig != "" { 206 | cmd.Env = append(cmd.Env, "KUBECONFIG="+*kubeconfig) 207 | } else { 208 | return nil, fmt.Errorf("[BUG] NewCommandWithKubeconfig must not be called with empty kubeconfig path. args = %s", strings.Join(args, " ")) 209 | } 210 | 211 | logf("[DEBUG] Generated command: wd = %s, args = %s", fs.WorkingDirectory, strings.Join(cmd.Args, " ")) 212 | return cmd, nil 213 | } 214 | 215 | func getKubeconfig(fs *ReleaseSet) (*string, error) { 216 | var rel string 217 | 218 | att := fs.Kubeconfig 219 | 220 | var env string 221 | 222 | if v, ok := fs.EnvironmentVariables["KUBECONFIG"]; ok { 223 | env = v.(string) 224 | } 225 | 226 | if att != "" { 227 | if env != "" { 228 | return nil, fmt.Errorf("validating release set: helmfile_release_set.environment_variables.KUBECONFIG cannot be set with helmfile_release_set.kubeconfig") 229 | } 230 | 231 | rel = att 232 | } else { 233 | rel = env 234 | } 235 | 236 | abs, err := filepath.Abs(rel) 237 | if err != nil { 238 | return nil, xerrors.Errorf("determining absolute path for kubeconfig path %s: %w", rel, err) 239 | } 240 | 241 | return &abs, nil 242 | } 243 | 244 | func CreateReleaseSet(ctx *sdk.Context, fs *ReleaseSet, d ResourceReadWrite) error { 245 | logf("[DEBUG] Creating release set resource...") 246 | 247 | diffFile, err := getDiffFile(ctx, fs) 248 | if err != nil { 249 | return fmt.Errorf("getting diff file: %w", err) 250 | } 251 | 252 | defer func() { 253 | if _, err := os.Stat(diffFile); err == nil { 254 | if err := os.Remove(diffFile); err != nil { 255 | logf("Failed cleaning diff file: %v", err) 256 | } 257 | } 258 | }() 259 | 260 | args := []string{ 261 | "apply", 262 | "--concurrency", strconv.Itoa(fs.Concurrency), 263 | "--suppress-secrets", 264 | } 265 | 266 | additionalArgs, err := getAdditionalHelmfileApplyFlags(newContext(d), fs) 267 | if err != nil { 268 | return err 269 | } 270 | 271 | args = append(args, additionalArgs...) 272 | 273 | for k, v := range fs.ReleasesValues { 274 | args = append(args, "--set", fmt.Sprintf("%s=%s", k, v)) 275 | } 276 | 277 | cmd, err := NewCommandWithKubeconfig(fs, args...) 278 | if err != nil { 279 | return err 280 | } 281 | defer os.Remove(fs.TmpHelmFilePath) 282 | 283 | //obtain exclusive lock 284 | mutexKV.Lock(fs.WorkingDirectory) 285 | defer mutexKV.Unlock(fs.WorkingDirectory) 286 | 287 | state := NewState() 288 | st, err := runCommand(ctx, cmd, state, false) 289 | if err != nil { 290 | return fmt.Errorf("running helmfile-apply: %w", err) 291 | } 292 | 293 | d.Set(KeyApplyOutput, st.Output) 294 | //SetDiffOutput(d, "") 295 | 296 | return nil 297 | } 298 | 299 | func ReadReleaseSet(ctx *sdk.Context, fs *ReleaseSet, d ResourceReadWrite) error { 300 | logf("[DEBUG] Reading release set resource...") 301 | 302 | // We treat diff_output as always empty, to show `helmfile diff` output as a complete diff, 303 | // rather than a diff of diffs. 304 | // 305 | // `terraform plan` shows diff on diff_output between the value after Read and CustomizeDiff. 306 | // So we set it empty here, in terraform resource's Read, 307 | // and set it non-empty later, in terraform resource's CustomizeDiff. 308 | // This way, terraform shows the diff between an empty string and non-empty string(full helmfile diff output), 309 | // which gives us what we want. 310 | // 311 | // Note that just emptying diff_output on storing it to the terraform state in StateFunc doesn't work. 312 | // StateFunc is called after Read and CustomizeDiff, which results in terraform showing diff of 313 | // an empty string against an empty string, which is ovbiously not what we want. 314 | d.Set(KeyDiffOutput, "") 315 | d.Set(KeyApplyOutput, "") 316 | 317 | if fs.Kubeconfig == "" { 318 | logf("Skipping helmfile-build due to that kubeconfig is empty, which means that this operation has been called on a helmfile resource that depends on in-existent resource") 319 | 320 | return nil 321 | } 322 | 323 | // We run `helmfile build` against the state BEFORE the planned change, 324 | // to make sure any error in helmfile.yaml before successful apply is shown to the user. 325 | _, err := runBuild(ctx, fs) 326 | if err != nil { 327 | logf("[DEBUG] Build error detected: %v", err) 328 | 329 | return nil 330 | } 331 | 332 | //d.Set(KeyDiffOutput, state.Output) 333 | 334 | return nil 335 | } 336 | 337 | func runBuild(ctx *sdk.Context, fs *ReleaseSet, flags ...string) (*State, error) { 338 | args := []string{ 339 | "build", 340 | } 341 | 342 | args = append(args, flags...) 343 | 344 | cmd, err := NewCommandWithKubeconfig(fs, args...) 345 | if err != nil { 346 | return nil, err 347 | } 348 | defer os.Remove(fs.TmpHelmFilePath) 349 | 350 | //obtain exclusive lock 351 | mutexKV.Lock(fs.WorkingDirectory) 352 | defer mutexKV.Unlock(fs.WorkingDirectory) 353 | 354 | state := NewState() 355 | return runCommand(ctx, cmd, state, false) 356 | } 357 | 358 | func getHelmfileVersion(ctx *sdk.Context, fs *ReleaseSet) (*semver.Version, error) { 359 | args := []string{ 360 | "version", 361 | } 362 | 363 | cmd, err := NewCommandWithKubeconfig(fs, args...) 364 | if err != nil { 365 | return nil, fmt.Errorf("creating command: %w", err) 366 | } 367 | defer os.Remove(fs.TmpHelmFilePath) 368 | 369 | //obtain exclusive lock 370 | mutexKV.Lock(fs.WorkingDirectory) 371 | defer mutexKV.Unlock(fs.WorkingDirectory) 372 | 373 | state := NewState() 374 | st, err := runCommand(ctx, cmd, state, false) 375 | if err != nil { 376 | return nil, fmt.Errorf("running command: %w", err) 377 | } 378 | 379 | splits := strings.Split(strings.TrimSpace(st.Output), " ") 380 | 381 | versionPart := strings.TrimLeft(splits[len(splits)-1], "v") 382 | 383 | v, err := semver.NewVersion(versionPart) 384 | 385 | if err != nil { 386 | logf("Failed to parse %q as semver: %v", versionPart, err) 387 | } 388 | 389 | return v, nil 390 | } 391 | 392 | func runTemplate(ctx *sdk.Context, fs *ReleaseSet) (*State, error) { 393 | args := []string{ 394 | "template", 395 | } 396 | 397 | cmd, err := NewCommandWithKubeconfig(fs, args...) 398 | if err != nil { 399 | return nil, err 400 | } 401 | defer os.Remove(fs.TmpHelmFilePath) 402 | 403 | //obtain exclusive lock 404 | mutexKV.Lock(fs.WorkingDirectory) 405 | defer mutexKV.Unlock(fs.WorkingDirectory) 406 | 407 | state := NewState() 408 | return runCommand(ctx, cmd, state, false) 409 | } 410 | 411 | type DiffConfig struct { 412 | DryRun bool 413 | Kubeconfig string 414 | MaxDiffOutputLen int 415 | } 416 | 417 | type DiffOption func(*DiffConfig) 418 | 419 | func WithDiffConfig(c DiffConfig) DiffOption { 420 | return func(p *DiffConfig) { 421 | *p = c 422 | } 423 | } 424 | 425 | func runDiff(ctx *sdk.Context, fs *ReleaseSet, conf DiffConfig) (*State, error) { 426 | args := []string{ 427 | "diff", 428 | "--concurrency", strconv.Itoa(fs.Concurrency), 429 | "--detailed-exitcode", 430 | "--suppress-secrets", 431 | "--context", "3", 432 | } 433 | 434 | for k, v := range fs.ReleasesValues { 435 | args = append(args, "--set", fmt.Sprintf("%s=%s", k, v)) 436 | } 437 | 438 | if conf.DryRun { 439 | args = append(args, "--dry-run") 440 | } 441 | 442 | cmd, err := NewCommandWithKubeconfig(fs, args...) 443 | if err != nil { 444 | return nil, err 445 | } 446 | defer os.Remove(fs.TmpHelmFilePath) 447 | 448 | // Use the stable directory for storing temporary charts and values files 449 | // so that helmfile-diff output becomes stables and terraform plan doesn't break. 450 | // See https://github.com/roboll/helmfile/pull/1622 451 | 452 | hash, err := HashObject(fs) 453 | if err != nil { 454 | return nil, xerrors.Errorf("computing hash of object: %w", err) 455 | } 456 | 457 | relpath := filepath.Join(".terraform", "helmfile", fmt.Sprintf("temp-%s", hash)) 458 | 459 | abspath, err := filepath.Abs(relpath) 460 | if err != nil { 461 | return nil, xerrors.Errorf("getting absolute path to %s: %w", relpath) 462 | } 463 | 464 | if info, _ := os.Stat(abspath); info != nil { 465 | if err := os.RemoveAll(abspath); err != nil { 466 | return nil, xerrors.Errorf("removing stable temp directory %s: %w", abspath, err) 467 | } 468 | } 469 | 470 | if err := os.MkdirAll(abspath, 0755); err != nil { 471 | return nil, xerrors.Errorf("creating temp directory for helmfile and chartify %s: %w", abspath, err) 472 | } 473 | defer os.Remove(abspath) 474 | 475 | cmd.Env = append(cmd.Env, "HELMFILE_TEMPDIR="+abspath) 476 | cmd.Env = append(cmd.Env, "CHARTIFY_TEMPDIR="+abspath) 477 | 478 | if conf.Kubeconfig != "" { 479 | cmd.Env = append(cmd.Env, "KUBECONFIG="+conf.Kubeconfig) 480 | } 481 | 482 | //obtain exclusive lock 483 | mutexKV.Lock(fs.WorkingDirectory) 484 | defer mutexKV.Unlock(fs.WorkingDirectory) 485 | 486 | state := NewState() 487 | diff, err := runCommand(ctx, cmd, state, true) 488 | if err != nil { 489 | return nil, fmt.Errorf("running command: %w", err) 490 | } 491 | 492 | return diff, nil 493 | } 494 | 495 | func getAdditionalHelmfileApplyFlags(ctx *sdk.Context, fs *ReleaseSet) ([]string, error) { 496 | helmfileVersion, err := getHelmfileVersion(ctx, fs) 497 | if err != nil { 498 | return nil, fmt.Errorf("getting helmfile version: %w", err) 499 | } 500 | 501 | // See https://github.com/roboll/helmfile/pull/1618 for --skip-diff-on-install 502 | 503 | gt136, err := semver.NewConstraint(">= 0.136.0") 504 | if err != nil { 505 | return nil, err 506 | } 507 | 508 | var args []string 509 | 510 | if helmfileVersion != nil && gt136.Check(helmfileVersion) { 511 | args = append(args, "--skip-diff-on-install") 512 | } 513 | 514 | return args, nil 515 | } 516 | 517 | func getDiffFile(ctx *sdk.Context, fs *ReleaseSet) (string, error) { 518 | helmfileVersion, err := getHelmfileVersion(ctx, fs) 519 | if err != nil { 520 | return "", fmt.Errorf("getting helmfile version: %w", err) 521 | } 522 | 523 | gte126, err := semver.NewConstraint(">= 0.126.0") 524 | if err != nil { 525 | return "", err 526 | } 527 | 528 | var determinisiticOutput string 529 | 530 | if helmfileVersion != nil && gte126.Check(helmfileVersion) { 531 | logf("Detected Helmfile version greater than 0.126.0(=%s). Using `helmfile build --embed-values` to compute the unique ID of the desired state.", helmfileVersion) 532 | build, err := runBuild(ctx, fs, "--embed-values") 533 | if err != nil { 534 | return "", fmt.Errorf("running helmfile build: %w", err) 535 | } 536 | 537 | determinisiticOutput, err = removeNondeterministicBuildLogLines(build.Output) 538 | if err != nil { 539 | return "", err 540 | } 541 | } else { 542 | // `helmfile template` output is not stable and reliable when you have randomness in manifest generation, 543 | // like using random id in test pods. 544 | // 545 | // Since helmfile v0.126.0, we can use `helmfile build --embed-values` whose output 546 | // is stable as long as there's no randomness in helmfile state itself. 547 | // For prior helmfile versions, we fallback to `helmfile template`, as follows. 548 | // 549 | // Also see https://github.com/mumoshu/terraform-provider-helmfile/issues/28 for more context. 550 | tmpl, err := runTemplate(ctx, fs) 551 | if err != nil { 552 | return "", fmt.Errorf("running helmfile template: %w", err) 553 | } 554 | 555 | determinisiticOutput, err = removeNondeterministicTemplateAndDiffLogLines(tmpl.Output) 556 | if err != nil { 557 | return "", err 558 | } 559 | } 560 | 561 | hash := sha256.New() 562 | hash.Write([]byte(determinisiticOutput)) 563 | diffFile := filepath.Join(".terraform", "helmfile", fmt.Sprintf("diff-%x", hash.Sum(nil))) 564 | 565 | return diffFile, nil 566 | } 567 | 568 | func writeDiffFile(ctx *sdk.Context, fs *ReleaseSet, content string) error { 569 | diffFile, err := getDiffFile(ctx, fs) 570 | if err != nil { 571 | return err 572 | } 573 | 574 | if err := os.MkdirAll(filepath.Dir(diffFile), 0755); err != nil { 575 | return fmt.Errorf("creating directory for diff file %s: %v", diffFile, err) 576 | } 577 | 578 | logf("Writing diff file to %s", diffFile) 579 | 580 | if err := ioutil.WriteFile(diffFile, []byte(content), 0644); err != nil { 581 | return fmt.Errorf("writing diff to %s: %v", diffFile, err) 582 | } 583 | 584 | return nil 585 | } 586 | 587 | func readDiffFile(ctx *sdk.Context, fs *ReleaseSet) (string, error) { 588 | diffFile, err := getDiffFile(ctx, fs) 589 | if err != nil { 590 | return "", err 591 | } 592 | 593 | bs, err := ioutil.ReadFile(diffFile) 594 | if err != nil { 595 | return "", err 596 | } 597 | 598 | if len(bs) > 0 { 599 | logf("[DEBUG] Skipped running helmfile-diff on resource because we already have changes on diff: %+v", *fs) 600 | } 601 | 602 | return string(bs), nil 603 | } 604 | 605 | // DiffReleaseSet detects diff to be included in the terraform plan by runnning `helmfile diff`. 606 | // Beware that this function MUST be idempotent and the result is reliable. 607 | // 608 | // `terraform apply` seem to run diff twice, and if this function emitted a result different than the first run results in 609 | // errors like: 610 | // 611 | // When expanding the plan for helmfile_release_set.mystack to include new values 612 | // learned so far during apply, provider "registry.terraform.io/-/helmfile" 613 | // produced an invalid new value for .diff_output: was cty.StringVal("Adding repo 614 | // ... 615 | // a lot of text 616 | // ... 617 | // but now cty.StringVal("Adding repo stable 618 | // ... 619 | // a lot of text 620 | // ... 621 | func DiffReleaseSet(ctx *sdk.Context, fs *ReleaseSet, d ResourceReadWrite, opts ...DiffOption) (string, error) { 622 | logf("[DEBUG] Detecting changes on release set resource...") 623 | 624 | var diffConf DiffConfig 625 | for _, o := range opts { 626 | o(&diffConf) 627 | } 628 | 629 | diff, err := readDiffFile(ctx, fs) 630 | if err != nil { 631 | state, err := runDiff(ctx, fs, diffConf) 632 | if err != nil { 633 | logf("[DEBUG] Diff error detected: %v", err) 634 | 635 | // Make sure errors due to the latest `helmfile diff` run is shown to the user 636 | // d.SetNew(KeyError, err.Error()) 637 | 638 | // We return the error to stop terraform from modifying the state AND 639 | // let the user knows about the error. 640 | return "", fmt.Errorf("running helmfile diff: %w", err) 641 | } 642 | 643 | // We should ideally show this like `~ diff_output = -> (known after apply)`, 644 | // but it's shown as `~ diff_output = `, which is counter-intuitive. 645 | // But I wasn't able to find any way to achieve that. 646 | //d.SetNew(KeyDiffOutput, state.Output) 647 | //d.SetNewComputed(KeyDiffOutput) 648 | 649 | // Show the possibly transient error to disappear after successful apply. 650 | // 651 | // Seems like SetNew(KEY, "") is equivalent to SetNewComputed(KEY), according to the result below that is obtained 652 | // with SetNew: 653 | // ~ error = "/Users/c-ykuoka/go/bin/helmfile: exit status 1\nin ./helmfile-b96f019fb6b4f691ffca8269edb33ffb16cb60a20c769013049c1181ebf7ecc9.yaml: failed to read helmfile-b96f019fb6b4f691ffca8269edb33ffb16cb60a20c769013049c1181ebf7ecc9.yaml: reading document at index 1: yaml: line 2: mapping values are not allowed in this context\n" -> (known after apply) 654 | //d.SetNew(KeyError, "") 655 | //d.SetNewComputed(KeyError) 656 | 657 | // Mark apply output for changes to instruct the user to run `terraform apply` 658 | // Marking it when there's no diff output means `terraform plan` always show changes, which defeats the purpose of 659 | // `plan`. 660 | if state.Output != "" { 661 | diff, err = removeNondeterministicTemplateAndDiffLogLines(state.Output) 662 | if err != nil { 663 | return "", err 664 | } 665 | 666 | if err := writeDiffFile(ctx, fs, diff); err != nil { 667 | return "", err 668 | } 669 | } 670 | } 671 | 672 | // Executing d.Set(KeyDiffOutput, "") still internally records the update to the state 673 | // even if d.Get(KeyDiffOutput) is already "", which breaks our acceptance test. 674 | // Guard against that here. 675 | if diff != "" { 676 | maxDiffOutputLen := diffConf.MaxDiffOutputLen 677 | 678 | if maxDiffOutputLen == 0 { 679 | const DefaultMaxDiffOutputLen = 4096 680 | 681 | maxDiffOutputLen = DefaultMaxDiffOutputLen 682 | } 683 | 684 | notice := "...\n" + 685 | "helmfile-diff output was too long, and therefore snipped.\n" + 686 | fmt.Sprintf("Set max_diff_output_len in the provider config, which is currently %d, to a larger value to see more.", maxDiffOutputLen) 687 | noticeLen := len(notice) 688 | 689 | if len(diff) > maxDiffOutputLen { 690 | i := maxDiffOutputLen - noticeLen - 1 691 | for ; diff[i] != '\n'; i-- { 692 | 693 | } 694 | if i < 0 { 695 | i = 0 696 | } 697 | diff = diff[:i+1] + "\n" + notice 698 | } 699 | d.Set(KeyDiffOutput, diff) 700 | } 701 | 702 | //var previousApplyOutput string 703 | //if v := d.Get(KeyApplyOutput); v != nil { 704 | // previousApplyOutput = v.(string) 705 | //} 706 | // 707 | //if diff == "" && previousApplyOutput != "" { 708 | // // When the diff is empty, we should still proceed with updating the state to empty apply_output 709 | // // We set apply_output to "", so that the terraform is notified that this resource needs to be updated 710 | // // In `UpdateReleaseSet` func, we check if `diff_output` is empty, and then set empty string to apply_output again, 711 | // // so that the `apply_output=""` in plan matches `apply_output=""` in update. 712 | // d.SetNew(KeyApplyOutput, "") 713 | //} else if diff != "" { 714 | // d.SetNewComputed(KeyApplyOutput) 715 | //} 716 | 717 | return diff, nil 718 | } 719 | 720 | // Until https://github.com/roboll/helmfile/pull/1383 and Helmfile v0.125.1, 721 | // various helmfile command was running `helm repo up` to update Helm chart repositories before diff/template/apply. 722 | // `helm repo up` seems to update repositories concnurrently, in an nondeterministic order, which makes the stdout printed by the command 723 | // nondeterministic. 724 | // 725 | // This provider uses output of `helmfile template` to calculate the hash key of the helmfile-diff cache, which is used 726 | // to make originally non-determinisitc `helmfile-diff` result to be determinisitc. 727 | // In `removeNondeterministicTemplateAndDiffLogLines`, we remove non-deterministic part of `helm repo up`, so that the provider reliably 728 | // work with older versions of Helmfile. 729 | func removeNondeterministicTemplateAndDiffLogLines(s string) (string, error) { 730 | buf := &bytes.Buffer{} 731 | w := bufio.NewWriter(buf) 732 | 733 | b := bufio.NewScanner(strings.NewReader(s)) 734 | for b.Scan() { 735 | l := b.Text() 736 | if !strings.HasPrefix(l, "...Successfully got an update from the \"") { 737 | if _, err := w.WriteString(l); err != nil { 738 | return "", err 739 | } 740 | if _, err := w.WriteRune('\n'); err != nil { 741 | return "", err 742 | } 743 | } 744 | } 745 | if err := w.Flush(); err != nil { 746 | return "", fmt.Errorf("filtering helmfile template output: %v", err) 747 | } 748 | 749 | return buf.String(), nil 750 | } 751 | 752 | // This provider uses output of `helmfile build` to calculate the hash key of the helmfile-diff cache, which is used 753 | // to make originally non-determinisitc `helmfile-diff` result to be determinisitc. 754 | // 755 | // In `removeNondeterministicBuildLogLines`, we remove some part of `helm build --embed-values` that is non-deterministic 756 | // due to that the temporary helmfile.yaml generated by the provider has a random name. 757 | func removeNondeterministicBuildLogLines(s string) (string, error) { 758 | buf := &bytes.Buffer{} 759 | w := bufio.NewWriter(buf) 760 | 761 | b := bufio.NewScanner(strings.NewReader(s)) 762 | for b.Scan() { 763 | l := b.Text() 764 | if !strings.HasPrefix(l, "#") && !strings.HasPrefix(l, "filepath: ") { 765 | if _, err := w.WriteString(l); err != nil { 766 | return "", err 767 | } 768 | if _, err := w.WriteRune('\n'); err != nil { 769 | return "", err 770 | } 771 | } 772 | } 773 | if err := w.Flush(); err != nil { 774 | return "", fmt.Errorf("filtering helmfile build output: %v", err) 775 | } 776 | 777 | return buf.String(), nil 778 | } 779 | 780 | func UpdateReleaseSet(ctx *sdk.Context, fs *ReleaseSet, d ResourceReadWrite) error { 781 | diffFile, err := getDiffFile(ctx, fs) 782 | if err != nil { 783 | return err 784 | } 785 | 786 | defer func() { 787 | if _, err := os.Stat(diffFile); err == nil { 788 | if err := os.Remove(diffFile); err != nil { 789 | logf("Failed cleaning diff file: %v", err) 790 | } 791 | } 792 | }() 793 | 794 | logf("[DEBUG] Updating release set resource...") 795 | 796 | d.Set(KeyDirty, false) 797 | 798 | var plannedDiffOutput string 799 | if v := d.Get(KeyDiffOutput); v != nil { 800 | plannedDiffOutput = v.(string) 801 | } 802 | 803 | if plannedDiffOutput == "" { 804 | return nil 805 | } 806 | 807 | args := []string{ 808 | "apply", 809 | "--concurrency", strconv.Itoa(fs.Concurrency), 810 | "--suppress-secrets", 811 | } 812 | 813 | additionalArgs, err := getAdditionalHelmfileApplyFlags(ctx, fs) 814 | if err != nil { 815 | return err 816 | } 817 | 818 | args = append(args, additionalArgs...) 819 | 820 | for k, v := range fs.ReleasesValues { 821 | args = append(args, "--set", fmt.Sprintf("%s=%s", k, v)) 822 | } 823 | 824 | cmd, err := NewCommandWithKubeconfig(fs, args...) 825 | if err != nil { 826 | return err 827 | } 828 | defer os.Remove(fs.TmpHelmFilePath) 829 | 830 | //obtain exclusive lock 831 | mutexKV.Lock(fs.WorkingDirectory) 832 | defer mutexKV.Unlock(fs.WorkingDirectory) 833 | 834 | state := NewState() 835 | st, err := runCommand(ctx, cmd, state, false) 836 | if err != nil { 837 | return err 838 | } 839 | 840 | d.Set(KeyApplyOutput, st.Output) 841 | 842 | return nil 843 | } 844 | 845 | func DeleteReleaseSet(ctx *sdk.Context, fs *ReleaseSet, d ResourceReadWrite) error { 846 | logf("[DEBUG] Deleting release set resource...") 847 | cmd, err := NewCommandWithKubeconfig(fs, "destroy") 848 | if err != nil { 849 | return err 850 | } 851 | defer os.Remove(fs.TmpHelmFilePath) 852 | 853 | //obtain exclusive lock 854 | mutexKV.Lock(fs.WorkingDirectory) 855 | defer mutexKV.Unlock(fs.WorkingDirectory) 856 | 857 | state := NewState() 858 | _, err = runCommand(ctx, cmd, state, false) 859 | if err != nil { 860 | return err 861 | } 862 | 863 | //SetDiffOutput(d, "") 864 | 865 | return nil 866 | } 867 | 868 | func ImportReleaseSet(d *schema.ResourceData) (*schema.ResourceData, error) { 869 | helmfileYamlPath := d.Id() 870 | 871 | content, err := ioutil.ReadFile(helmfileYamlPath) 872 | if err != nil { 873 | return nil, fmt.Errorf("reading %s: %w", helmfileYamlPath, err) 874 | } 875 | 876 | d.SetId(newId()) 877 | 878 | d.Set(KeyConcurrency, 0) 879 | d.Set(KeyBin, "helmfile") 880 | d.Set(KeyHelmBin, "helm") 881 | d.Set(KeyDirty, false) 882 | d.Set(KeyContent, string(content)) 883 | 884 | return d, nil 885 | } 886 | -------------------------------------------------------------------------------- /pkg/helmfile/resource_helmfile_embedding_example.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 6 | "github.com/rs/xid" 7 | ) 8 | 9 | func resourceHelmfileEmbeddingExample() *schema.Resource { 10 | return &schema.Resource{ 11 | Create: resourceHelmfileEmbeddingExampleCreate, 12 | Delete: resourceHelmfileEmbeddingExampleDelete, 13 | Read: resourceHelmfileEmbeddingExampleRead, 14 | Update: resourceHelmfileEmbeddingExampleUpdate, 15 | CustomizeDiff: resourceHelmfileEmbeddingExampleCustomizeDiff, 16 | Schema: map[string]*schema.Schema{ 17 | "embedded": { 18 | Type: schema.TypeList, 19 | Optional: true, 20 | // Seems like we need to make the whole attributed as computed, rather than only a subset of 21 | // nested fields as computed. 22 | // Otherwise, we get nested computed fields like diff_output always shown as "(known after apply)", 23 | // rather than the actual planned value 24 | Computed: true, 25 | Elem: &schema.Resource{ 26 | Schema: ReleaseSetSchema, 27 | }, 28 | }, 29 | }, 30 | } 31 | } 32 | 33 | func ExtractEmbeddedReleaseSetResources(data ResourceRead, attr string) ([]map[string]interface{}, error) { 34 | var entries []map[string]interface{} 35 | 36 | if d := data.Get(attr); d == nil { 37 | return nil, fmt.Errorf("getting field: no attribute named %q found", attr) 38 | } else { 39 | ifs := d.([]interface{}) 40 | 41 | for _, i := range ifs { 42 | entries = append(entries, i.(map[string]interface{})) 43 | } 44 | } 45 | 46 | return entries, nil 47 | } 48 | 49 | func resourceHelmfileEmbeddingExampleCreate(data *schema.ResourceData, i interface{}) error { 50 | embeddedResources, err := ExtractEmbeddedReleaseSetResources(data, "embedded") 51 | if err != nil { 52 | return err 53 | } 54 | 55 | for _, e := range embeddedResources { 56 | fs := &ResourceReadWriteEmbedded{m: e} 57 | 58 | rs, err := NewReleaseSet(fs) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | if err := CreateReleaseSet(newContext(fs), rs, fs); err != nil { 64 | return err 65 | } 66 | } 67 | 68 | // Note: If you missed marking new resource and setting the id, it may end up unintuitive tf error like: 69 | // "... produced an unexpected new value for was present, but now absent." 70 | // 71 | 72 | data.MarkNewResource() 73 | 74 | //create random uuid for the id 75 | id := xid.New().String() 76 | data.SetId(id) 77 | 78 | data.Set("embedded", embeddedResources) 79 | 80 | dump("create", embeddedResources) 81 | 82 | return nil 83 | } 84 | 85 | func resourceHelmfileEmbeddingExampleDelete(data *schema.ResourceData, i interface{}) error { 86 | embeddedResources, err := ExtractEmbeddedReleaseSetResources(data, "embedded") 87 | if err != nil { 88 | return err 89 | } 90 | 91 | for _, e := range embeddedResources { 92 | fs := &ResourceReadWriteEmbedded{m: e} 93 | 94 | rs, err := NewReleaseSet(fs) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | if err := DeleteReleaseSet(newContext(fs), rs, fs); err != nil { 100 | return err 101 | } 102 | } 103 | return nil 104 | } 105 | 106 | func resourceHelmfileEmbeddingExampleRead(data *schema.ResourceData, i interface{}) error { 107 | embeddedResources, err := ExtractEmbeddedReleaseSetResources(data, "embedded") 108 | if err != nil { 109 | return err 110 | } 111 | 112 | for _, e := range embeddedResources { 113 | fs := &ResourceReadWriteEmbedded{m: e} 114 | 115 | rs, err := NewReleaseSet(fs) 116 | if err != nil { 117 | return err 118 | } 119 | 120 | if err := ReadReleaseSet(newContext(fs), rs, fs); err != nil { 121 | return err 122 | } 123 | } 124 | return nil 125 | } 126 | 127 | func resourceHelmfileEmbeddingExampleUpdate(data *schema.ResourceData, i interface{}) error { 128 | embeddedResources, err := ExtractEmbeddedReleaseSetResources(data, "embedded") 129 | if err != nil { 130 | return err 131 | } 132 | 133 | for _, e := range embeddedResources { 134 | fs := &ResourceReadWriteEmbedded{m: e} 135 | 136 | rs, err := NewReleaseSet(fs) 137 | if err != nil { 138 | return err 139 | } 140 | 141 | if err := UpdateReleaseSet(newContext(fs), rs, fs); err != nil { 142 | return err 143 | } 144 | } 145 | 146 | data.Set("embedded", embeddedResources) 147 | 148 | return nil 149 | } 150 | 151 | func resourceHelmfileEmbeddingExampleCustomizeDiff(resourceDiff *schema.ResourceDiff, i interface{}) error { 152 | embeddedResources, err := ExtractEmbeddedReleaseSetResources(resourceDiff, "embedded") 153 | if err != nil { 154 | return err 155 | } 156 | 157 | var hasDiff bool 158 | 159 | for _, e := range embeddedResources { 160 | fs := &ResourceReadWriteEmbedded{m: e} 161 | 162 | rs, err := NewReleaseSet(fs) 163 | if err != nil { 164 | return err 165 | } 166 | 167 | // DryRun=true should be set if terraform-provider-helmfile is integrated into an another provider 168 | // and the helmfile_release_set resource is embedded into a resource tha also declares the target K8s cluster, 169 | // which means before creating the cluster the provider needs to show helmfile-diff result without K8s 170 | // 171 | // DryRun=false and Kubeconfig!="" should be set if the K8s cluster is already there and you have the kubeconfig to 172 | // access the K8s API 173 | diff, err := DiffReleaseSet(newContext(fs), rs, fs, WithDiffConfig(DiffConfig{DryRun: false, Kubeconfig: ""})) 174 | if err != nil { 175 | return err 176 | } 177 | 178 | if diff != "" { 179 | hasDiff = true 180 | } 181 | } 182 | 183 | if hasDiff { 184 | resourceDiff.SetNew("embedded", embeddedResources) 185 | } 186 | 187 | dump("diff", embeddedResources) 188 | 189 | return nil 190 | } 191 | -------------------------------------------------------------------------------- /pkg/helmfile/resource_release.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk/tfsdk" 7 | "github.com/rs/xid" 8 | "golang.org/x/xerrors" 9 | "runtime/debug" 10 | 11 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 12 | ) 13 | 14 | const KeyNamespace = "namespace" 15 | const KeyName = "name" 16 | const KeyChart = "chart" 17 | const KeyVersion = "version" 18 | const KeyHelmVersion = "helm_version" 19 | const KeyHelmDiffVersion = "helm_diff_version" 20 | const KeyVerify = "verify" 21 | const KeyWait = "wait" 22 | const KeyForce = "force" 23 | const KeyAtomic = "atomic" 24 | const KeyCleanupOnFail = "cleanup_on_fail" 25 | const KeyTimeout = "timeout" 26 | const KeyKubecontext = "kubecontext" 27 | const KeyKubeconfig = "kubeconfig" 28 | 29 | func resourceHelmfileRelease() *schema.Resource { 30 | return &schema.Resource{ 31 | Create: resourceHelmfileReleaseCreate, 32 | Delete: resourceHelmfileReleaseDelete, 33 | Read: resourceHelmfileReleaseRead, 34 | Update: resourceHelmfileReleaseUpdate, 35 | CustomizeDiff: resourceHelmfileReleaseDiff, 36 | Schema: map[string]*schema.Schema{ 37 | KeyAWSRegion: { 38 | Type: schema.TypeString, 39 | Optional: true, 40 | ForceNew: false, 41 | }, 42 | KeyAWSProfile: { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | ForceNew: false, 46 | }, 47 | KeyAWSAssumeRole: tfsdk.SchemaAssumeRole(), 48 | KeyNamespace: { 49 | Type: schema.TypeString, 50 | Optional: true, 51 | ForceNew: true, 52 | Default: "default", 53 | }, 54 | KeyName: { 55 | Type: schema.TypeString, 56 | Optional: true, 57 | ForceNew: true, 58 | }, 59 | KeyChart: { 60 | Type: schema.TypeString, 61 | Required: true, 62 | ForceNew: false, 63 | }, 64 | KeyVersion: { 65 | Type: schema.TypeString, 66 | Optional: true, 67 | ForceNew: false, 68 | Default: "", 69 | }, 70 | KeyHelmVersion: { 71 | Type: schema.TypeString, 72 | Optional: true, 73 | ForceNew: false, 74 | Default: "", 75 | }, 76 | KeyValues: { 77 | Type: schema.TypeList, 78 | Optional: true, 79 | ForceNew: false, 80 | Elem: &schema.Schema{ 81 | Type: schema.TypeString, 82 | }, 83 | }, 84 | KeyWorkingDirectory: { 85 | Type: schema.TypeString, 86 | Optional: true, 87 | ForceNew: false, 88 | Default: "", 89 | }, 90 | KeyVerify: { 91 | Type: schema.TypeBool, 92 | Optional: true, 93 | Default: false, 94 | }, 95 | KeyWait: { 96 | Type: schema.TypeBool, 97 | Optional: true, 98 | Default: true, 99 | }, 100 | KeyForce: { 101 | Type: schema.TypeBool, 102 | Optional: true, 103 | Default: false, 104 | }, 105 | KeyAtomic: { 106 | Type: schema.TypeBool, 107 | Optional: true, 108 | Default: true, 109 | }, 110 | KeyCleanupOnFail: { 111 | Type: schema.TypeBool, 112 | Optional: true, 113 | Default: true, 114 | }, 115 | KeyTimeout: { 116 | Type: schema.TypeInt, 117 | Optional: true, 118 | Default: 0, 119 | }, 120 | KeyKubeconfig: { 121 | Type: schema.TypeString, 122 | Required: true, 123 | ForceNew: false, 124 | }, 125 | KeyKubecontext: { 126 | Type: schema.TypeString, 127 | Optional: true, 128 | ForceNew: false, 129 | Default: "", 130 | }, 131 | KeyBin: { 132 | Type: schema.TypeString, 133 | Optional: true, 134 | ForceNew: false, 135 | Default: "helmfile", 136 | }, 137 | KeyHelmBin: { 138 | Type: schema.TypeString, 139 | Optional: true, 140 | ForceNew: false, 141 | Default: "helm", 142 | }, 143 | KeyDiffOutput: { 144 | Type: schema.TypeString, 145 | Computed: true, 146 | }, 147 | KeyApplyOutput: { 148 | Type: schema.TypeString, 149 | Computed: true, 150 | }, 151 | KeyError: { 152 | Type: schema.TypeString, 153 | Computed: true, 154 | }, 155 | KeyDirty: { 156 | Type: schema.TypeBool, 157 | Optional: true, 158 | Default: false, 159 | }, 160 | }, 161 | } 162 | } 163 | 164 | //helpers to unwravel the recursive bits by adding a base condition 165 | func resourceHelmfileReleaseCreate(d *schema.ResourceData, _ interface{}) (finalErr error) { 166 | defer func() { 167 | if err := recover(); err != nil { 168 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 169 | } 170 | }() 171 | 172 | rs, err := NewReleaseSetWithSingleRelease(d) 173 | if err != nil { 174 | return err 175 | } 176 | 177 | if err := CreateReleaseSet(newContext(d), rs, d); err != nil { 178 | return err 179 | } 180 | 181 | d.MarkNewResource() 182 | 183 | //create random uuid for the id 184 | id := xid.New().String() 185 | d.SetId(id) 186 | 187 | return nil 188 | } 189 | 190 | func resourceHelmfileReleaseRead(d *schema.ResourceData, _ interface{}) (finalErr error) { 191 | defer func() { 192 | if err := recover(); err != nil { 193 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 194 | } 195 | }() 196 | 197 | rs, err := NewReleaseSetWithSingleRelease(d) 198 | if err != nil { 199 | return err 200 | } 201 | 202 | return ReadReleaseSet(newContext(d), rs, d) 203 | } 204 | 205 | func resourceHelmfileReleaseUpdate(d *schema.ResourceData, _ interface{}) (finalErr error) { 206 | defer func() { 207 | if err := recover(); err != nil { 208 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 209 | } 210 | }() 211 | 212 | rs, err := NewReleaseSetWithSingleRelease(d) 213 | if err != nil { 214 | return err 215 | } 216 | 217 | return UpdateReleaseSet(newContext(d), rs, d) 218 | } 219 | 220 | func resourceHelmfileReleaseDiff(d *schema.ResourceDiff, _ interface{}) (finalErr error) { 221 | defer func() { 222 | if err := recover(); err != nil { 223 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 224 | } 225 | }() 226 | 227 | rs, err := NewReleaseSetWithSingleRelease(d) 228 | if err != nil { 229 | return err 230 | } 231 | 232 | diff, err := DiffReleaseSet(newContext(d), rs, resourceDiffToFields(d)) 233 | if err != nil { 234 | return err 235 | } 236 | 237 | if diff != "" { 238 | if err := d.SetNewComputed(KeyApplyOutput); err != nil { 239 | return xerrors.Errorf("setting new computed %s: %w", KeyApplyOutput, err) 240 | } 241 | } 242 | 243 | return nil 244 | } 245 | 246 | func resourceHelmfileReleaseDelete(d *schema.ResourceData, _ interface{}) (finalErr error) { 247 | defer func() { 248 | if err := recover(); err != nil { 249 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 250 | } 251 | }() 252 | 253 | rs, err := NewReleaseSetWithSingleRelease(d) 254 | if err != nil { 255 | return err 256 | } 257 | 258 | if err := DeleteReleaseSet(newContext(d), rs, d); err != nil { 259 | return err 260 | } 261 | 262 | d.SetId("") 263 | 264 | return nil 265 | } 266 | 267 | func NewReleaseSetWithSingleRelease(d ResourceRead) (*ReleaseSet, error) { 268 | r := NewRelease(d) 269 | 270 | var values []interface{} 271 | for _, v := range r.Values { 272 | var vv map[string]interface{} 273 | if err := json.Unmarshal([]byte(fmt.Sprintf("%s", v)), &vv); err != nil { 274 | return nil, err 275 | } 276 | values = append(values, vv) 277 | } 278 | content := map[string]interface{}{ 279 | "releases": []interface{}{ 280 | map[string]interface{}{ 281 | "namespace": r.Namespace, 282 | "name": r.Name, 283 | "chart": r.Chart, 284 | "version": r.Version, 285 | "values": values, 286 | "verify": r.Verify, 287 | "wait": r.Wait, 288 | "force": r.Force, 289 | "atomic": r.Atomic, 290 | "cleanupOnFail": r.CleanupOnFail, 291 | "timeout": r.Timeout, 292 | "kubeContext": r.Kubecontext, 293 | }, 294 | }, 295 | } 296 | bs, err := json.Marshal(content) 297 | if err != nil { 298 | return nil, err 299 | } 300 | 301 | rs := &ReleaseSet{ 302 | Bin: r.Bin, 303 | HelmBin: r.HelmBin, 304 | Content: string(bs), 305 | Environment: "default", 306 | WorkingDirectory: r.WorkingDirectory, 307 | Kubeconfig: r.Kubeconfig, 308 | } 309 | 310 | return rs, nil 311 | } 312 | -------------------------------------------------------------------------------- /pkg/helmfile/resource_release_set.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hashicorp/terraform-plugin-sdk/helper/schema" 6 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk/tfsdk" 7 | "github.com/rs/xid" 8 | "golang.org/x/xerrors" 9 | "log" 10 | "os" 11 | "runtime/debug" 12 | "strings" 13 | ) 14 | 15 | const KeyValuesFiles = "values_files" 16 | const KeyValues = "values" 17 | const KeySelector = "selector" 18 | const KeySelectors = "selectors" 19 | const KeyEnvironmentVariables = "environment_variables" 20 | const KeyWorkingDirectory = "working_directory" 21 | const KeyPath = "path" 22 | const KeyContent = "content" 23 | const KeyEnvironment = "environment" 24 | const KeyBin = "binary" 25 | const KeyHelmBin = "helm_binary" 26 | const KeyDiffOutput = "diff_output" 27 | const KeyError = "error" 28 | const KeyApplyOutput = "apply_output" 29 | const KeyDirty = "dirty" 30 | const KeyConcurrency = "concurrency" 31 | const KeyReleasesValues = "releases_values" 32 | const KeySkipDiffOnMissingFiles = "skip_diff_on_missing_files" 33 | 34 | const HelmfileDefaultPath = "helmfile.yaml" 35 | 36 | var ReleaseSetSchema = map[string]*schema.Schema{ 37 | KeyAWSRegion: { 38 | Type: schema.TypeString, 39 | Optional: true, 40 | ForceNew: false, 41 | }, 42 | KeyAWSProfile: { 43 | Type: schema.TypeString, 44 | Optional: true, 45 | ForceNew: false, 46 | }, 47 | KeyAWSAssumeRole: tfsdk.SchemaAssumeRole(), 48 | KeyValuesFiles: { 49 | Type: schema.TypeList, 50 | Optional: true, 51 | ForceNew: false, 52 | Elem: &schema.Schema{ 53 | Type: schema.TypeString, 54 | }, 55 | }, 56 | KeyValues: { 57 | Type: schema.TypeList, 58 | Optional: true, 59 | ForceNew: false, 60 | Elem: &schema.Schema{ 61 | Type: schema.TypeString, 62 | }, 63 | }, 64 | KeySkipDiffOnMissingFiles: { 65 | Type: schema.TypeList, 66 | Optional: true, 67 | ForceNew: false, 68 | Elem: &schema.Schema{ 69 | Type: schema.TypeString, 70 | }, 71 | }, 72 | KeySelector: { 73 | Type: schema.TypeMap, 74 | Optional: true, 75 | ForceNew: false, 76 | }, 77 | KeySelectors: { 78 | Type: schema.TypeList, 79 | Optional: true, 80 | ForceNew: false, 81 | Elem: &schema.Schema{ 82 | Type: schema.TypeString, 83 | }, 84 | }, 85 | KeyEnvironmentVariables: { 86 | Type: schema.TypeMap, 87 | Optional: true, 88 | Elem: schema.TypeString, 89 | }, 90 | KeyWorkingDirectory: { 91 | Type: schema.TypeString, 92 | Optional: true, 93 | ForceNew: false, 94 | Default: "", 95 | }, 96 | KeyKubeconfig: { 97 | Type: schema.TypeString, 98 | Required: true, 99 | ForceNew: false, 100 | }, 101 | KeyPath: { 102 | Type: schema.TypeString, 103 | Optional: true, 104 | ForceNew: false, 105 | Default: "", 106 | }, 107 | KeyContent: { 108 | Type: schema.TypeString, 109 | Optional: true, 110 | ForceNew: false, 111 | }, 112 | KeyBin: { 113 | Type: schema.TypeString, 114 | Optional: true, 115 | ForceNew: false, 116 | Default: "helmfile", 117 | }, 118 | KeyHelmBin: { 119 | Type: schema.TypeString, 120 | Optional: true, 121 | ForceNew: false, 122 | Default: "helm", 123 | }, 124 | KeyVersion: { 125 | Type: schema.TypeString, 126 | Optional: true, 127 | ForceNew: false, 128 | Default: "", 129 | }, 130 | KeyHelmVersion: { 131 | Type: schema.TypeString, 132 | Optional: true, 133 | ForceNew: false, 134 | Default: "", 135 | }, 136 | KeyHelmDiffVersion: { 137 | Type: schema.TypeString, 138 | Optional: true, 139 | ForceNew: false, 140 | Default: "", 141 | }, 142 | KeyEnvironment: { 143 | Type: schema.TypeString, 144 | Optional: true, 145 | ForceNew: false, 146 | Default: "", 147 | }, 148 | KeyDiffOutput: { 149 | Type: schema.TypeString, 150 | Computed: true, 151 | }, 152 | KeyApplyOutput: { 153 | Type: schema.TypeString, 154 | Computed: true, 155 | }, 156 | KeyError: { 157 | Type: schema.TypeString, 158 | Computed: true, 159 | }, 160 | KeyDirty: { 161 | Type: schema.TypeBool, 162 | Optional: true, 163 | Default: false, 164 | }, 165 | KeyConcurrency: { 166 | Type: schema.TypeInt, 167 | Optional: true, 168 | Default: 0, 169 | }, 170 | KeyReleasesValues: { 171 | Type: schema.TypeMap, 172 | Optional: true, 173 | ForceNew: false, 174 | }, 175 | } 176 | 177 | func resourceHelmfileReleaseSet() *schema.Resource { 178 | return &schema.Resource{ 179 | Create: resourceReleaseSetCreate, 180 | Delete: resourceReleaseSetDelete, 181 | Read: resourceReleaseSetRead, 182 | Update: resourceReleaseSetUpdate, 183 | CustomizeDiff: resourceReleaseSetDiff, 184 | Importer: &schema.ResourceImporter{ 185 | State: resourceReleaseSetImport, 186 | }, 187 | Schema: ReleaseSetSchema, 188 | } 189 | } 190 | 191 | //helpers to unwravel the recursive bits by adding a base condition 192 | func resourceReleaseSetCreate(d *schema.ResourceData, meta interface{}) (finalErr error) { 193 | defer func() { 194 | if err := recover(); err != nil { 195 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 196 | } 197 | }() 198 | 199 | fs, err := NewReleaseSet(d) 200 | if err != nil { 201 | return err 202 | } 203 | 204 | if err := CreateReleaseSet(newContext(d), fs, d); err != nil { 205 | return fmt.Errorf("creating release set: %w", err) 206 | } 207 | 208 | d.MarkNewResource() 209 | 210 | d.SetId(newId()) 211 | 212 | return nil 213 | } 214 | 215 | func newId() string { 216 | //create random uuid for the id 217 | id := xid.New().String() 218 | 219 | return id 220 | } 221 | 222 | func resourceReleaseSetRead(d *schema.ResourceData, meta interface{}) (finalErr error) { 223 | defer func() { 224 | if err := recover(); err != nil { 225 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 226 | } 227 | }() 228 | 229 | fs, err := NewReleaseSet(d) 230 | if err != nil { 231 | return err 232 | } 233 | 234 | if err := ReadReleaseSet(newContext(d), fs, d); err != nil { 235 | return fmt.Errorf("reading release set: %w", err) 236 | } 237 | 238 | return nil 239 | } 240 | 241 | func resourceReleaseSetDiff(d *schema.ResourceDiff, meta interface{}) (finalErr error) { 242 | defer func() { 243 | if err := recover(); err != nil { 244 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 245 | } 246 | }() 247 | 248 | old, new := d.GetChange(KeyWorkingDirectory) 249 | log.Printf("Getting old and new working directories for id %q: old = %v, new = %v, got = %v", d.Id(), old, new, d.Get(KeyWorkingDirectory)) 250 | 251 | fs, err := NewReleaseSet(d) 252 | if err != nil { 253 | return err 254 | } 255 | 256 | kubeconfig, err := getKubeconfig(fs) 257 | if err != nil { 258 | return fmt.Errorf("getting kubeconfig: %w", err) 259 | } 260 | 261 | if fs.Kubeconfig == "" { 262 | logf("Skipping helmfile-diff due to that kubeconfig is empty, which means that this operation has been called on a helmfile resource that depends on in-existent resource") 263 | 264 | return nil 265 | } 266 | 267 | if v, err := shouldDiff(fs); err != nil { 268 | return xerrors.Errorf("checking skip_diff_on_missing_files to determine if the provider needs to run helmfile-diff: %w", err) 269 | } else if !v { 270 | logf("Skipping helmfile-diff due to that one or more files listed in skip_diff_on_missing_files were missing") 271 | 272 | return nil 273 | } 274 | 275 | provider := meta.(*ProviderInstance) 276 | 277 | diff, err := DiffReleaseSet(newContext(d), fs, resourceDiffToFields(d), WithDiffConfig(DiffConfig{ 278 | MaxDiffOutputLen: provider.MaxDiffOutputLen, 279 | })) 280 | if err != nil { 281 | // helmfile_release_set.kubeconfig or helmfile_releaset_set.environment_variables.KUBECONFIG can be empty 282 | // on `plan` if the value depends on another terraform resource. 283 | // This `plan` includes the implicit/automatic plan that is conducted before `terraform destroy`. 284 | // So, on `plan` helmfile diff can fail due to the missing KUBECONFIG. If we did return an error for that, 285 | // `terraform plan` or `terraform destroy` on helmfile_release_set will never succeed if the dependant resource is missing. 286 | if *kubeconfig != "" { 287 | // kubeconfig can be also empty when the kubeconfig path is static but not generated when terraform triggers 288 | // diff on this release_set. 289 | // We detect that situation by looking for the file. 290 | // If the kubeconfig_path is not empty AND the file is in-existent, we may safely say that 291 | // the path is static but the file is not yet generated. 292 | // In code below, `info == nil` or `os.IsNotExist(err)` means that the file is in-existent. 293 | if info, _ := os.Stat(*kubeconfig); info != nil { 294 | return fmt.Errorf("diffing release set: %w", err) 295 | } 296 | } else if !strings.Contains(err.Error(), "Kubernetes cluster unreachable") { 297 | return fmt.Errorf("diffing release set: %w", err) 298 | } 299 | log.Printf("Ignoring helmfile-diff error on plan because it may be due to that terraform's behaviour that "+ 300 | "helmfile_releaset_set.kubeconfig that depends on another missing resource can be empty: %v", err) 301 | } 302 | 303 | if diff != "" { 304 | d.SetNewComputed(KeyApplyOutput) 305 | } 306 | 307 | return nil 308 | } 309 | 310 | func resourceReleaseSetUpdate(d *schema.ResourceData, meta interface{}) (finalErr error) { 311 | defer func() { 312 | if err := recover(); err != nil { 313 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 314 | } 315 | }() 316 | 317 | fs, err := NewReleaseSet(d) 318 | if err != nil { 319 | return err 320 | } 321 | 322 | return UpdateReleaseSet(newContext(d), fs, d) 323 | } 324 | 325 | func resourceReleaseSetDelete(d *schema.ResourceData, meta interface{}) (finalErr error) { 326 | defer func() { 327 | if err := recover(); err != nil { 328 | finalErr = fmt.Errorf("unhandled error: %v\n%s", err, debug.Stack()) 329 | } 330 | }() 331 | 332 | fs, err := NewReleaseSet(d) 333 | if err != nil { 334 | return err 335 | } 336 | 337 | if err := DeleteReleaseSet(newContext(d), fs, d); err != nil { 338 | return err 339 | } 340 | 341 | d.SetId("") 342 | 343 | return nil 344 | } 345 | 346 | func resourceReleaseSetImport(data *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { 347 | data, err := ImportReleaseSet(data) 348 | if err != nil { 349 | return nil, fmt.Errorf("importing release set: %w", err) 350 | } 351 | 352 | return []*schema.ResourceData{data}, nil 353 | } 354 | -------------------------------------------------------------------------------- /pkg/helmfile/resource_release_set_test.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" 9 | "github.com/hashicorp/terraform-plugin-sdk/helper/resource" 10 | "github.com/hashicorp/terraform-plugin-sdk/terraform" 11 | ) 12 | 13 | func TestAccHelmfileReleaseSet_basic(t *testing.T) { 14 | resourceName := "helmfile_release_set.the_product" 15 | releaseID := acctest.RandString(8) 16 | resource.Test(t, resource.TestCase{ 17 | PreCheck: func() { testAccPreCheck(t) }, 18 | Providers: testAccProviders, 19 | CheckDestroy: testAccCheckShellScriptDestroy, 20 | Steps: []resource.TestStep{ 21 | { 22 | Config: testAccHelmfileReleaseSetConfig_basic(releaseID), 23 | 24 | Check: resource.ComposeTestCheckFunc( 25 | resource.TestCheckResourceAttr(resourceName, "environment_variables.%", "1"), 26 | resource.TestCheckResourceAttr(resourceName, "selector.%", "1"), 27 | resource.TestCheckResourceAttr(resourceName, "values.#", "1"), 28 | resource.TestCheckResourceAttr(resourceName, "diff_output", wantedHelmfileDiffOutputForReleaseID(releaseID)), 29 | resource.TestCheckResourceAttrSet(resourceName, "id"), 30 | ), 31 | }, 32 | }, 33 | }) 34 | } 35 | 36 | func TestAccHelmfileReleaseSet_binaries(t *testing.T) { 37 | resourceName := "helmfile_release_set.the_product" 38 | releaseID := acctest.RandString(8) 39 | resource.Test(t, resource.TestCase{ 40 | PreCheck: func() { testAccPreCheck(t) }, 41 | Providers: testAccProviders, 42 | CheckDestroy: testAccCheckShellScriptDestroy, 43 | Steps: []resource.TestStep{ 44 | { 45 | Config: testAccHelmfileReleaseSetConfig_binaries(releaseID), 46 | 47 | Check: resource.ComposeTestCheckFunc( 48 | resource.TestCheckResourceAttr(resourceName, "environment_variables.%", "1"), 49 | resource.TestCheckResourceAttr(resourceName, "selector.%", "1"), 50 | resource.TestCheckResourceAttr(resourceName, "values.#", "1"), 51 | resource.TestCheckResourceAttr(resourceName, "diff_output", wantedHelmfileDiffOutputForReleaseID(releaseID)), 52 | resource.TestCheckResourceAttrSet(resourceName, "id"), 53 | ), 54 | }, 55 | }, 56 | }) 57 | } 58 | 59 | func testAccCheckShellScriptDestroy(s *terraform.State) error { 60 | _ = testAccProvider.Meta().(*ProviderInstance) 61 | 62 | for _, rs := range s.RootModule().Resources { 63 | if rs.Type != "helmfile_release_set" { 64 | continue 65 | } 66 | // 67 | //helmfileYaml := fmt.Sprintf("helmfile-%s.yaml", rs.Primary.ID) 68 | // 69 | //cmd := exec.Command("helmfile", "-f", helmfileYaml, "status") 70 | //if out, err := cmd.CombinedOutput(); err == nil { 71 | // return fmt.Errorf("verifying helmfile status: releases still exist for %s", helmfileYaml) 72 | //} else if !strings.Contains(string(out), "Error: release: not found") { 73 | // return fmt.Errorf("verifying helmfile status: unexpected error: %v:\n\nCOMBINED OUTPUT:\n%s", err, string(out)) 74 | //} 75 | } 76 | return nil 77 | } 78 | 79 | func testAccHelmfileReleaseSetConfig_basic(randVal string) string { 80 | return fmt.Sprintf(` 81 | resource "helmfile_release_set" "the_product" { 82 | content = < 0 { 74 | if err := s.Init(); err != nil { 75 | return nil, nil, fmt.Errorf("initializing shoal: %w\n%s", err, buf.String()) 76 | } 77 | 78 | if err := s.InitGitProvider(conf); err != nil { 79 | return nil, nil, fmt.Errorf("initializing shoal git provider: %w\n%s", err, buf.String()) 80 | } 81 | 82 | wd, err := os.Getwd() 83 | if err != nil { 84 | return nil, nil, err 85 | } 86 | 87 | if conf.Helm.Plugins.Diff != "" { 88 | // TODO Any better place to do this? 89 | // This is for letting helm know about the location of helm plugins installed by shoal 90 | os.Setenv("XDG_DATA_HOME", filepath.Join(wd, ".shoal/Library")) 91 | } 92 | 93 | errch := make(chan error) 94 | 95 | go func() { 96 | if err := s.Sync(conf); err != nil { 97 | errch <- fmt.Errorf("syncing shoal foods: %w\n%s", err, buf.String()) 98 | } 99 | 100 | errch <- nil 101 | }() 102 | 103 | timer := time.NewTicker(60 * time.Second) 104 | defer timer.Stop() 105 | 106 | select { 107 | case err := <-errch: 108 | if err != nil { 109 | return nil, nil, xerrors.Errorf("running shoal-sync: %w\n%s", err, buf.String()) 110 | } 111 | case <-timer.C: 112 | return nil, nil, fmt.Errorf("timeout exceeded while waiting for shoal-sync\n%s", buf.String()) 113 | } 114 | } 115 | 116 | binPath := s.BinPath() 117 | 118 | if helmfileVersion != "" { 119 | helmfileBin = filepath.Join(binPath, "helmfile") 120 | } 121 | 122 | if helmVersion != "" { 123 | helmBin = filepath.Join(binPath, "helm") 124 | } 125 | 126 | if helmfileBin == "" { 127 | return nil, nil, errors.New("bug: helmfile_release_set.bin is missing") 128 | } 129 | 130 | return &helmfileBin, &helmBin, nil 131 | } 132 | -------------------------------------------------------------------------------- /pkg/helmfile/skip_diff.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "errors" 5 | "golang.org/x/xerrors" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | func shouldDiff(rs *ReleaseSet) (bool, error) { 11 | for _, path := range rs.SkipDiffOnMissingFiles { 12 | logf("processing %q in skip_diff_on_missing_files...", path) 13 | 14 | abs, err := filepath.Abs(path) 15 | if err != nil { 16 | return false, xerrors.Errorf("determining absolute path to %s: %w", path, err) 17 | } 18 | 19 | if _, err := os.Stat(abs); err != nil { 20 | if errors.Is(err, os.ErrNotExist) { 21 | logf("detected missing file: %s", abs) 22 | 23 | return false, nil 24 | } 25 | 26 | return false, xerrors.Errorf("failed calling stat: %w", err) 27 | } 28 | 29 | logf("detected existing file: %s", abs) 30 | } 31 | 32 | return true, nil 33 | } 34 | -------------------------------------------------------------------------------- /pkg/helmfile/utility.go: -------------------------------------------------------------------------------- 1 | package helmfile 2 | 3 | import ( 4 | "github.com/mumoshu/terraform-provider-eksctl/pkg/sdk" 5 | "log" 6 | "os/exec" 7 | ) 8 | 9 | // State is a wrapper around both the input and output attributes that are relavent for updates 10 | type State struct { 11 | Output string 12 | } 13 | 14 | // NewState is the constructor for State 15 | func NewState() *State { 16 | return &State{} 17 | } 18 | 19 | func readEnvironmentVariables(ev map[string]interface{}, exclude string) []string { 20 | var variables []string 21 | if ev != nil { 22 | for k, v := range ev { 23 | if k == exclude { 24 | continue 25 | } 26 | variables = append(variables, k+"="+v.(string)) 27 | } 28 | } 29 | return variables 30 | } 31 | 32 | func runCommand(ctx *sdk.Context, cmd *exec.Cmd, state *State, diffMode bool) (*State, error) { 33 | res, err := ctx.Run(cmd) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | newState := NewState() 39 | if diffMode && res.ExitStatus == 0 { 40 | newState.Output = "" 41 | } else { 42 | newState.Output = res.Output 43 | } 44 | 45 | log.Printf("[DEBUG] helmfile command new state: \"%v\"", newState) 46 | 47 | return newState, nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/profile/profile.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "fmt" 5 | "github.com/pkg/profile" 6 | "os" 7 | ) 8 | 9 | func Start() interface{ Stop() } { 10 | var opts []func(*profile.Profile) 11 | 12 | switch p := os.Getenv("TF_HELMFILE_PROFILE"); p { 13 | case "mem": 14 | opts = append(opts, profile.MemProfile) 15 | case "cpu": 16 | opts = append(opts, profile.CPUProfile) 17 | case "": 18 | // Do nothing 19 | return noopProfiler{} 20 | default: 21 | panic(fmt.Sprintf("Unsupported TF_HELMFILE_PROFILE=%s: Supported values are %q and %q", p, "mem", "cpu")) 22 | } 23 | 24 | if p := os.Getenv("TF_HELMFILE_PROFILE_PATH"); p != "" { 25 | opts = append(opts, profile.ProfilePath(p)) 26 | } 27 | 28 | profiler := profile.Start(opts...) 29 | 30 | return profiler 31 | } 32 | 33 | type noopProfiler struct{} 34 | 35 | func (_ noopProfiler) Stop() { 36 | 37 | } 38 | --------------------------------------------------------------------------------