diff --git a/.changelog/1247.txt b/.changelog/1247.txt index 1df9dfafc..d86fda3e0 100644 --- a/.changelog/1247.txt +++ b/.changelog/1247.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/helm_release: add `upgrade` map attribute to enable idempotent release installation, addressing components of [GH-425](https://github.com/hashicorp/terraform-provider-helm/issues/425) +resource/helm_release: add `upgrade_install` boolean attribute to enable idempotent release installation, addressing components of [GH-425](https://github.com/hashicorp/terraform-provider-helm/issues/425) ``` diff --git a/docs/index.md b/docs/index.md index 7cbfbb24d..e78c5b56c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -186,10 +186,10 @@ The `kubernetes` block supports: * `config_context` - (Optional) Context to choose from the config file. Can be sourced from `KUBE_CTX`. * `proxy_url` - (Optional) URL to the proxy to be used for all API requests. URLs with "http", "https", and "socks5" schemes are supported. Can be sourced from `KUBE_PROXY_URL`. * `exec` - (Optional) Configuration block to use an [exec-based credential plugin](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins), e.g. call an external command to receive user credentials. -* `api_version` - (Required) API version to use when decoding the ExecCredentials resource, e.g. `client.authentication.k8s.io/v1beta1`. -* `command` - (Required) Command to execute. -* `args` - (Optional) List of arguments to pass when executing the plugin. -* `env` - (Optional) Map of environment variables to set when executing the plugin. + * `api_version` - (Required) API version to use when decoding the ExecCredentials resource, e.g. `client.authentication.k8s.io/v1beta1`. + * `command` - (Required) Command to execute. + * `args` - (Optional) List of arguments to pass when executing the plugin. + * `env` - (Optional) Map of environment variables to set when executing the plugin. The `registry` block has options: diff --git a/docs/resources/release.md b/docs/resources/release.md index 6e9bd4fff..e818804bd 100644 --- a/docs/resources/release.md +++ b/docs/resources/release.md @@ -54,7 +54,7 @@ A Chart is a Helm package. It contains all of the resource definitions necessary - `set_sensitive` (Block Set) Custom sensitive values to be merged with the values. (see [below for nested schema](#nestedblock--set_sensitive)) - `skip_crds` (Boolean) If set, no CRDs will be installed. By default, CRDs are installed if not already present. Defaults to `false`. - `timeout` (Number) Time in seconds to wait for any individual kubernetes operation. Defaults to 300 seconds. -- `upgrade` (Block List, Max: 1) Configure 'upgrade' strategy for installing charts. WARNING: this may not be suitable for production use -- see the 'Upgrade Mode' section of the provider documentation, (see [below for nested schema](#nestedblock--upgrade)) +- `upgrade_install` (Boolean) If true, the provider will install the release at the specified version even if a release not controlled by the provider is present: this is equivalent to running 'helm upgrade --install' with the Helm CLI. WARNING: this may not be suitable for production use -- see the 'Upgrade Mode' note in the provider documentation. Defaults to `false`. - `values` (List of String) List of values in raw yaml format to pass to helm. - `verify` (Boolean) Verify the package before installing it.Defaults to `false`. - `version` (String) Specify the exact chart version to install. If this is not specified, the latest version is installed. @@ -115,18 +115,6 @@ Optional: - `type` (String) - -### Nested Schema for `upgrade` - -Required: - -- `enable` (Boolean) If true, the provider will install the release at the specified version even if a release not controlled by the provider is present: this is equivalent to using the 'helm upgrade' CLI tool rather than 'helm install'. - -Optional: - -- `install` (Boolean) When using the 'upgrade' strategy, install the release if it is not already present. This is equivalent to using the 'helm upgrade' CLI tool with the '--install' flag. - - ### Nested Schema for `metadata` @@ -353,16 +341,14 @@ having to manually import the release into terraform state each time. But the me different from the defaults and you can easily produce unexpected or undesirable results if you are not careful: using this approach in production is not necessarily recommended! -If upgrade mode is enabled by setting `enable` to `true` in the `upgrade` block, the provider will first check to see +If upgrade mode is enabled by setting the `upgrade_install` attribute to `true`, the provider will first check to see if a release with the given name already exists. If that release exists, it will attempt to upgrade the release to the state defined in the resource, using the same strategy as the [helm upgrade](https://helm.sh/docs/helm/helm_upgrade) command. In this case, the `generate_name`, `name_template` and `replace` attributes of the resource (if set) are ignored, as those attributes are not supported by helm's "upgrade" behavior. -If the release does _not_ exist, the behavior is controlled by the setting of the `install` attribute. If `install` -is `false` or unset, the apply stage will fail: the provider cannot upgrade a non-existent release. If `install` -is set to `true`, the provider will perform a from-scratch installation of the chart. In this case, all resource -attributes are honored. +If the release does not already exist, the provider will perform a from-scratch installation of the chart. In this +case, all resource attributes are honored. ## Import diff --git a/helm/resource_release.go b/helm/resource_release.go index 18197b366..d2a294307 100644 --- a/helm/resource_release.go +++ b/helm/resource_release.go @@ -386,26 +386,11 @@ func resourceRelease() *schema.Resource { Description: "The rendered manifest as JSON.", Computed: true, }, - "upgrade": { - Type: schema.TypeList, + "upgrade_install": { + Type: schema.TypeBool, Optional: true, - MaxItems: 1, - Description: "Configure 'upgrade' strategy for installing charts. WARNING: this may not be suitable for production use -- see the 'Upgrade Mode' section of the provider documentation,", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enable": { - Type: schema.TypeBool, - Required: true, - Description: "If true, the provider will install the release at the specified version even if a release not controlled by the provider is present: this is equivalent to using the 'helm upgrade' CLI tool rather than 'helm install'.", - }, - "install": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "When using the 'upgrade' strategy, install the release if it is not already present. This is equivalent to using the 'helm upgrade' CLI tool with the '--install' flag.", - }, - }, - }, + Default: defaultAttributes["upgrade_install"], + Description: "If true, the provider will install the release at the specified version even if a release not controlled by the provider is present: this is equivalent to running 'helm upgrade --install' with the Helm CLI. WARNING: this may not be suitable for production use -- see the 'Upgrade Mode' note in the provider documentation. Defaults to `false`.", }, "metadata": { Type: schema.TypeList, @@ -626,20 +611,11 @@ func resourceReleaseCreate(ctx context.Context, d *schema.ResourceData, meta int } var rel *release.Release - var installIfNoReleaseToUpgrade bool var releaseAlreadyExists bool var enableUpgradeStrategy bool releaseName := d.Get("name").(string) - upgradeBlock := d.Get("upgrade").([]interface{}) - if len(upgradeBlock) > 0 { - upgradeStrategyMap := upgradeBlock[0].(map[string]interface{}) - var ok bool - enableUpgradeStrategy, ok = upgradeStrategyMap["enable"].(bool) - if ok && enableUpgradeStrategy { - installIfNoReleaseToUpgrade, _ = upgradeStrategyMap["install"].(bool) - } - } + enableUpgradeStrategy = d.Get("upgrade_install").(bool) if enableUpgradeStrategy { // Check to see if there is already a release installed. @@ -656,7 +632,7 @@ func resourceReleaseCreate(ctx context.Context, d *schema.ResourceData, meta int } if enableUpgradeStrategy && releaseAlreadyExists { - debug("%s Upgrading chart", logID) + debug("%s Upgrade-installing chart installed out of band", logID) upgradeClient := action.NewUpgrade(actionConfig) upgradeClient.ChartPathOptions = *cpo @@ -682,7 +658,7 @@ func resourceReleaseCreate(ctx context.Context, d *schema.ResourceData, meta int debug("%s Upgrading chart", logID) rel, err = upgradeClient.Run(releaseName, c, values) - } else if (enableUpgradeStrategy && installIfNoReleaseToUpgrade && !releaseAlreadyExists) || !enableUpgradeStrategy { + } else { instClient := action.NewInstall(actionConfig) instClient.Replace = d.Get("replace").(bool) @@ -716,16 +692,12 @@ func resourceReleaseCreate(ctx context.Context, d *schema.ResourceData, meta int debug("%s Installing chart", logID) rel, err = instClient.Run(c, values) - } else if enableUpgradeStrategy && !installIfNoReleaseToUpgrade && !releaseAlreadyExists { - return diag.FromErr(fmt.Errorf( - "upgrade strategy enabled, but chart not already installed and install=false chartName=%v releaseName=%v enableUpgradeStrategy=%t installIfNoReleaseToUpgrade=%t releaseAlreadyExists=%t", - chartName, releaseName, enableUpgradeStrategy, installIfNoReleaseToUpgrade, releaseAlreadyExists)) } if err != nil && rel == nil { return diag.FromErr(err) } - if err != nil && rel != nil { + if err != nil { exists, existsErr := resourceReleaseExists(d, meta) if existsErr != nil { diff --git a/helm/resource_release_test.go b/helm/resource_release_test.go index 88fd89b2e..79f46abc0 100644 --- a/helm/resource_release_test.go +++ b/helm/resource_release_test.go @@ -111,7 +111,7 @@ func TestAccResourceRelease_emptyVersion(t *testing.T) { }) } -// "upgrade" without a previously installed release with --install (effectively equivalent to TestAccResourceRelease_basic) +// "upgrade_install" without a previously installed release (effectively equivalent to TestAccResourceRelease_basic) func TestAccResourceRelease_upgrade_with_install_coldstart(t *testing.T) { name := randName("basic") namespace := createRandomNamespace(t) @@ -124,7 +124,7 @@ func TestAccResourceRelease_upgrade_with_install_coldstart(t *testing.T) { CheckDestroy: testAccCheckHelmReleaseDestroy(namespace), Steps: []resource.TestStep{{ - Config: testAccHelmReleaseConfigWithUpgradeStrategy(testResourceName, namespace, name, "1.2.3", true, true), + Config: testAccHelmReleaseConfigWithUpgradeStrategy(testResourceName, namespace, name, "1.2.3", true), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("helm_release.test", "metadata.0.name", name), resource.TestCheckResourceAttr("helm_release.test", "metadata.0.namespace", namespace), @@ -136,7 +136,7 @@ func TestAccResourceRelease_upgrade_with_install_coldstart(t *testing.T) { resource.TestCheckResourceAttr("helm_release.test", "metadata.0.app_version", "1.19.5"), ), }, { - Config: testAccHelmReleaseConfigWithUpgradeStrategy(testResourceName, namespace, name, "1.2.3", true, true), + Config: testAccHelmReleaseConfigWithUpgradeStrategy(testResourceName, namespace, name, "1.2.3", true), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("helm_release.test", "metadata.0.revision", "1"), resource.TestCheckResourceAttr("helm_release.test", "metadata.0.version", "1.2.3"), @@ -177,25 +177,6 @@ func TestAccResourceRelease_upgrade_with_install_warmstart(t *testing.T) { }) } -// "upgrade" without a previously installed release without --install (will fail because nothing to upgrade) -func TestAccResourceRelease_upgrade_without_install(t *testing.T) { - name := randName("basic") - namespace := createRandomNamespace(t) - // Delete namespace automatically created by helm after checks - defer deleteNamespace(t, namespace) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckHelmReleaseDestroy(namespace), - Steps: []resource.TestStep{{ - Config: testAccHelmReleaseConfigWithUpgradeStrategy(testResourceName, namespace, name, "1.2.3", true, false), - ExpectError: regexp.MustCompile("upgrade strategy enabled, but chart not already installed and install=false"), - ExpectNonEmptyPlan: true, - }}, - }) -} - func TestAccResourceRelease_import(t *testing.T) { name := randName("import") namespace := createRandomNamespace(t) @@ -1010,7 +991,7 @@ func testAccHelmReleaseConfigEmptyVersion(resource, ns, name string) string { `, resource, name, ns, testRepositoryURL) } -func testAccHelmReleaseConfigWithUpgradeStrategy(resource, ns, name, version string, enabled, install bool) string { +func testAccHelmReleaseConfigWithUpgradeStrategy(resource, ns, name, version string, upgrade_install bool) string { return fmt.Sprintf(` resource "helm_release" "%s" { name = %q @@ -1020,10 +1001,7 @@ func testAccHelmReleaseConfigWithUpgradeStrategy(resource, ns, name, version str chart = "test-chart" version = %q - upgrade { - enable = %t - install = %t - } + upgrade_install = %t set { name = "foo" @@ -1044,7 +1022,7 @@ func testAccHelmReleaseConfigWithUpgradeStrategy(resource, ns, name, version str value = false } } - `, resource, name, ns, testRepositoryURL, version, enabled, install) + `, resource, name, ns, testRepositoryURL, version, upgrade_install) } func testAccHelmReleaseConfigWithUpgradeStrategyWarmstart(ns, name string) string { @@ -1056,10 +1034,8 @@ func testAccHelmReleaseConfigWithUpgradeStrategyWarmstart(ns, name string) strin chart = "./test-chart-1.2.3.tgz" version = "0.1.0" - upgrade { - enable = true - install = false - } + upgrade_install = true + set { name = "foo" value = "bar" diff --git a/templates/resources/release.md.tmpl b/templates/resources/release.md.tmpl index 561869c14..ea87ec1a5 100644 --- a/templates/resources/release.md.tmpl +++ b/templates/resources/release.md.tmpl @@ -86,16 +86,14 @@ having to manually import the release into terraform state each time. But the me different from the defaults and you can easily produce unexpected or undesirable results if you are not careful: using this approach in production is not necessarily recommended! -If upgrade mode is enabled by setting `enable` to `true` in the `upgrade` block, the provider will first check to see +If upgrade mode is enabled by setting the `upgrade_install` attribute to `true`, the provider will first check to see if a release with the given name already exists. If that release exists, it will attempt to upgrade the release to the state defined in the resource, using the same strategy as the [helm upgrade](https://helm.sh/docs/helm/helm_upgrade) command. In this case, the `generate_name`, `name_template` and `replace` attributes of the resource (if set) are ignored, as those attributes are not supported by helm's "upgrade" behavior. -If the release does _not_ exist, the behavior is controlled by the setting of the `install` attribute. If `install` -is `false` or unset, the apply stage will fail: the provider cannot upgrade a non-existent release. If `install` -is set to `true`, the provider will perform a from-scratch installation of the chart. In this case, all resource -attributes are honored. +If the release does not already exist, the provider will perform a from-scratch installation of the chart. In this +case, all resource attributes are honored. ## Import