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
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ require (
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-plugin v1.7.0
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/hashicorp/go-slug v0.16.3
github.com/hashicorp/go-tfe v1.74.1
github.com/hashicorp/go-slug v0.16.4
github.com/hashicorp/go-tfe v1.88.1-0.20250730143610-3c0fd4f54fc2
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/hcl/v2 v2.24.0
github.com/hashicorp/jsonapi v1.3.2
github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e
github.com/hashicorp/terraform-registry-address v0.3.0
github.com/hashicorp/terraform-svchost v0.1.1
github.com/hashicorp/terraform/internal/backend/remote-state/azure v0.0.0-00010101000000-000000000000
Expand Down Expand Up @@ -255,7 +255,7 @@ require (
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.155.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1126,15 +1126,15 @@ github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5O
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-slug v0.16.3 h1:pe0PMwz2UWN1168QksdW/d7u057itB2gY568iF0E2Ns=
github.com/hashicorp/go-slug v0.16.3/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ=
github.com/hashicorp/go-slug v0.16.4 h1:kI0mOUVjbBsyocwO29pZIQzzkBnfQNdU4eqlUpNdNVA=
github.com/hashicorp/go-slug v0.16.4/go.mod h1:THWVTAXwJEinbsp4/bBRcmbaO5EYNLTqxbG4tZ3gCYQ=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-tfe v1.74.1 h1:I/8fOwSYox17IZV7SULIQH0ZRPNL2g/biW6hHWnOTVY=
github.com/hashicorp/go-tfe v1.74.1/go.mod h1:kGHWMZ3HHjitgqON8nBZ4kPVJ3cLbzM4JMgmNVMs9aQ=
github.com/hashicorp/go-tfe v1.88.1-0.20250730143610-3c0fd4f54fc2 h1:BdSTYcozPpDhDiovPCSjrdYYvAyDGjlZOHm1fPMQbS4=
github.com/hashicorp/go-tfe v1.88.1-0.20250730143610-3c0fd4f54fc2/go.mod h1:6dUFMBKh0jkxlRsrw7bYD2mby0efdwE4dtlAuTogIzA=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
Expand All @@ -1152,8 +1152,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
github.com/hashicorp/jsonapi v1.3.2 h1:gP3fX2ZT7qXi+PbwieptzkspIohO2kCSiBUvUTBAbMs=
github.com/hashicorp/jsonapi v1.3.2/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e h1:xwy/1T0cxHWaLx2MM0g4BlaQc1BXn/9835mPrBqwSPU=
github.com/hashicorp/jsonapi v1.4.3-0.20250220162346-81a76b606f3e/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
Expand Down Expand Up @@ -1932,8 +1932,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
17 changes: 17 additions & 0 deletions internal/cloud/backend_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,23 @@ in order to capture the filesystem context the remote workspace expects:
}
}

if len(op.ActionTargets) != 0 {
if len(op.ActionTargets) > 1 {
// For now, we only support a single action from the command line.
// We've future proofed the API and inputs so we can send multiple
// but versions of Terraform will enforce this both here, and
// on the other side.
//
// It shouldn't actually be possible to reach here anyway - we're
// validating at the point the flag is read that it only has a
// single entry. But, we'll check again to be safe.
return nil, errors.New("unsupported arguments, at most 1 action can be invoked per operation")
}

// TODO: Switch this over to a slice once go-tfe is updated.
runOptions.InvokeActionAddr = tfe.String(op.ActionTargets[0].String())
}

