-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(TFECO-7339) feat: Support context references within orchestrate bloc…
…ks in deployment configuration (#1813) * feat: Support context references within orchestrate blocks in deployment configuration * chore: add changie entry * Bump terraform-schema to `c383931` * refactor: simplify code
- Loading branch information
Showing
8 changed files
with
242 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
kind: ENHANCEMENTS | ||
body: Support context references within orchestrate blocks in deployment configuration | ||
time: 2024-08-29T14:02:10.75361+02:00 | ||
custom: | ||
Issue: "1813" | ||
Repository: terraform-ls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package jobs | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/hcl-lang/lang" | ||
"github.com/hashicorp/hcl-lang/reference" | ||
"github.com/hashicorp/hcl/v2" | ||
"github.com/hashicorp/terraform-ls/internal/features/stacks/state" | ||
"github.com/zclconf/go-cty/cty" | ||
) | ||
|
||
var orchestrateContextScopeId = lang.ScopeId("orchestrate_context") | ||
|
||
// used in various places | ||
var changesAttributes = map[string]cty.Type{ | ||
"total": cty.Number, | ||
"add": cty.Number, | ||
"change": cty.Number, | ||
"import": cty.Number, | ||
"remove": cty.Number, | ||
"move": cty.Number, | ||
"forget": cty.Number, | ||
"defer": cty.Number, | ||
} | ||
var changesType = cty.Object(changesAttributes) | ||
|
||
func builtinReferences(record *state.StackRecord) reference.Targets { | ||
targets := make(reference.Targets, 0) | ||
|
||
if record == nil { | ||
return targets | ||
} | ||
|
||
// The ranges of the orchestrate blocks as we have to create targets with these ranges | ||
// to ensure they are only available within orchestrate blocks | ||
for _, rule := range record.Meta.OrchestrationRules { | ||
rng := rule.Range | ||
|
||
// create the static base targets (like context.operation, context.success, etc.) | ||
targets = append(targets, baseTargets(rng)...) | ||
// create the static plan targets (like context.plan.mode, context.plan.applyable, etc.) | ||
targets = append(targets, staticPlanTargets(rng)...) | ||
|
||
// targets for each component for the component_changes map (like context.plan.component_changes["vpc"].total) | ||
for name := range record.Meta.Components { | ||
addr := lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "component_changes"}, | ||
lang.IndexStep{Key: cty.StringVal(name)}, | ||
} | ||
targets = append(targets, changesTargets(addr, rng, &name)...) | ||
} | ||
} | ||
|
||
return targets | ||
} | ||
|
||
func baseTargets(rng hcl.Range) reference.Targets { | ||
var diagType = cty.Object(map[string]cty.Type{ | ||
"summary": cty.String, | ||
"detail": cty.String, | ||
}) | ||
|
||
return reference.Targets{ | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "operation"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.String, | ||
Description: lang.Markdown("The operation. Either \"plan\" or \"apply\""), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "success"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Bool, | ||
Description: lang.Markdown("Whether the operation that triggered the evaluation of this check completed successfully"), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "errors"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Set(diagType), | ||
Description: lang.Markdown("A set of diagnostic error message objects"), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "warnings"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Set(diagType), | ||
Description: lang.Markdown("A set of diagnostic warning message objects"), | ||
}, | ||
} | ||
} | ||
|
||
// staticPlanTargets returns the targets for the plan context that are not dependent on the component names | ||
func staticPlanTargets(rng hcl.Range) reference.Targets { | ||
targets := reference.Targets{ | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Object(map[string]cty.Type{ | ||
"mode": cty.String, | ||
"applyable": cty.Bool, | ||
"changes": changesType, | ||
"component_changes": cty.Map(changesType), | ||
"replans": cty.Number, | ||
"deployment": cty.DynamicPseudoType, | ||
}), | ||
Description: lang.Markdown("An object including data about the current plan"), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "mode"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.String, | ||
Description: lang.Markdown("The plan mode, one of \"normal\", \"refresh-only\", or \"destroy\""), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "applyable"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Bool, | ||
Description: lang.Markdown("A boolean, whether or not the plan can be applied"), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "replans"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.Number, | ||
Description: lang.Markdown("The number of replans in this plan's sequence, starting at 0"), | ||
}, | ||
{ | ||
LocalAddr: lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "deployment"}, | ||
}, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: cty.DynamicPseudoType, | ||
Description: lang.Markdown("A direct reference to the current deployment. Can be used to compare with deployments blocks, e.g. context.plan.deployment == deployment.production"), | ||
}, | ||
} | ||
// utility to add all the changes targets like context.plan.changes.total, context.plan.changes.add, etc. | ||
targets = append(targets, changesTargets(lang.Address{ | ||
lang.RootStep{Name: "context"}, | ||
lang.AttrStep{Name: "plan"}, | ||
lang.AttrStep{Name: "changes"}, | ||
}, rng, nil)...) | ||
return targets | ||
} | ||
|
||
func changesTargets(address lang.Address, rng hcl.Range, componentName *string) reference.Targets { | ||
descriptionAppendix := "for all components" // default | ||
if componentName != nil { | ||
descriptionAppendix = fmt.Sprintf("for the component \"%s\"", *componentName) | ||
} | ||
|
||
nestedTargets := make(reference.Targets, 0) | ||
for key, typ := range changesAttributes { | ||
a := append(address.Copy(), lang.AttrStep{Name: key}) | ||
nestedTargets = append(nestedTargets, reference.Target{ | ||
Name: key, | ||
LocalAddr: a, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
ScopeId: orchestrateContextScopeId, | ||
Type: typ, | ||
Description: lang.Markdown(fmt.Sprintf("The number of %s changes %s", key, descriptionAppendix)), | ||
}) | ||
} | ||
|
||
return append(nestedTargets, reference.Target{ | ||
LocalAddr: address, | ||
TargetableFromRangePtr: rng.Ptr(), | ||
Type: changesType, | ||
Name: "changes", | ||
Description: lang.Markdown(fmt.Sprintf("The changes that are planned %s", descriptionAppendix)), | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters