diff --git a/api/v1alpha1/common.go b/api/v1alpha1/common.go index 132124dd..f2949a66 100644 --- a/api/v1alpha1/common.go +++ b/api/v1alpha1/common.go @@ -36,8 +36,9 @@ type RunHistoryPolicy struct { } type RemediationStrategy struct { - AutoApply bool `json:"autoApply,omitempty"` - OnError OnErrorRemediationStrategy `json:"onError,omitempty"` + AutoApply bool `json:"autoApply,omitempty"` + ApplyWithoutPlanArtifact *bool `json:"applyWithoutPlanArtifact,omitempty"` + OnError OnErrorRemediationStrategy `json:"onError,omitempty"` } type OnErrorRemediationStrategy struct { @@ -108,6 +109,17 @@ func GetRunHistoryPolicy(repository *TerraformRepository, layer *TerraformLayer) } } +func GetApplyWithoutPlanArtifactEnabled(repository *TerraformRepository, layer *TerraformLayer) bool { + enabled := false + if repository.Spec.RemediationStrategy.ApplyWithoutPlanArtifact != nil { + enabled = *repository.Spec.RemediationStrategy.ApplyWithoutPlanArtifact + } + if layer.Spec.RemediationStrategy.ApplyWithoutPlanArtifact != nil { + enabled = *layer.Spec.RemediationStrategy.ApplyWithoutPlanArtifact + } + return enabled +} + func GetRemediationStrategy(repo *TerraformRepository, layer *TerraformLayer) RemediationStrategy { result := RemediationStrategy{ AutoApply: false, diff --git a/api/v1alpha1/common_test.go b/api/v1alpha1/common_test.go index ccf7b2f6..c35691af 100644 --- a/api/v1alpha1/common_test.go +++ b/api/v1alpha1/common_test.go @@ -230,6 +230,84 @@ func TestGetTerragruntEnabled(t *testing.T) { } } +func TestGetApplyWithoutPlanArtifactEnabled(t *testing.T) { + tt := []struct { + name string + repository *configv1alpha1.TerraformRepository + layer *configv1alpha1.TerraformLayer + expected bool + }{ + { + "OnlyRepositoryEnabling", + &configv1alpha1.TerraformRepository{ + Spec: configv1alpha1.TerraformRepositorySpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{true}[0], + }, + }, + }, + &configv1alpha1.TerraformLayer{}, + true, + }, + { + "OnlyLayerEnabling", + &configv1alpha1.TerraformRepository{}, + &configv1alpha1.TerraformLayer{ + Spec: configv1alpha1.TerraformLayerSpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{true}[0], + }, + }, + }, + true, + }, + { + "DisabledInRepositoryEnabledInLayer", + &configv1alpha1.TerraformRepository{ + Spec: configv1alpha1.TerraformRepositorySpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{false}[0], + }, + }, + }, + &configv1alpha1.TerraformLayer{ + Spec: configv1alpha1.TerraformLayerSpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{true}[0], + }, + }, + }, + true, + }, + { + "EnabledInRepositoryDisabledInLayer", + &configv1alpha1.TerraformRepository{ + Spec: configv1alpha1.TerraformRepositorySpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{true}[0], + }, + }, + }, + &configv1alpha1.TerraformLayer{ + Spec: configv1alpha1.TerraformLayerSpec{ + RemediationStrategy: configv1alpha1.RemediationStrategy{ + ApplyWithoutPlanArtifact: &[]bool{false}[0], + }, + }, + }, + false, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + result := configv1alpha1.GetApplyWithoutPlanArtifactEnabled(tc.repository, tc.layer) + if tc.expected != result { + t.Errorf("different enabled status computed: expected %t go %t", tc.expected, result) + } + }) + } +} + func TestOverrideRunnerSpec(t *testing.T) { tt := []struct { name string diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c7b80eff..83b8a16d 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -157,6 +157,11 @@ func (in *OverrideRunnerSpec) DeepCopy() *OverrideRunnerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemediationStrategy) DeepCopyInto(out *RemediationStrategy) { *out = *in + if in.ApplyWithoutPlanArtifact != nil { + in, out := &in.ApplyWithoutPlanArtifact, &out.ApplyWithoutPlanArtifact + *out = new(bool) + **out = **in + } in.OnError.DeepCopyInto(&out.OnError) } diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 2944492d..d43dcfe7 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -50,7 +50,7 @@ type TerraformExec interface { Install() error Init(string) error Plan() error - Apply() error + Apply(bool) error Show(string) ([]byte, error) } @@ -304,7 +304,12 @@ func (r *Runner) apply() (string, error) { return "", err } log.Print("launching terraform apply") - err = r.exec.Apply() + if configv1alpha1.GetApplyWithoutPlanArtifactEnabled(r.repository, r.layer) { + log.Infof("applying without reusing plan artifact from previous plan run") + err = r.exec.Apply(false) + } else { + err = r.exec.Apply(true) + } if err != nil { log.Errorf("error executing terraform apply: %s", err) return "", err diff --git a/internal/runner/terraform/terraform.go b/internal/runner/terraform/terraform.go index a769e804..48d4dd5b 100644 --- a/internal/runner/terraform/terraform.go +++ b/internal/runner/terraform/terraform.go @@ -84,9 +84,14 @@ func (t *Terraform) Plan() error { return nil } -func (t *Terraform) Apply() error { +func (t *Terraform) Apply(usePlanArtifact bool) error { t.verbose() - err := t.exec.Apply(context.Background(), tfexec.DirOrPlan(t.planArtifactPath)) + applyOpts := []tfexec.ApplyOption{} + if usePlanArtifact { + applyOpts = append(applyOpts, tfexec.DirOrPlan(t.planArtifactPath)) + } + + err := t.exec.Apply(context.Background(), applyOpts...) if err != nil { return err } diff --git a/internal/runner/terragrunt/terragrunt.go b/internal/runner/terragrunt/terragrunt.go index 48d38d6a..39b6064d 100644 --- a/internal/runner/terragrunt/terragrunt.go +++ b/internal/runner/terragrunt/terragrunt.go @@ -86,8 +86,12 @@ func (t *Terragrunt) Plan() error { return nil } -func (t *Terragrunt) Apply() error { - options := append(t.getDefaultOptions("apply"), t.planArtifactPath) +func (t *Terragrunt) Apply(usePlanArtifact bool) error { + options := append(t.getDefaultOptions("apply"), "-auto-approve") + if usePlanArtifact { + options = append(options, t.planArtifactPath) + } + cmd := exec.Command(t.execPath, options...) verbose(cmd) cmd.Dir = t.workingDir diff --git a/manifests/crds/config.terraform.padok.cloud_terraformlayers.yaml b/manifests/crds/config.terraform.padok.cloud_terraformlayers.yaml index cfa3c2c5..b8dfa192 100644 --- a/manifests/crds/config.terraform.padok.cloud_terraformlayers.yaml +++ b/manifests/crds/config.terraform.padok.cloud_terraformlayers.yaml @@ -2004,6 +2004,8 @@ spec: type: string remediationStrategy: properties: + applyWithoutPlanArtifact: + type: boolean autoApply: type: boolean onError: diff --git a/manifests/crds/config.terraform.padok.cloud_terraformrepositories.yaml b/manifests/crds/config.terraform.padok.cloud_terraformrepositories.yaml index 6c05c783..e929e218 100644 --- a/manifests/crds/config.terraform.padok.cloud_terraformrepositories.yaml +++ b/manifests/crds/config.terraform.padok.cloud_terraformrepositories.yaml @@ -1990,6 +1990,8 @@ spec: type: object remediationStrategy: properties: + applyWithoutPlanArtifact: + type: boolean autoApply: type: boolean onError: diff --git a/manifests/install.yaml b/manifests/install.yaml index 916ff00f..cddba10e 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -1887,6 +1887,8 @@ spec: type: string remediationStrategy: properties: + applyWithoutPlanArtifact: + type: boolean autoApply: type: boolean onError: @@ -4048,6 +4050,8 @@ spec: type: object remediationStrategy: properties: + applyWithoutPlanArtifact: + type: boolean autoApply: type: boolean onError: