Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide ability to disable backend init so that validate-all can be run without a backend #761

Merged
merged 4 commits into from
Jul 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ type terragruntInclude struct {

// Configuration for Terraform remote state as parsed from a terragrunt.hcl config file
type remoteStateConfigFile struct {
Backend string `hcl:"backend,attr"`
Config cty.Value `hcl:"config,attr"`
Backend string `hcl:"backend,attr"`
DisableInit *bool `hcl:"disable_init,attr"`
Config cty.Value `hcl:"config,attr"`
}

func (remoteState *remoteStateConfigFile) String() string {
Expand Down Expand Up @@ -523,6 +524,10 @@ func convertToTerragruntConfig(terragruntConfigFromFile *terragruntConfigFile, c
remoteState.Backend = terragruntConfigFromFile.RemoteState.Backend
remoteState.Config = remoteStateConfig

if terragruntConfigFromFile.RemoteState.DisableInit != nil {
remoteState.DisableInit = *terragruntConfigFromFile.RemoteState.DisableInit
}

remoteState.FillDefaults()
if err := remoteState.Validate(); err != nil {
return nil, err
Expand Down
23 changes: 15 additions & 8 deletions remote/remote_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import (

// Configuration for Terraform remote state
type RemoteState struct {
Backend string
Config map[string]interface{}
Backend string
DisableInit bool
Config map[string]interface{}
}

func (remoteState *RemoteState) String() string {
return fmt.Sprintf("RemoteState{Backend = %v, Config = %v}", remoteState.Backend, remoteState.Config)
return fmt.Sprintf("RemoteState{Backend = %v, DisableInit = %v, Config = %v}", remoteState.Backend, remoteState.DisableInit, remoteState.Config)
}

type RemoteStateInitializer interface {
// Return true if remote state needs to be initialized
NeedsInitialization(config map[string]interface{}, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error)
NeedsInitialization(remoteState *RemoteState, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error)

// Initialize the remote state
Initialize(config map[string]interface{}, terragruntOptions *options.TerragruntOptions) error
Initialize(remoteState *RemoteState, terragruntOptions *options.TerragruntOptions) error

// Return the config that should be passed on to terraform via -backend-config cmd line param
// Allows the Backends to filter and/or modify the configuration given from the user
Expand Down Expand Up @@ -54,7 +55,7 @@ func (remoteState *RemoteState) Initialize(terragruntOptions *options.Terragrunt
terragruntOptions.Logger.Printf("Initializing remote state for the %s backend", remoteState.Backend)
initializer, hasInitializer := remoteStateInitializers[remoteState.Backend]
if hasInitializer {
return initializer.Initialize(remoteState.Config, terragruntOptions)
return initializer.Initialize(remoteState, terragruntOptions)
}

return nil
Expand All @@ -71,14 +72,18 @@ func (remoteState *RemoteState) NeedsInit(terragruntOptions *options.TerragruntO
return false, err
}

if remoteState.DisableInit {
return false, nil
}

// Remote state not configured
if state == nil {
return true, nil
}

if initializer, hasInitializer := remoteStateInitializers[remoteState.Backend]; hasInitializer {
// Remote state initializer says initialization is necessary
return initializer.NeedsInitialization(remoteState.Config, state.Backend, terragruntOptions)
return initializer.NeedsInitialization(remoteState, state.Backend, terragruntOptions)
} else if state.IsRemote() && remoteState.differsFrom(state.Backend, terragruntOptions) {
// If there's no remote state initializer, then just compare the the config values
return true, nil
Expand Down Expand Up @@ -127,8 +132,10 @@ func terraformStateConfigEqual(existingConfig map[string]interface{}, newConfig

// Convert the RemoteState config into the format used by the terraform init command
func (remoteState RemoteState) ToTerraformInitArgs() []string {

config := remoteState.Config
if remoteState.DisableInit {
return []string{"-backend=false"}
}

initializer, hasInitializer := remoteStateInitializers[remoteState.Backend]
if hasInitializer {
Expand Down
14 changes: 9 additions & 5 deletions remote/remote_state_s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,16 @@ type S3Initializer struct{}
//
// 1. Any of the existing backend settings are different than the current config
// 2. The configured S3 bucket or DynamoDB table does not exist
func (s3Initializer S3Initializer) NeedsInitialization(config map[string]interface{}, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error) {
if !configValuesEqual(config, existingBackend, terragruntOptions) {
func (s3Initializer S3Initializer) NeedsInitialization(remoteState *RemoteState, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error) {
if remoteState.DisableInit {
return false, nil
}

if !configValuesEqual(remoteState.Config, existingBackend, terragruntOptions) {
return true, nil
}

s3Config, err := parseS3Config(config)
s3Config, err := parseS3Config(remoteState.Config)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -186,8 +190,8 @@ func configValuesEqual(config map[string]interface{}, existingBackend *Terraform

// Initialize the remote state S3 bucket specified in the given config. This function will validate the config
// parameters, create the S3 bucket if it doesn't already exist, and check that versioning is enabled.
func (s3Initializer S3Initializer) Initialize(config map[string]interface{}, terragruntOptions *options.TerragruntOptions) error {
s3ConfigExtended, err := parseExtendedS3Config(config)
func (s3Initializer S3Initializer) Initialize(remoteState *RemoteState, terragruntOptions *options.TerragruntOptions) error {
s3ConfigExtended, err := parseExtendedS3Config(remoteState.Config)
if err != nil {
return err
}
Expand Down
17 changes: 17 additions & 0 deletions remote/remote_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ func TestToTerraformInitArgsUnknownBackend(t *testing.T) {
assertTerraformInitArgsEqual(t, args, "-backend-config=encrypt=true -backend-config=bucket=my-bucket -backend-config=key=terraform.tfstate -backend-config=region=us-east-1")
}

func TestToTerraformInitArgsInitDisabled(t *testing.T) {
t.Parallel()

remoteState := RemoteState{
Backend: "s3",
DisableInit: true,
Config: map[string]interface{}{
"encrypt": true,
"bucket": "my-bucket",
"key": "terraform.tfstate",
"region": "us-east-1"},
}
args := remoteState.ToTerraformInitArgs()

assertTerraformInitArgsEqual(t, args, "-backend=false")
}

func TestToTerraformInitArgsNoBackendConfigs(t *testing.T) {
t.Parallel()

Expand Down