diff --git a/CHANGELOG.md b/CHANGELOG.md index 56619f02..09c02699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.9.2] (September 1, 2022) + +BUG FIXES: + +- Fixes a bug in the `launchdarkly_feature_flag` resource where explicit defaults were not getting set for boolean flags upon creation. + ## [2.9.1] (August 24, 2022) BUG FIXES: diff --git a/launchdarkly/default_variations_helper.go b/launchdarkly/default_variations_helper.go index d177e507..0fd84f63 100644 --- a/launchdarkly/default_variations_helper.go +++ b/launchdarkly/default_variations_helper.go @@ -9,25 +9,36 @@ import ( func defaultVariationsFromResourceData(d *schema.ResourceData) (*ldapi.Defaults, error) { schemaVariations := d.Get(VARIATIONS).([]interface{}) + numberOfVariations := len(schemaVariations) variationType := d.Get(VARIATION_TYPE).(string) - if len(schemaVariations) == 0 && variationType == BOOL_VARIATION { - // default boolean variations - return &ldapi.Defaults{OnVariation: int32(0), OffVariation: int32(1)}, nil - } rawDefaults, ok := d.GetOk(DEFAULTS) if !ok { - return &ldapi.Defaults{OnVariation: int32(0), OffVariation: int32(len(schemaVariations) - 1)}, nil + defaultOff := numberOfVariations - 1 + if variationType == BOOL_VARIATION { + defaultOff = 1 // otherwise at this point it would be -1 + } + return &ldapi.Defaults{OnVariation: int32(0), OffVariation: int32(defaultOff)}, nil } defaultList := rawDefaults.([]interface{}) + if variationType == BOOL_VARIATION { + if numberOfVariations == 0 && len(defaultList) == 0 { + // default boolean variations + return &ldapi.Defaults{OnVariation: int32(0), OffVariation: int32(1)}, nil + } else { + // this allows us to confidence check the variation indices below + numberOfVariations = 2 + } + } + defaults := defaultList[0].(map[string]interface{}) on := defaults[ON_VARIATION].(int) off := defaults[OFF_VARIATION].(int) - if on >= len(schemaVariations) { - return nil, fmt.Errorf("default on_variation %v is out of range, must be between 0 and %v inclusive", on, len(schemaVariations)-1) + if on >= numberOfVariations { + return nil, fmt.Errorf("default on_variation %v is out of range, must be between 0 and %v inclusive", on, numberOfVariations-1) } - if off >= len(schemaVariations) { - return nil, fmt.Errorf("default off_variation %v is out of range, must be between 0 and %v inclusive", off, len(schemaVariations)-1) + if off >= numberOfVariations { + return nil, fmt.Errorf("default off_variation %v is out of range, must be between 0 and %v inclusive", off, numberOfVariations-1) } return &ldapi.Defaults{OnVariation: int32(on), OffVariation: int32(off)}, nil diff --git a/launchdarkly/default_variations_helper_test.go b/launchdarkly/default_variations_helper_test.go index 0ba48c72..efa9824c 100644 --- a/launchdarkly/default_variations_helper_test.go +++ b/launchdarkly/default_variations_helper_test.go @@ -18,7 +18,7 @@ func TestDefaultVariationsFromResourceData(t *testing.T) { expectedErr error }{ { - name: "no defaults on boolean", + name: "automatic boolean defaults", vars: map[string]interface{}{ VARIATIONS: []interface{}{ map[string]interface{}{ @@ -56,6 +56,28 @@ func TestDefaultVariationsFromResourceData(t *testing.T) { OffVariation: 1, }, }, + { + name: "explicit defautls overwrite default defaults", + vars: map[string]interface{}{ + VARIATIONS: []interface{}{ + map[string]interface{}{ + VALUE: "true", + }, + map[string]interface{}{ + VALUE: "false", + }, + }, + DEFAULTS: []interface{}{ + map[string]interface{}{ + ON_VARIATION: 1, + OFF_VARIATION: 0, + }}, + }, + expected: &ldapi.Defaults{ + OnVariation: 1, + OffVariation: 0, + }, + }, { name: "invalid default on value", vars: map[string]interface{}{ diff --git a/launchdarkly/resource_launchdarkly_feature_flag_test.go b/launchdarkly/resource_launchdarkly_feature_flag_test.go index 6ef1aed7..14715974 100644 --- a/launchdarkly/resource_launchdarkly_feature_flag_test.go +++ b/launchdarkly/resource_launchdarkly_feature_flag_test.go @@ -30,7 +30,7 @@ resource "launchdarkly_feature_flag" "basic" { include_in_snippet = true temporary = true defaults { - on_variation = 0 + on_variation = 1 off_variation = 1 } } @@ -290,7 +290,7 @@ resource "launchdarkly_feature_flag" "defaults" { name = "Feature flag with defaults" variation_type = "boolean" defaults { - on_variation = 0 + on_variation = 1 off_variation = 1 } } @@ -497,7 +497,7 @@ func withRandomProjectIncludeInSnippetTrue(randomProject, resource string) strin %s`, randomProject, resource) } -func TestAccFeatureFlag_Basic(t *testing.T) { +func TestAccFeatureFlag_BasicCreateAndUpdate(t *testing.T) { projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) resourceName := "launchdarkly_feature_flag.basic" resource.ParallelTest(t, resource.TestCase{ @@ -518,6 +518,9 @@ func TestAccFeatureFlag_Basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "variations.#", "2"), resource.TestCheckResourceAttr(resourceName, "variations.0.value", "true"), resource.TestCheckResourceAttr(resourceName, "variations.1.value", "false"), + // bool variation defaults should default to 0 and 1 if not set + resource.TestCheckResourceAttr(resourceName, "defaults.0.on_variation", "0"), + resource.TestCheckResourceAttr(resourceName, "defaults.0.off_variation", "1"), resource.TestCheckNoResourceAttr(resourceName, MAINTAINER_ID), ), }, @@ -527,29 +530,6 @@ func TestAccFeatureFlag_Basic(t *testing.T) { // TODO: While we have to account for usingMobileKey being set to true by default, we cant use importStateVerify // ImportStateVerify: true, }, - }, - }) -} - -func TestAccFeatureFlag_Update(t *testing.T) { - projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - resourceName := "launchdarkly_feature_flag.basic" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: withRandomProject(projectKey, testAccFeatureFlagBasic), - Check: resource.ComposeTestCheckFunc( - testAccCheckProjectExists("launchdarkly_project.test"), - testAccCheckFeatureFlagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, NAME, "Basic feature flag"), - resource.TestCheckResourceAttr(resourceName, KEY, "basic-flag"), - resource.TestCheckResourceAttr(resourceName, PROJECT_KEY, projectKey), - ), - }, { Config: withRandomProject(projectKey, testAccFeatureFlagUpdate), Check: resource.ComposeTestCheckFunc( @@ -564,7 +544,7 @@ func TestAccFeatureFlag_Update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.1", "update"), resource.TestCheckResourceAttr(resourceName, INCLUDE_IN_SNIPPET, "true"), resource.TestCheckResourceAttr(resourceName, TEMPORARY, "true"), - resource.TestCheckResourceAttr(resourceName, "defaults.0.on_variation", "0"), + resource.TestCheckResourceAttr(resourceName, "defaults.0.on_variation", "1"), resource.TestCheckResourceAttr(resourceName, "defaults.0.off_variation", "1"), ), }, @@ -755,7 +735,7 @@ func TestAccFeatureFlag_InvalidMaintainer(t *testing.T) { }) } -func TestAccFeatureFlag_CreateMultivariate(t *testing.T) { +func TestAccFeatureFlag_CreateAndUpdateMultivariate(t *testing.T) { projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) resourceName := "launchdarkly_feature_flag.multivariate" resource.ParallelTest(t, resource.TestCase{ @@ -797,78 +777,6 @@ func TestAccFeatureFlag_CreateMultivariate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "custom_properties.1.value.0", "very special custom property"), ), }, - }, - }) -} - -func TestAccFeatureFlag_CreateMultivariate2(t *testing.T) { - projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - resourceName := "launchdarkly_feature_flag.multivariate_numbers" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: withRandomProject(projectKey, testAccFeatureFlagCreateMultivariate2), - Check: resource.ComposeTestCheckFunc( - testAccCheckProjectExists("launchdarkly_project.test"), - testAccCheckFeatureFlagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, NAME, "multivariate flag 2 name"), - resource.TestCheckResourceAttr(resourceName, KEY, "multivariate-flag-2"), - resource.TestCheckResourceAttr(resourceName, PROJECT_KEY, projectKey), - resource.TestCheckResourceAttr(resourceName, DESCRIPTION, "this is a multivariate flag to test big number values"), - resource.TestCheckResourceAttr(resourceName, VARIATION_TYPE, "number"), - resource.TestCheckResourceAttr(resourceName, "variations.#", "3"), - resource.TestCheckResourceAttr(resourceName, "variations.0.description", "a description"), - resource.TestCheckResourceAttr(resourceName, "variations.0.name", "variation1"), - resource.TestCheckResourceAttr(resourceName, "variations.0.value", "86400000"), - resource.TestCheckResourceAttr(resourceName, "variations.1.value", "123"), - resource.TestCheckResourceAttr(resourceName, "variations.2.value", "123456789"), - resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), - resource.TestCheckResourceAttr(resourceName, "tags.0", "is"), - resource.TestCheckResourceAttr(resourceName, "tags.1", "this"), - resource.TestCheckResourceAttr(resourceName, "tags.2", "unordered"), - ), - }, - }, - }) -} - -func TestAccFeatureFlag_UpdateMultivariate(t *testing.T) { - projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) - resourceName := "launchdarkly_feature_flag.multivariate" - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { - testAccPreCheck(t) - }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: withRandomProject(projectKey, testAccFeatureFlagCreateMultivariate), - Check: resource.ComposeTestCheckFunc( - testAccCheckProjectExists("launchdarkly_project.test"), - testAccCheckFeatureFlagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "variations.#", "3"), - resource.TestCheckResourceAttr(resourceName, "variations.0.description", "a description"), - resource.TestCheckResourceAttr(resourceName, "variations.0.name", "variation1"), - resource.TestCheckResourceAttr(resourceName, "variations.0.value", "string1"), - resource.TestCheckResourceAttr(resourceName, "variations.1.value", "string2"), - resource.TestCheckResourceAttr(resourceName, "variations.2.value", "another option"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.#", "2"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.key", "some.property"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.name", "Some Property"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.value.#", "3"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.value.0", "value1"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.value.1", "value2"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.0.value.2", "value3"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.1.key", "some.property2"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.1.name", "Some Property"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.1.value.#", "1"), - resource.TestCheckResourceAttr(resourceName, "custom_properties.1.value.0", "very special custom property"), - ), - }, { Config: withRandomProject(projectKey, testAccFeatureFlagUpdateMultivariate), Check: resource.ComposeTestCheckFunc( @@ -922,6 +830,41 @@ func TestAccFeatureFlag_UpdateMultivariate(t *testing.T) { }) } +func TestAccFeatureFlag_CreateMultivariate2(t *testing.T) { + projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) + resourceName := "launchdarkly_feature_flag.multivariate_numbers" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: withRandomProject(projectKey, testAccFeatureFlagCreateMultivariate2), + Check: resource.ComposeTestCheckFunc( + testAccCheckProjectExists("launchdarkly_project.test"), + testAccCheckFeatureFlagExists(resourceName), + resource.TestCheckResourceAttr(resourceName, NAME, "multivariate flag 2 name"), + resource.TestCheckResourceAttr(resourceName, KEY, "multivariate-flag-2"), + resource.TestCheckResourceAttr(resourceName, PROJECT_KEY, projectKey), + resource.TestCheckResourceAttr(resourceName, DESCRIPTION, "this is a multivariate flag to test big number values"), + resource.TestCheckResourceAttr(resourceName, VARIATION_TYPE, "number"), + resource.TestCheckResourceAttr(resourceName, "variations.#", "3"), + resource.TestCheckResourceAttr(resourceName, "variations.0.description", "a description"), + resource.TestCheckResourceAttr(resourceName, "variations.0.name", "variation1"), + resource.TestCheckResourceAttr(resourceName, "variations.0.value", "86400000"), + resource.TestCheckResourceAttr(resourceName, "variations.1.value", "123"), + resource.TestCheckResourceAttr(resourceName, "variations.2.value", "123456789"), + resource.TestCheckResourceAttr(resourceName, "tags.#", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.0", "is"), + resource.TestCheckResourceAttr(resourceName, "tags.1", "this"), + resource.TestCheckResourceAttr(resourceName, "tags.2", "unordered"), + ), + }, + }, + }) +} + func TestAccFeatureFlag_UpdateDefaults(t *testing.T) { projectKey := acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum) resourceName := "launchdarkly_feature_flag.defaults" @@ -936,7 +879,7 @@ func TestAccFeatureFlag_UpdateDefaults(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckProjectExists("launchdarkly_project.test"), testAccCheckFeatureFlagExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "defaults.0.on_variation", "0"), + resource.TestCheckResourceAttr(resourceName, "defaults.0.on_variation", "1"), resource.TestCheckResourceAttr(resourceName, "defaults.0.off_variation", "1"), ), },