Skip to content

Commit

Permalink
Merge pull request #299 from heroku/issue/299/davidji99-provide-abili…
Browse files Browse the repository at this point in the history
…ty-to-not-s

Provide ability to not set `all_config_vars` on heroku_app resource
  • Loading branch information
davidji99 authored Mar 10, 2021
2 parents a0ff56f + da49baa commit 55ac958
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 23 deletions.
11 changes: 10 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,18 @@ The following arguments are supported:
provided, it will be sourced from the `HEROKU_HEADERS` environment variable
(if set).

* `customizations` - (Optional) Various attributes altering the behavior of certain resources.
Only a single `customizations` block may be specified, and it supports the following arguments:

* `set_app_all_config_vars_in_state` - (Optional) Controls whether the `heroku_app.all_config_vars` attribute
is set in the state file. The aforementioned attribute stores a snapshot of all config vars in Terraform state,
even if they are not defined in Terraform. This means sensitive Heroku add-on config vars,
such as Postgres' `DATABASE_URL`, are always accessible in the state.
Set to `false` to only track managed config vars in the state. Defaults to `true`.

* `delays` - (Optional) Delays help mitigate issues that can arise due to
Heroku's eventually consistent data model. Only a single `delays` block may be
specified and it supports the following arguments:
specified, and it supports the following arguments:

* `post_app_create_delay` - (Optional) The number of seconds to wait after an
app is created. Default is to wait 5 seconds.
Expand Down
9 changes: 5 additions & 4 deletions docs/resources/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@ The following attributes are exported:
at by default.
* `heroku_hostname` - A hostname for the Heroku application, suitable
for pointing DNS records.
* `all_config_vars` - A map of all of the configuration variables that
exist for the app, containing both those set by Terraform and those
set externally. (These are treated as "sensitive" so that
their values are redacted in console output.)
* `all_config_vars` - A map of all configuration variables that
exist for the app, containing both those set by Terraform and those
set externally. (These are treated as "sensitive" so that
their values are redacted in console output.) This attribute is not set in state if the `provider`
attribute `set_app_all_config_vars_in_state` is `false`.
* `uuid` - The unique UUID of the Heroku app. **NOTE:** Use this for `null_resource` triggers.

## Import
Expand Down
50 changes: 35 additions & 15 deletions heroku/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,28 @@ const (
DefaultPostDomainCreateDelay = int64(5)

// Default custom timeouts
DefaultAddonCreateTimeout = int64(20)
DefaultAddonCreateTimeout = int64(20)
DefaultSetAppAllConfigVarsInState = true
)

type Config struct {
Api *heroku.Service
APIKey string
DebugHTTP bool
Email string
Headers http.Header
Api *heroku.Service
APIKey string
DebugHTTP bool
Email string
Headers http.Header
URL string

// Delays
PostAppCreateDelay int64
PostDomainCreateDelay int64
PostSpaceCreateDelay int64
URL string

// Custom Timeouts
// Timeouts
AddonCreateTimeout int64

// Customization
SetAppAllConfigVarsInState bool
}

