Skip to content
Draft
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
23 changes: 3 additions & 20 deletions internal/backend/local/backend_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (

"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"

"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend/backendrun"
Expand Down Expand Up @@ -437,7 +435,7 @@ func (b *Local) opApply(
SetVariables: applyTimeValues,
ProviderLocks: providerLocksSnapshot(op.DependencyLocks),
PolicyClient: lr.PolicyClient,
PolicyResults: plan.PolicyResults,
PolicyResults: views.NewStreamingPolicyResults(op.View),
})
}()

Expand All @@ -446,23 +444,8 @@ func (b *Local) opApply(
}
diags = diags.Append(applyDiags)

// Print the policy results we found during apply
policyResultCount := 0
if plan.PolicyResults != nil {
policyResultCount = plan.PolicyResults.Len()
}
var polRenderSpan trace.Span
polRenderSpanEnd := func() {}
if policyResultCount > 0 {
_, polRenderSpan = tracer().Start(stopCtx, "terraform.local.apply.render_policy_results",
trace.WithAttributes(
attribute.Int("apply.policy_results", policyResultCount),
),
)
polRenderSpanEnd = func() { polRenderSpan.End() }
}
op.View.PolicyResults(plan.PolicyResults, nil)
polRenderSpanEnd()
// Policy results (if any) were streamed to the view live during the apply
// walk, so there is nothing to render here.

// Even on error with an empty state, the state value should not be nil.
// Return early here to prevent corrupting any existing state.
Expand Down
29 changes: 9 additions & 20 deletions internal/backend/local/backend_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import (
"io"
"log"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"

"github.com/hashicorp/terraform/internal/backend/backendrun"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/genconfig"
"github.com/hashicorp/terraform/internal/logging"
"github.com/hashicorp/terraform/internal/plans"
Expand Down Expand Up @@ -113,6 +111,12 @@ func (b *Local) opPlan(
// resulting state is always just the input state.
runningOp.State = lr.InputState

// Stream policy evaluation results to the view as they are produced during
// the plan walk.
if lr.PlanOpts.PolicyClient != nil {
lr.PlanOpts.PolicyResults = views.NewStreamingPolicyResults(op.View)
}

// Perform the plan in a goroutine so we can be interrupted
var plan *plans.Plan
var planDiags tfdiags.Diagnostics
Expand Down Expand Up @@ -225,23 +229,8 @@ func (b *Local) opPlan(

op.View.Plan(plan, schemas)

// Report all policy results that may have accumulated during the plan
policyResultCount := 0
if plan.PolicyResults != nil {
policyResultCount = plan.PolicyResults.Len()
}
var polRenderSpan trace.Span
polRenderSpanEnd := func() {}
if policyResultCount > 0 {
_, polRenderSpan = tracer().Start(stopCtx, "terraform.local.plan.render_policy_results",
trace.WithAttributes(
attribute.Int("plan.policy_results", policyResultCount),
),
)
polRenderSpanEnd = func() { polRenderSpan.End() }
}
op.View.PolicyResults(plan.PolicyResults, nil)
polRenderSpanEnd()
// Policy results (if any) were streamed to the view live during the plan
// walk, so there is nothing to render here.

// If we've accumulated any diagnostics along the way then we'll show them
// here just before we show the summary and next steps. This can potentially
Expand Down
10 changes: 4 additions & 6 deletions internal/command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/getproviders/providerreqs"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/policy"
"github.com/hashicorp/terraform/internal/providercache"
"github.com/hashicorp/terraform/internal/states"
Expand Down Expand Up @@ -77,7 +76,7 @@ func (c *InitCommand) Run(args []string) int {
return c.run(initArgs, view)
}

func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, earlyRoot *configs.Module, upgrade bool, view views.Init, policyClient policy.Client) (output bool, abort bool, policyResults *plans.PolicyResults, diags tfdiags.Diagnostics) {
func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, earlyRoot *configs.Module, upgrade bool, view views.Init, policyClient policy.Client) (output bool, abort bool, diags tfdiags.Diagnostics) {
testModules := false // We can also have modules buried in test files.
for _, file := range earlyRoot.Tests {
for _, run := range file.Runs {
Expand All @@ -89,7 +88,7 @@ func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, ear

if len(earlyRoot.ModuleCalls) == 0 && !testModules {
// Nothing to do
return false, false, nil, nil
return false, false, nil
}

ctx, span := tracer.Start(ctx, "install modules", trace.WithAttributes(
Expand All @@ -110,11 +109,10 @@ func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, ear
}
hooks := []initwd.ModuleInstallHook{uiHook}
if policyClient != nil {
policyResults = plans.NewPolicyResults()
policyHook := &policyModuleInstallHook{
client: policyClient,
rootModule: earlyRoot,
policyResults: policyResults,
policyResults: views.NewStreamingPolicyResults(view),
}
hooks = append(hooks, policyHook)
}
Expand All @@ -139,7 +137,7 @@ func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, ear
}
}

return true, installAbort, policyResults, diags
return true, installAbort, diags
}

func (c *InitCommand) initCloud(ctx context.Context, root *configs.Module, extraConfig arguments.FlagNameValueSlice, viewType arguments.ViewType, view views.Init) (be backend.Backend, output bool, diags tfdiags.Diagnostics) {
Expand Down
9 changes: 2 additions & 7 deletions internal/command/init_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/policy"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/terraform"
Expand Down Expand Up @@ -210,7 +209,7 @@ func (c *InitCommand) run(initArgs *arguments.Init, view views.Init) int {
)
}

policyResults := plans.NewPolicyResults()
policyResults := views.NewStreamingPolicyResults(view)

var pssLocks *depsfile.Locks // May end up containing 0 or 1 lock.
if rootModEarly.StateStore != nil {
Expand Down Expand Up @@ -245,7 +244,6 @@ Please use \"terraform state migrate -upgrade\" to upgrade the state store provi
configProvidersOutput, pssLocks, safeInitAction, stateStoreProviderAuthResult, configProviderDiags = c.getProvidersFromPSSConfig(ctx, rootModEarly, alteredPreviousLocks, allowUpgrade, initArgs.PluginPath, initArgs.Lockfile, view)
diags = diags.Append(configProviderDiags)
if configProviderDiags.HasErrors() {
view.PolicyResults(policyResults, nil)
view.Diagnostics(diags)
return 1
}
Expand Down Expand Up @@ -368,9 +366,8 @@ Please use \"terraform state migrate -upgrade\" to upgrade the state store provi
}

if initArgs.Get {
modsOutput, modsAbort, policyResults, modsDiags := c.getModules(ctx, path, initArgs.TestsDirectory, rootModEarly, initArgs.Upgrade, view, policyClient)
modsOutput, modsAbort, modsDiags := c.getModules(ctx, path, initArgs.TestsDirectory, rootModEarly, initArgs.Upgrade, view, policyClient)
diags = diags.Append(modsDiags)
view.PolicyResults(policyResults, nil)
if modsAbort || modsDiags.HasErrors() {
view.Diagnostics(diags)
return 1
Expand Down Expand Up @@ -439,7 +436,6 @@ Please use \"terraform state migrate -upgrade\" to upgrade the state store provi
stateProvidersOutput, providerLocks, stateProvidersDiags := c.getProviders(ctx, config, state, initArgs.Upgrade, pssLocks, initArgs.PluginPath, view, providerHook)
diags = diags.Append(stateProvidersDiags)
if stateProvidersDiags.HasErrors() {
view.PolicyResults(policyResults, nil)
view.Diagnostics(diags)
return 1
}
Expand All @@ -465,7 +461,6 @@ Please use \"terraform state migrate -upgrade\" to upgrade the state store provi
// If we accumulated any warnings along the way that weren't accompanied
// by errors then we'll output them here so that the success message is
// still the final thing shown.
view.PolicyResults(policyResults, nil)
view.Diagnostics(diags)
_, cloud := back.(*cloud.Cloud)
output := views.OutputInitSuccessMessage
Expand Down
4 changes: 2 additions & 2 deletions internal/command/meta_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type policyModuleInstallHook struct {
initwd.ModuleInstallHookImpl
client policy.Client
rootModule *configs.Module
policyResults *plans.PolicyResults
policyResults plans.PolicyResult
}

// ModuleSourceResolved implements [initwd.ModuleInstallHook] and is called after a module source is resolved, and enables policy evaluation for the module before
Expand Down Expand Up @@ -137,7 +137,7 @@ var _ providercache.InstallerHook = &providerPolicyHook{}
// providerPolicyHook enables policy evaluation during provider installation.
type providerPolicyHook struct {
client policy.Client
policyResults *plans.PolicyResults
policyResults plans.PolicyResult
rootModule *configs.Module
}

Expand Down
9 changes: 9 additions & 0 deletions internal/command/views/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ProviderInstaller interface {
type Init interface {
Diagnostics(diags tfdiags.Diagnostics)
PolicyResults(results *plans.PolicyResults, setupDiags policy.Diagnostics)
StreamPolicyResult(addr string, result plans.PolicyEvaluation)
Output(messageCode InitMessageCode, params ...any)
LogInitMessage(messageCode InitMessageCode, params ...any)
Log(message string, params ...any)
Expand Down Expand Up @@ -70,6 +71,10 @@ func (v *InitHuman) PolicyResults(results *plans.PolicyResults, setupDiags polic
v.view.PolicyResults(results, setupDiags)
}

func (v *InitHuman) StreamPolicyResult(addr string, result plans.PolicyEvaluation) {
v.view.StreamPolicyResult(addr, result)
}

func (v *InitHuman) Output(messageCode InitMessageCode, params ...any) {
v.view.streams.Println(v.PrepareMessage(messageCode, params...))
}
Expand Down Expand Up @@ -117,6 +122,10 @@ func (v *InitJSON) PolicyResults(results *plans.PolicyResults, setupDiags policy
v.view.PolicyResults(results, setupDiags)
}

func (v *InitJSON) StreamPolicyResult(addr string, result plans.PolicyEvaluation) {
v.view.StreamPolicyResult(addr, result)
}

func (v *InitJSON) Output(messageCode InitMessageCode, params ...any) {
// don't add empty messages to json output
preppedMessage := v.PrepareMessage(messageCode, params...)
Expand Down
86 changes: 47 additions & 39 deletions internal/command/views/json_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,53 @@ func (v *JSONView) Outputs(outputs json.Outputs) {
)
}

func (v *JSONView) logPolicyResult(addr string, result plans.PolicyEvaluation) {
// Log all the info messages
for _, enforcement := range result.EvaluationResponse.Enforcements {
if enforcement.Message == "" {
continue
}
var src []byte
if enforcement.LocalRange != nil {
src = v.view.configSources()[enforcement.LocalRange.Filename]
}
info := json.NewPolicyInfo(src, enforcement)
args := []any{
"type", json.MessagePolicyInfo,
"target_address", addr,
json.MessagePolicyInfo, info,
"@policy", "true",
"result", enforcement.Result.String(),
}
if enforcement.Policy != nil {
args = append(args, "policy_metadata", json.MetadataFromEnforcement(enforcement))
}
v.log.Info("Policy info", args...)
}

for _, diag := range result.EvaluationResponse.Diagnostics {
v.logPolicyDiagnostic(diag, "target_address", addr)
}

for _, policy := range result.EvaluationResponse.Policies {
v.log.Info(
"Policy Result",
"type", json.MessagePolicyEvaluationResult,
"result", policy.Result.String(),
"target_address", addr,
"policy_address", policy.Address,
"@policy", "true",
"policy_metadata", json.MetadataFromPolicy(*policy),
)
}
}

func (v *JSONView) StreamPolicyResult(addr string, result plans.PolicyEvaluation) {
v.logPolicyResult(addr, result)
}

func (v *JSONView) PolicyResults(results *plans.PolicyResults, setupDiags policy.Diagnostics) {
// Log all non-policy-specific diagnostics if any.

for _, diag := range setupDiags {
v.logPolicyDiagnostic(diag)
}
Expand All @@ -157,44 +202,7 @@ func (v *JSONView) PolicyResults(results *plans.PolicyResults, setupDiags policy
}

for addr, result := range results.Iter() {
// Log all the info messages
for _, enforcement := range result.EvaluationResponse.Enforcements {
if enforcement.Message == "" {
continue
}
var src []byte
if enforcement.LocalRange != nil {
src = v.view.configSources()[enforcement.LocalRange.Filename]
}
info := json.NewPolicyInfo(src, enforcement)
args := []any{
"type", json.MessagePolicyInfo,
"target_address", addr,
json.MessagePolicyInfo, info,
"@policy", "true",
"result", enforcement.Result.String(),
}
if enforcement.Policy != nil {
args = append(args, "policy_metadata", json.MetadataFromEnforcement(enforcement))
}
v.log.Info("Policy info", args...)
}

for _, diag := range result.EvaluationResponse.Diagnostics {
v.logPolicyDiagnostic(diag, "target_address", addr)
}

for _, policy := range result.EvaluationResponse.Policies {
v.log.Info(
"Policy Result",
"type", json.MessagePolicyEvaluationResult,
"result", policy.Result.String(),
"target_address", addr,
"policy_address", policy.Address,
"@policy", "true",
"policy_metadata", json.MetadataFromPolicy(*policy),
)
}
v.logPolicyResult(addr, result)
}
}

Expand Down
10 changes: 10 additions & 0 deletions internal/command/views/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type Operation interface {
Diagnostics(diags tfdiags.Diagnostics)

PolicyResults(results *plans.PolicyResults, setupDiags policy.Diagnostics)

StreamPolicyResult(addr string, result plans.PolicyEvaluation)
}

func NewOperation(vt arguments.ViewType, inAutomation bool, view *View) Operation {
Expand Down Expand Up @@ -138,6 +140,10 @@ func (v *OperationHuman) PolicyResults(results *plans.PolicyResults, setupDiags
v.view.PolicyResults(results, setupDiags)
}

func (v *OperationHuman) StreamPolicyResult(addr string, result plans.PolicyEvaluation) {
v.view.StreamPolicyResult(addr, result)
}

func (v *OperationHuman) PlannedChange(change *plans.ResourceInstanceChangeSrc) {
// PlannedChange is primarily for machine-readable output in order to
// get a per-resource-instance change description. We don't use it
Expand Down Expand Up @@ -301,6 +307,10 @@ func (v *OperationJSON) PolicyResults(results *plans.PolicyResults, setupDiags p
v.view.PolicyResults(results, setupDiags)
}

func (v *OperationJSON) StreamPolicyResult(addr string, result plans.PolicyEvaluation) {
v.view.StreamPolicyResult(addr, result)
}

const fatalInterrupt = `
Two interrupts received. Exiting immediately. Note that data loss may have occurred.
`
Expand Down
Loading
Loading