From 7a3a6296e5228f6bcf602e36d699dae8d42ae4be Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Thu, 29 Aug 2024 13:53:15 +0200 Subject: [PATCH] feat: early decode orchestrate blocks in deployment configs Their ranges are required for context references that are only available within orchestrate blocks --- earlydecoder/stacks/decoder.go | 6 ++++++ earlydecoder/stacks/decoder_test.go | 7 +++++-- earlydecoder/stacks/load_deploy.go | 19 ++++++++++++++++++- earlydecoder/stacks/load_stack.go | 6 ++++-- earlydecoder/stacks/schema.go | 4 ++++ internal/schema/refscope/scopes.go | 1 + .../schema/stacks/1.9/orchestrate_block.go | 5 +++-- stack/meta.go | 5 +++-- stack/orchestrate.go | 11 +++++++++++ 9 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 stack/orchestrate.go diff --git a/earlydecoder/stacks/decoder.go b/earlydecoder/stacks/decoder.go index 047bca5..a8147e2 100644 --- a/earlydecoder/stacks/decoder.go +++ b/earlydecoder/stacks/decoder.go @@ -89,6 +89,11 @@ func LoadStack(path string, files map[string]*hcl.File) (*stack.Meta, hcl.Diagno stores[key] = *store } + orchestrationRules := make(map[string]stack.OrchestrationRule) + for key, rule := range mod.OrchestrationRules { + orchestrationRules[key] = *rule + } + return &stack.Meta{ Path: path, Filenames: filenames, @@ -98,6 +103,7 @@ func LoadStack(path string, files map[string]*hcl.File) (*stack.Meta, hcl.Diagno ProviderRequirements: providerRequirements, Deployments: deployments, Stores: stores, + OrchestrationRules: orchestrationRules, }, diags } diff --git a/earlydecoder/stacks/decoder_test.go b/earlydecoder/stacks/decoder_test.go index 0ad39f3..95853c3 100644 --- a/earlydecoder/stacks/decoder_test.go +++ b/earlydecoder/stacks/decoder_test.go @@ -45,6 +45,7 @@ func TestLoadStack(t *testing.T) { ProviderRequirements: map[string]stack.ProviderRequirement{}, Deployments: map[string]stack.Deployment{}, Stores: map[string]stack.Store{}, + OrchestrationRules: map[string]stack.OrchestrationRule{}, }, nil, }, @@ -72,6 +73,7 @@ func TestLoadStack(t *testing.T) { ProviderRequirements: map[string]stack.ProviderRequirement{}, Deployments: map[string]stack.Deployment{}, Stores: map[string]stack.Store{}, + OrchestrationRules: map[string]stack.OrchestrationRule{}, }, nil, }, @@ -97,8 +99,9 @@ func TestLoadStack(t *testing.T) { "aws": {Source: tfaddr.MustParseProviderSource("hashicorp/aws"), VersionConstraints: version.MustConstraints(version.NewConstraint("~> 5.7.0"))}, "random": {Source: tfaddr.MustParseProviderSource("hashicorp/random"), VersionConstraints: version.MustConstraints(version.NewConstraint("~> 3.5.1"))}, }, - Deployments: map[string]stack.Deployment{}, - Stores: map[string]stack.Store{}, + Deployments: map[string]stack.Deployment{}, + Stores: map[string]stack.Store{}, + OrchestrationRules: map[string]stack.OrchestrationRule{}, }, nil, }, diff --git a/earlydecoder/stacks/load_deploy.go b/earlydecoder/stacks/load_deploy.go index 2f8ab57..2c46cb4 100644 --- a/earlydecoder/stacks/load_deploy.go +++ b/earlydecoder/stacks/load_deploy.go @@ -6,6 +6,7 @@ package earlydecoder import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform-schema/stack" "github.com/zclconf/go-cty/cty" ) @@ -51,8 +52,24 @@ func loadDeployFromFile(file *hcl.File, ds *decodedStack) hcl.Diagnostics { ds.Stores[storeName] = &stack.Store{ Type: storeType, } - } + case "orchestrate": + if len(block.Labels) != 2 || block.Labels[0] == "" || block.Labels[1] == "" { + continue + } + + body, ok := block.Body.(*hclsyntax.Body) + if !ok { + continue + } + + ruleType := block.Labels[0] + ruleName := block.Labels[1] + ds.OrchestrationRules[ruleName] = &stack.OrchestrationRule{ + Type: ruleType, + Range: body.SrcRange, + } + } } return diags diff --git a/earlydecoder/stacks/load_stack.go b/earlydecoder/stacks/load_stack.go index 8096440..ebae1ea 100644 --- a/earlydecoder/stacks/load_stack.go +++ b/earlydecoder/stacks/load_stack.go @@ -23,8 +23,9 @@ type decodedStack struct { Outputs map[string]*stack.Output ProviderRequirements map[string]*providerRequirement - Deployments map[string]*stack.Deployment - Stores map[string]*stack.Store + Deployments map[string]*stack.Deployment + Stores map[string]*stack.Store + OrchestrationRules map[string]*stack.OrchestrationRule } func newDecodedStack() *decodedStack { @@ -35,6 +36,7 @@ func newDecodedStack() *decodedStack { ProviderRequirements: make(map[string]*providerRequirement), Deployments: make(map[string]*stack.Deployment), Stores: make(map[string]*stack.Store), + OrchestrationRules: make(map[string]*stack.OrchestrationRule), } } diff --git a/earlydecoder/stacks/schema.go b/earlydecoder/stacks/schema.go index f416c76..b3c48ca 100644 --- a/earlydecoder/stacks/schema.go +++ b/earlydecoder/stacks/schema.go @@ -37,6 +37,10 @@ var deploymentRootSchema = &hcl.BodySchema{ Type: "store", LabelNames: []string{"type", "name"}, }, + { + Type: "orchestrate", + LabelNames: []string{"type", "name"}, + }, }, } diff --git a/internal/schema/refscope/scopes.go b/internal/schema/refscope/scopes.go index a92e589..99e22be 100644 --- a/internal/schema/refscope/scopes.go +++ b/internal/schema/refscope/scopes.go @@ -20,4 +20,5 @@ var ( ComponentScope = lang.ScopeId("component") IdentityTokenScope = lang.ScopeId("identity_token") StoreScope = lang.ScopeId("store") + OrchestrateContext = lang.ScopeId("orchestrate_context") ) diff --git a/internal/schema/stacks/1.9/orchestrate_block.go b/internal/schema/stacks/1.9/orchestrate_block.go index e60984b..1d44aca 100644 --- a/internal/schema/stacks/1.9/orchestrate_block.go +++ b/internal/schema/stacks/1.9/orchestrate_block.go @@ -20,7 +20,8 @@ func orchestrateBlockSchema() *schema.BlockSchema { Description: lang.PlainText("Rule Type"), IsDepKey: true, Completable: true, - // TODO: auto_approve is the only one supported now, but converged, replan, rollout are possible values for the first label on the block + // TODO: auto_approve is the only one supported now, but converged, replan, rollout, deferral_replan are possible values for the first label on the block + // TODO: complete the available labels }, { Name: "name", @@ -37,7 +38,7 @@ func orchestrateBlockSchema() *schema.BlockSchema { Attributes: map[string]*schema.AttributeSchema{ "condition": { Description: lang.Markdown("The condition must evaluate to true or false"), - Constraint: schema.LiteralType{Type: cty.Bool}, + Constraint: schema.AnyExpression{OfType: cty.Bool}, }, "reason": { Description: lang.Markdown("The reason must be a string"), diff --git a/stack/meta.go b/stack/meta.go index b94169e..4597cdf 100644 --- a/stack/meta.go +++ b/stack/meta.go @@ -12,6 +12,7 @@ type Meta struct { Outputs map[string]Output ProviderRequirements map[string]ProviderRequirement - Deployments map[string]Deployment - Stores map[string]Store + Deployments map[string]Deployment + Stores map[string]Store + OrchestrationRules map[string]OrchestrationRule } diff --git a/stack/orchestrate.go b/stack/orchestrate.go new file mode 100644 index 0000000..f99b250 --- /dev/null +++ b/stack/orchestrate.go @@ -0,0 +1,11 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package stack + +import "github.com/hashicorp/hcl/v2" + +type OrchestrationRule struct { + Type string + Range hcl.Range +}