Skip to content

Commit 5c65640

Browse files
authored
feat: add support for instance action (#3390)
* feat: add support for action reboot * Fix * add support to skip open tofu tests * add skip for opentofu * Fix tests * Fix zone * fix lint
1 parent 1f00731 commit 5c65640

File tree

14 files changed

+2057
-9
lines changed

14 files changed

+2057
-9
lines changed

.github/workflows/acceptance-tests.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,4 @@ jobs:
305305
SCW_SECRET_KEY: "11111111-1111-1111-1111-111111111111"
306306
SCW_ENABLE_BETA: true
307307
TF_ACC_LOG: trace
308+
TF_ACC_OPENTOFU: true

.github/workflows/documentation.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ jobs:
5959
- uses: actions/setup-go@v6
6060
with:
6161
go-version: stable
62-
- uses: hashicorp/setup-terraform@v3
62+
- name: Install Terraform
63+
uses: hashicorp/setup-terraform@v3
6364
- run: go tool tfplugindocs validate
6465
- run: rm -fr ./docs
6566
- run: go tool tfplugindocs generate
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
subcategory: "Instances"
3+
page_title: "Scaleway: scaleway_instance_server_action"
4+
---
5+
6+
# scaleway_instance_server_action (Action)
7+
8+
<!-- action schema generated by tfplugindocs -->
9+
## Schema
10+
11+
### Required
12+
13+
- `action` (String) Type of action to perform
14+
- `server_id` (String) Server id to send the action to
15+
16+
### Optional
17+
18+
- `wait` (Boolean) Wait for server to finish action
19+
- `zone` (String) Zone of server to send the action to
20+
21+

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/hashicorp/go-multierror v1.1.1
2323
github.com/hashicorp/go-retryablehttp v0.7.8
2424
github.com/hashicorp/terraform-plugin-framework v1.16.0
25+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.1-0.20250909114857-8e55d8ccabdb
2526
github.com/hashicorp/terraform-plugin-go v0.29.0
2627
github.com/hashicorp/terraform-plugin-log v0.9.0
2728
github.com/hashicorp/terraform-plugin-mux v0.21.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ github.com/hashicorp/terraform-plugin-docs v0.24.0 h1:YNZYd+8cpYclQyXbl1EEngbld8
328328
github.com/hashicorp/terraform-plugin-docs v0.24.0/go.mod h1:YLg+7LEwVmRuJc0EuCw0SPLxuQXw5mW8iJ5ml/kvi+o=
329329
github.com/hashicorp/terraform-plugin-framework v1.16.0 h1:tP0f+yJg0Z672e7levixDe5EpWwrTrNryPM9kDMYIpE=
330330
github.com/hashicorp/terraform-plugin-framework v1.16.0/go.mod h1:0xFOxLy5lRzDTayc4dzK/FakIgBhNf/lC4499R9cV4Y=
331+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.1-0.20250909114857-8e55d8ccabdb h1:wRiOv+xaGRrBuc8r774OtrELwQCiSLLQNrkH00ZLO90=
332+
github.com/hashicorp/terraform-plugin-framework-validators v0.18.1-0.20250909114857-8e55d8ccabdb/go.mod h1:vU2y54LtDNHGLjDD7LH/if+4KBKZ5ljTrgDdrM6y8Pc=
331333
github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU=
332334
github.com/hashicorp/terraform-plugin-go v0.29.0/go.mod h1:vYZbIyvxyy0FWSmDHChCqKvI40cFTDGSb3D8D70i9GM=
333335
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=

internal/acctest/opentofu.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package acctest
2+
3+
import (
4+
"os"
5+
6+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/env"
7+
)
8+
9+
func IsRunningOpenTofu() bool {
10+
return os.Getenv(env.AccRunningOpenTofu) == "true"
11+
}

internal/env/env.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ const (
1414
AppendUserAgent = "TF_APPEND_USER_AGENT"
1515
// AccDomainRegistration if set to "true" will trigger acceptance test for domain registration
1616
AccDomainRegistration = "TF_ACC_DOMAIN_REGISTRATION"
17+
// AccRunningOpenTofu is set to "true" in the CI to document that we are using OpenTofu. It can be helpful to skip
18+
// tests that are not yet compatible with OpenTofu
19+
AccRunningOpenTofu = "TF_ACC_OPENTOFU"
1720
)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package instance
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
8+
"github.com/hashicorp/terraform-plugin-framework/action"
9+
"github.com/hashicorp/terraform-plugin-framework/action/schema"
10+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
"github.com/scaleway/scaleway-sdk-go/api/instance/v1"
13+
"github.com/scaleway/scaleway-sdk-go/scw"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
16+
)
17+
18+
var (
19+
_ action.Action = (*ServerAction)(nil)
20+
_ action.ActionWithConfigure = (*ServerAction)(nil)
21+
)
22+
23+
type ServerAction struct {
24+
instanceAPI *instance.API
25+
}
26+
27+
func (a *ServerAction) Configure(ctx context.Context, req action.ConfigureRequest, resp *action.ConfigureResponse) {
28+
if req.ProviderData == nil {
29+
return
30+
}
31+
32+
m, ok := req.ProviderData.(*meta.Meta)
33+
if !ok {
34+
resp.Diagnostics.AddError(
35+
"Unexpected Action Configure Type",
36+
fmt.Sprintf("Expected *scw.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
37+
)
38+
39+
return
40+
}
41+
42+
client := m.ScwClient()
43+
a.instanceAPI = instance.NewAPI(client)
44+
}
45+
46+
func (a *ServerAction) Metadata(ctx context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
47+
resp.TypeName = req.ProviderTypeName + "_instance_server_action"
48+
}
49+
50+
type ServerActionModel struct {
51+
ServerID types.String `tfsdk:"server_id"`
52+
Zone types.String `tfsdk:"zone"`
53+
Action types.String `tfsdk:"action"`
54+
Wait types.Bool `tfsdk:"wait"`
55+
}
56+
57+
func NewServerAction() action.Action {
58+
return &ServerAction{}
59+
}
60+
61+
func (a *ServerAction) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) {
62+
actionsValues := instance.ServerAction("").Values()
63+
64+
actionStringValues := make([]string, 0, len(actionsValues))
65+
for _, actionValue := range actionsValues {
66+
actionStringValues = append(actionStringValues, actionValue.String())
67+
}
68+
69+
resp.Schema = schema.Schema{
70+
Attributes: map[string]schema.Attribute{
71+
"action": schema.StringAttribute{
72+
Required: true,
73+
Description: "Type of action to perform",
74+
Validators: []validator.String{
75+
stringvalidator.OneOfCaseInsensitive(actionStringValues...),
76+
},
77+
},
78+
"server_id": schema.StringAttribute{
79+
Required: true,
80+
Description: "Server id to send the action to",
81+
},
82+
"zone": schema.StringAttribute{
83+
Optional: true,
84+
Description: "Zone of server to send the action to",
85+
},
86+
"wait": schema.BoolAttribute{
87+
Optional: true,
88+
Description: "Wait for server to finish action",
89+
},
90+
},
91+
}
92+
}
93+
94+
func (a *ServerAction) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
95+
var data ServerActionModel
96+
// Read action config data into the model
97+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
98+
99+
if resp.Diagnostics.HasError() {
100+
return
101+
}
102+
103+
if a.instanceAPI == nil {
104+
resp.Diagnostics.AddError(
105+
"Unconfigured instanceAPI",
106+
"The action was not properly configured. The Scaleway client is missing. "+
107+
"This is usually a bug in the provider. Please report it to the maintainers.",
108+
)
109+
110+
return
111+
}
112+
113+
actionReq := &instance.ServerActionRequest{
114+
ServerID: locality.ExpandID(data.ServerID.ValueString()),
115+
Action: instance.ServerAction(data.Action.ValueString()),
116+
}
117+
if !data.Zone.IsNull() {
118+
actionReq.Zone = scw.Zone(data.Zone.ValueString())
119+
}
120+
121+
_, err := a.instanceAPI.ServerAction(actionReq)
122+
if err != nil {
123+
resp.Diagnostics.AddError(
124+
"error in server action",
125+
fmt.Sprintf("%s", err))
126+
}
127+
128+
if data.Wait.ValueBool() {
129+
waitReq := &instance.WaitForServerRequest{
130+
ServerID: locality.ExpandID(data.ServerID.ValueString()),
131+
Zone: scw.Zone(data.Zone.ValueString()),
132+
}
133+
134+
if !data.Zone.IsNull() {
135+
waitReq.Zone = scw.Zone(data.Zone.ValueString())
136+
}
137+
138+
_, errWait := a.instanceAPI.WaitForServer(waitReq)
139+
if errWait != nil {
140+
resp.Diagnostics.AddError(
141+
"error in wait server",
142+
fmt.Sprintf("%s", err))
143+
}
144+
}
145+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package instance_test
2+
3+
import (
4+
"errors"
5+
"regexp"
6+
"strings"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-testing/terraform"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
12+
)
13+
14+
func TestAccActionServer_Basic(t *testing.T) {
15+
if acctest.IsRunningOpenTofu() {
16+
t.Skip("Skipping TestAccActionServer_Basic because action are not yet supported on OpenTofu")
17+
}
18+
19+
tt := acctest.NewTestTools(t)
20+
defer tt.Cleanup()
21+
22+
resource.ParallelTest(t, resource.TestCase{
23+
ProtoV6ProviderFactories: tt.ProviderFactories,
24+
Steps: []resource.TestStep{
25+
{
26+
Config: `
27+
resource "scaleway_instance_server" "main" {
28+
name = "test-terraform-datasource-private-nic"
29+
type = "DEV1-S"
30+
image = "ubuntu_jammy"
31+
32+
lifecycle {
33+
action_trigger {
34+
events = [after_create]
35+
actions = [action.scaleway_instance_server_action.main]
36+
}
37+
}
38+
}
39+
40+
action "scaleway_instance_server_action" "main" {
41+
config {
42+
action = "reboot"
43+
server_id = scaleway_instance_server.main.id
44+
}
45+
}`,
46+
},
47+
{
48+
Config: `
49+
resource "scaleway_instance_server" "main" {
50+
name = "test-terraform-datasource-private-nic"
51+
type = "DEV1-S"
52+
image = "ubuntu_jammy"
53+
54+
lifecycle {
55+
action_trigger {
56+
events = [after_create]
57+
actions = [action.scaleway_instance_server_action.main]
58+
}
59+
}
60+
}
61+
62+
action "scaleway_instance_server_action" "main" {
63+
config {
64+
action = "reboot"
65+
server_id = scaleway_instance_server.main.id
66+
}
67+
}
68+
69+
data "scaleway_audit_trail_event" "instance" {
70+
resource_type = "instance_server"
71+
resource_id = scaleway_instance_server.main.id
72+
method_name = "ServerAction"
73+
}`,
74+
Check: resource.ComposeTestCheckFunc(
75+
resource.TestCheckResourceAttrSet("data.scaleway_audit_trail_event.instance", "events.#"),
76+
func(state *terraform.State) error {
77+
rs, ok := state.RootModule().Resources["data.scaleway_audit_trail_event.instance"]
78+
if !ok {
79+
return errors.New("not found: data.scaleway_audit_trail_event.instance")
80+
}
81+
82+
for key, value := range rs.Primary.Attributes {
83+
if !strings.Contains(key, "request_body") {
84+
continue
85+
}
86+
87+
if value == `{"action":"reboot"}` {
88+
return nil
89+
}
90+
}
91+
92+
return errors.New("did not found the reboot event")
93+
},
94+
),
95+
},
96+
},
97+
})
98+
}
99+
100+
func TestAccActionServer_UnknownVerb(t *testing.T) {
101+
if acctest.IsRunningOpenTofu() {
102+
t.Skip("Skipping TestAccActionServer_Basic because action are not yet supported on OpenTofu")
103+
}
104+
105+
tt := acctest.NewTestTools(t)
106+
defer tt.Cleanup()
107+
108+
resource.ParallelTest(t, resource.TestCase{
109+
ProtoV6ProviderFactories: tt.ProviderFactories,
110+
Steps: []resource.TestStep{
111+
{
112+
Config: `
113+
action "scaleway_instance_server_action" "main" {
114+
config {
115+
action = "unknownVerb"
116+
server_id = "11111111-1111-1111-1111-111111111111"
117+
}
118+
}
119+
`,
120+
ExpectError: regexp.MustCompile("Invalid Attribute Value Match"),
121+
},
122+
},
123+
})
124+
}

internal/services/instance/testdata/action-server-basic.cassette.yaml

Lines changed: 1661 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)