func (c Config) String() string {
Expand All @@ -48,11 +54,12 @@ func (c Config) String() string {

func NewConfig() *Config {
config := &Config{
Headers: make(http.Header),
PostAppCreateDelay: DefaultPostAppCreateDelay,
PostDomainCreateDelay: DefaultPostDomainCreateDelay,
PostSpaceCreateDelay: DefaultPostSpaceCreateDelay,
AddonCreateTimeout: DefaultAddonCreateTimeout,
Headers: make(http.Header),
PostAppCreateDelay: DefaultPostAppCreateDelay,
PostDomainCreateDelay: DefaultPostDomainCreateDelay,
PostSpaceCreateDelay: DefaultPostSpaceCreateDelay,
AddonCreateTimeout: DefaultAddonCreateTimeout,
SetAppAllConfigVarsInState: DefaultSetAppAllConfigVarsInState,
}
if logging.IsDebugOrHigher() {
config.DebugHTTP = true
Expand Down Expand Up @@ -103,10 +110,23 @@ func (c *Config) applySchema(d *schema.ResourceData) (err error) {
c.URL = url.(string)
}

if v, ok := d.GetOk("customizations"); ok {
vL := v.([]interface{})
if len(vL) > 1 {
return fmt.Errorf("Provider configuration error: only one customizations block is permitted")
}
for _, v := range vL {
customizations := v.(map[string]interface{})
if v, ok := customizations["set_app_all_config_vars_in_state"].(bool); ok {
c.SetAppAllConfigVarsInState = v
}
}
}

if v, ok := d.GetOk("delays"); ok {
vL := v.([]interface{})
if len(vL) > 1 {
return fmt.Errorf("Provider configuration error: only 1 delays config is permitted")
return fmt.Errorf("Provider configuration error: only one delays block is permitted")
}
for _, v := range vL {
delaysConfig := v.(map[string]interface{})
Expand All @@ -125,7 +145,7 @@ func (c *Config) applySchema(d *schema.ResourceData) (err error) {
if v, ok := d.GetOk("timeouts"); ok {
vL := v.([]interface{})
if len(vL) > 1 {
return fmt.Errorf("provider configuration error: only 1 timeouts config is permitted")
return fmt.Errorf("provider configuration error: only one timeouts block is permitted")
}

for _, v := range vL {
Expand Down
15 changes: 15 additions & 0 deletions heroku/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ func Provider() *schema.Provider {
DefaultFunc: schema.EnvDefaultFunc("HEROKU_API_URL", heroku.DefaultURL),
},

"customizations": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"set_app_all_config_vars_in_state": {
Type: schema.TypeBool,
Optional: true,
Default: DefaultSetAppAllConfigVarsInState,
},
},
},
},

"delays": {
Type: schema.TypeList,
MaxItems: 1,
Expand Down
30 changes: 30 additions & 0 deletions heroku/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,45 @@ import (
helper "github.com/heroku/terraform-provider-heroku/v4/helper/test"
)

const (
ProviderNameHeroku = "heroku"
)

var providers []*schema.Provider
var testAccProviderFactories map[string]func() (*schema.Provider, error)
var testAccProviders map[string]*schema.Provider
var testAccProvider *schema.Provider
var testAccConfig *helper.TestConfig

func testAccProviderFactoriesInternal(providers []*schema.Provider) map[string]func() (*schema.Provider, error) {
return testAccProviderFactoriesInit(providers, []string{ProviderNameHeroku})
}

// testAccProviderFactoriesInit creates ProviderFactories for the provider under testing.
func testAccProviderFactoriesInit(providers []*schema.Provider, providerNames []string) map[string]func() (*schema.Provider, error) {
var factories = make(map[string]func() (*schema.Provider, error), len(providerNames))

for _, name := range providerNames {
p := Provider()

factories[name] = func() (*schema.Provider, error) {
return p, nil
}

if providers != nil {
providers = append(providers, p)
}
}

return factories
}

func init() {
testAccProvider = Provider()
testAccProviders = map[string]*schema.Provider{
"heroku": testAccProvider,
}
testAccProviderFactories = testAccProviderFactoriesInit(providers, []string{ProviderNameHeroku})
testAccConfig = helper.NewTestConfig()
}

Expand Down
12 changes: 9 additions & 3 deletions heroku/resource_heroku_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ func setAppDetails(d *schema.ResourceData, app *application) (err error) {
}

func resourceHerokuAppRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Config).Api
config := meta.(*Config)
client := config.Api

care := make(map[string]struct{})
configVars := make(map[string]string)
Expand Down Expand Up @@ -407,8 +408,13 @@ func resourceHerokuAppRead(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARN] Error setting sensitive config vars: %s", err)
}

if err := d.Set("all_config_vars", app.Vars); err != nil {
log.Printf("[WARN] Error setting all_config_vars: %s", err)
// Set `all_config_vars` to empty map initially. Only set this attribute
// if set_app_all_config_vars_in_state is `true`.
d.Set("all_config_vars", map[string]string{})
if config.SetAppAllConfigVarsInState {
if err := d.Set("all_config_vars", app.Vars); err != nil {
log.Printf("[WARN] Error setting all_config_vars: %s", err)
}
}

buildpacksErr := d.Set("buildpacks", app.Buildpacks)
Expand Down
48 changes: 48 additions & 0 deletions heroku/resource_heroku_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,35 @@ func TestAccHerokuApp_Basic(t *testing.T) {
"heroku_app.foobar", "config_vars.FOO", "bar"),
resource.TestCheckResourceAttr(
"heroku_app.foobar", "internal_routing", "false"),
resource.TestCheckResourceAttr(
"heroku_app.foobar", "all_config_vars.%", "1"),
),
},
},
})
}

func TestAccHerokuApp_DontSetAllConfigVars(t *testing.T) {
appName := fmt.Sprintf("tftest-%s", acctest.RandString(10))
appStack := "heroku-20"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckHerokuAppConfig_DontSetConfigVars(appName, appStack),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"heroku_app.foobar", "name", appName),
resource.TestCheckResourceAttrSet(
"heroku_app.foobar", "uuid"),
resource.TestCheckResourceAttr(
"heroku_app.foobar", "config_vars.FOO", "bar"),
resource.TestCheckResourceAttr(
"heroku_app.foobar", "internal_routing", "false"),
resource.TestCheckResourceAttr(
"heroku_app.foobar", "all_config_vars.%", "0"),
),
},
},
Expand Down Expand Up @@ -952,3 +981,22 @@ resource "heroku_app" "foobar" {
}
}`, appName, org, locked)
}

func testAccCheckHerokuAppConfig_DontSetConfigVars(appName, appStack string) string {
return fmt.Sprintf(`
provider "heroku" {
customizations {
set_app_all_config_vars_in_state = false
}
}
resource "heroku_app" "foobar" {
name = "%s"
stack = "%s"
region = "us"
config_vars = {
FOO = "bar"
}
}`, appName, appStack)
}

0 comments on commit 55ac958

Please sign in to comment.