if len(op.ForceReplace) != 0 {
runOptions.ReplaceAddrs = make([]string, 0, len(op.ForceReplace))
for _, addr := range op.ForceReplace {
Expand Down
39 changes: 39 additions & 0 deletions internal/cloud/backend_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,45 @@ func TestCloud_planWithRefreshOnly(t *testing.T) {
}
}

func TestCloud_planWithInvoke(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()

op, configCleanup, done := testOperationPlan(t, "./testdata/action")
defer configCleanup()
defer done(t)

addr, _ := addrs.ParseAbsActionInstanceStr("action.test_action.test")

op.ActionTargets = append(op.ActionTargets, addr)
op.Workspace = testBackendSingleWorkspaceName

run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("error starting operation: %v", err)
}

<-run.Done()
if run.Result != backendrun.OperationSuccess {
t.Fatal("expected plan operation to succeed")
}
if run.PlanEmpty {
t.Fatalf("expected plan to be non-empty")
}

// We should find a run inside the mock client that has the same
// target address we requested above.
runsAPI := b.client.Runs.(*MockRuns)
if got, want := len(runsAPI.Runs), 1; got != want {
t.Fatalf("wrong number of runs in the mock client %d; want %d", got, want)
}
for _, run := range runsAPI.Runs {
if diff := cmp.Diff("action.test_action.test", run.InvokeActionAddr); diff != "" {
t.Errorf("wrong TargetAddrs in the created run\n%s", diff)
}
}
}

func TestCloud_planWithTarget(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()
Expand Down
6 changes: 6 additions & 0 deletions internal/cloud/testdata/action/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

action "test_action" "action" {
config {
attr = "hello, world"
}
}
22 changes: 22 additions & 0 deletions internal/cloud/testdata/action/plan.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Terraform v0.11.7

Configuring remote state backend...
Initializing Terraform configuration...
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

Terraform will invoke the following action(s):

# action.test_action.test will be invoked
action "test_action" "test" {
config {
program = [
"curl",
"https://checkpoint-api.hashicorp.com/v1/check/terraform",
]
}
}

24 changes: 23 additions & 1 deletion internal/cloud/tfe_client_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,13 @@ func (m *MockRegistryModules) ReadVersion(ctx context.Context, moduleID tfe.Regi
panic("implement me")
}

func (m *MockRegistryModules) ReadTerraformRegistryModule(ctx context.Context, moduleID tfe.RegistryModuleID, version string) (*tfe.TerraformRegistryModule, error) {
//TODO implement me
panic("implement me")
}

var _ tfe.Runs = (*MockRuns)(nil)

type MockRuns struct {
sync.Mutex

Expand Down Expand Up @@ -1261,6 +1268,11 @@ func (m *MockRuns) List(ctx context.Context, workspaceID string, options *tfe.Ru
return rl, nil
}

func (m *MockRuns) ListForOrganization(ctx context.Context, organization string, options *tfe.RunListForOrganizationOptions) (*tfe.OrganizationRunList, error) {
//TODO implement me
panic("implement me")
}

func (m *MockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*tfe.Run, error) {
m.Lock()
defer m.Unlock()
Expand Down Expand Up @@ -1323,6 +1335,10 @@ func (m *MockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t
r.Plan.GeneratedConfiguration = true
}

if options.InvokeActionAddr != nil {
r.InvokeActionAddr = *options.InvokeActionAddr
}

w, ok := m.client.Workspaces.workspaceIDs[options.Workspace.ID]
if !ok {
return nil, tfe.ErrResourceNotFound
Expand Down Expand Up @@ -1388,7 +1404,8 @@ func (m *MockRuns) ReadWithOptions(ctx context.Context, runID string, options *t
hasChanges := r.IsDestroy ||
bytes.Contains(logs, []byte("1 to add")) ||
bytes.Contains(logs, []byte("1 to change")) ||
bytes.Contains(logs, []byte("1 to import"))
bytes.Contains(logs, []byte("1 to import")) ||
bytes.Contains(logs, []byte("Terraform will invoke the following action(s)"))
if hasChanges {
r.Actions.IsCancelable = false
r.Actions.IsConfirmable = true
Expand Down Expand Up @@ -1887,6 +1904,11 @@ func (m *MockVariables) List(ctx context.Context, workspaceID string, options *t
return vl, nil
}

func (m *MockVariables) ListAll(ctx context.Context, workspaceID string, options *tfe.VariableListOptions) (*tfe.VariableList, error) {
//TODO implement me
panic("implement me")
}

func (m *MockVariables) Create(ctx context.Context, workspaceID string, options tfe.VariableCreateOptions) (*tfe.Variable, error) {
v := &tfe.Variable{
ID: GenerateID("var-"),
Expand Down
Loading