Skip to content

Commit 91a621b

Browse files
committed
STACKITRCO-187 - Add option iaas API param agent
Adds a terraform `stackit_server` option for the iaas ( _create server_ ) API param: `"agent": {"provisioned": true}` ref STACKITRCO-187 ref: stackitcloud/stackit-cli#1213 --- Tests: * ran `make fmt`, `make generate-docs` * ran unit tests ``` [~/terraform-provider-stackit] go test stackit/internal/services/iaas/server/* ok command-line-arguments 15.005s [~/terraform-provider-stackit] make test ... ok github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/iaas/server 15.006s coverage: 33.0% of statements ... [~/terraform-provider-stackit] ``` * Tested: with a locally-configured terraform - tested by adding, changing, deleting `agent`-related parts of the below main.tf ** Tested without providing agent inside main.tf (the result had `"agent" = null /* object */`) ** Tested with setting `agent = { provisioning = true }` (and then ran the `stackit-cli` command for checking if the agent was created successfully and able to run commands, `stackit -y server command create --server-id=3fdac6ea-3885-441c-b473-bc94ca570ca8 --project-id=c904f41c-2f8c-4edb-b966-e87d65f10b64 --template-name=RunShellScript --params script='echo hello'`). ** Tested when setting `agent = { provisioning = true }` and then set it to false - verified the server was deleted and recreated again with the new value. ``` [~] cat main.tf provider "stackit" { # Configuration options service_account_key_path = "/home/debian/terraform_dev/.terraform_key.json" default_region = "eu01" } resource "stackit_network_interface" "server_nic" { project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64" network_id = "97c5dde4-cb9d-49b8-be55-9cdf0c3795e1" } resource "stackit_server" "myserver1" { project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64" name = "terraformtestserver1" boot_volume = { size = 64 source_type = "image" source_id = "21466190-b904-4267-8bf3-1be4323f4ffb" delete_on_termination = true } availability_zone = "eu01-1" agent = { provisioned = true } machine_type = "t1.1" network_interfaces = [ stackit_network_interface.server_nic.network_interface_id ] } data "stackit_server" "myserver1_data" { project_id = "c904f41c-2f8c-4edb-b966-e87d65f10b64" server_id = stackit_server.myserver1.server_id } output "server_info_from_data" { value = data.stackit_server.myserver1_data } ``` ``` [~] terraform apply -auto-approve ## this is without `agent` set in the config ... server_info_from_data = { "affinity_group" = tostring(null) "agent" = null /* object */ "availability_zone" = "eu01-1" ... } ... ``` ``` [~] terraform apply -auto-approve ## this is with `agent = { provisioned = true }` set in the config ... server_info_from_data = { "affinity_group" = tostring(null) "agent" = { "provisioned" = true } "availability_zone" = "eu01-1" "boot_volume" = { "delete_on_termination" = true "id" = "673021e5-2a90-4482-8ffa-e0485c7588bd" } ... } ``` Signed-off-by: Adrian Nackov <adrian.nackov@mail.schwarz>
1 parent 74ed4bd commit 91a621b

File tree

7 files changed

+154
-81
lines changed

7 files changed

+154
-81
lines changed

docs/data-sources/server.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ data "stackit_server" "example" {
3434
### Read-Only
3535

3636
- `affinity_group` (String) The affinity group the server is assigned to.
37+
- `agent` (Attributes) STACKIT Server Agent as setup on the server (see [below for nested schema](#nestedatt--agent))
3738
- `availability_zone` (String) The availability zone of the server.
3839
- `boot_volume` (Attributes) The boot volume for the server (see [below for nested schema](#nestedatt--boot_volume))
3940
- `created_at` (String) Date-time when the server was created
@@ -48,6 +49,14 @@ data "stackit_server" "example" {
4849
- `updated_at` (String) Date-time when the server was updated
4950
- `user_data` (String) User data that is passed via cloud-init to the server.
5051

52+
<a id="nestedatt--agent"></a>
53+
### Nested Schema for `agent`
54+
55+
Read-Only:
56+
57+
- `provisioned` (Boolean) Whether a STACKIT Server Agent is provisioned at the server
58+
59+
5160
<a id="nestedatt--boot_volume"></a>
5261
### Nested Schema for `boot_volume`
5362

docs/ephemeral-resources/access_token.md

Lines changed: 0 additions & 73 deletions
This file was deleted.

docs/resources/server.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ import {
404404
### Optional
405405

406406
- `affinity_group` (String) The affinity group the server is assigned to.
407+
- `agent` (Attributes) The STACKIT Server Agent configured for the server (see [below for nested schema](#nestedatt--agent))
407408
- `availability_zone` (String) The availability zone of the server.
408409
- `boot_volume` (Attributes) The boot volume for the server (see [below for nested schema](#nestedatt--boot_volume))
409410
- `desired_status` (String) The desired status of the server resource. Possible values are: `active`, `inactive`, `deallocated`.
@@ -422,6 +423,14 @@ import {
422423
- `server_id` (String) The server ID.
423424
- `updated_at` (String) Date-time when the server was updated
424425

426+
<a id="nestedatt--agent"></a>
427+
### Nested Schema for `agent`
428+
429+
Optional:
430+
431+
- `provisioned` (Boolean) Whether a STACKIT Server Agent should be provisioned at the server
432+
433+
425434
<a id="nestedatt--boot_volume"></a>
426435
### Nested Schema for `boot_volume`
427436

stackit/internal/services/iaas/server/datasource.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type DataSourceModel struct {
3434
ServerId types.String `tfsdk:"server_id"`
3535
MachineType types.String `tfsdk:"machine_type"`
3636
Name types.String `tfsdk:"name"`
37+
Agent types.Object `tfsdk:"agent"`
3738
AvailabilityZone types.String `tfsdk:"availability_zone"`
3839
BootVolume types.Object `tfsdk:"boot_volume"`
3940
ImageId types.String `tfsdk:"image_id"`
@@ -52,6 +53,10 @@ var bootVolumeDataTypes = map[string]attr.Type{
5253
"delete_on_termination": basetypes.BoolType{},
5354
}
5455

56+
var agentDataTypes = map[string]attr.Type{
57+
"provisioned": basetypes.BoolType{},
58+
}
59+
5560
// NewServerDataSource is a helper function to simplify the provider implementation.
5661
func NewServerDataSource() datasource.DataSource {
5762
return &serverDataSource{}
@@ -123,6 +128,16 @@ func (d *serverDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
123128
MarkdownDescription: "Name of the type of the machine for the server. Possible values are documented in [Virtual machine flavors](https://docs.stackit.cloud/products/compute-engine/server/basics/machine-types/)",
124129
Computed: true,
125130
},
131+
"agent": schema.SingleNestedAttribute{
132+
Description: "STACKIT Server Agent as setup on the server",
133+
Computed: true,
134+
Attributes: map[string]schema.Attribute{
135+
"provisioned": schema.BoolAttribute{
136+
Description: "Whether a STACKIT Server Agent is provisioned at the server",
137+
Computed: true,
138+
},
139+
},
140+
},
126141
"availability_zone": schema.StringAttribute{
127142
Description: "The availability zone of the server.",
128143
Computed: true,
@@ -304,6 +319,18 @@ func mapDataSourceFields(ctx context.Context, serverResp *iaas.Server, model *Da
304319
model.BootVolume = types.ObjectNull(bootVolumeDataTypes)
305320
}
306321

322+
if serverResp.Agent != nil {
323+
agent, diags := types.ObjectValue(agentDataTypes, map[string]attr.Value{
324+
"provisioned": types.BoolPointerValue(serverResp.Agent.Provisioned),
325+
})
326+
if diags.HasError() {
327+
return fmt.Errorf("failed to map agent: %w", core.DiagsToError(diags))
328+
}
329+
model.Agent = agent
330+
} else {
331+
model.Agent = types.ObjectNull(agentDataTypes)
332+
}
333+
307334
if serverResp.UserData != nil && len(*serverResp.UserData) > 0 {
308335
model.UserData = types.StringValue(string(*serverResp.UserData))
309336
}

stackit/internal/services/iaas/server/datasource_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func TestMapDataSourceFields(t *testing.T) {
4141
ServerId: types.StringValue("sid"),
4242
Name: types.StringNull(),
4343
AvailabilityZone: types.StringNull(),
44+
Agent: types.ObjectNull(agentTypes),
4445
Labels: types.MapNull(types.StringType),
4546
ImageId: types.StringNull(),
4647
NetworkInterfaces: types.ListNull(types.StringType),
@@ -78,7 +79,10 @@ func TestMapDataSourceFields(t *testing.T) {
7879
NicId: utils.Ptr("nic2"),
7980
},
8081
},
81-
KeypairName: utils.Ptr("keypair_name"),
82+
KeypairName: utils.Ptr("keypair_name"),
83+
Agent: &iaas.ServerAgent{
84+
Provisioned: utils.Ptr(true),
85+
},
8286
AffinityGroup: utils.Ptr("group_id"),
8387
CreatedAt: utils.Ptr(testTimestamp()),
8488
UpdatedAt: utils.Ptr(testTimestamp()),
@@ -101,7 +105,10 @@ func TestMapDataSourceFields(t *testing.T) {
101105
types.StringValue("nic1"),
102106
types.StringValue("nic2"),
103107
}),
104-
KeypairName: types.StringValue("keypair_name"),
108+
KeypairName: types.StringValue("keypair_name"),
109+
Agent: types.ObjectValueMust(agentTypes, map[string]attr.Value{
110+
"provisioned": types.BoolValue(true),
111+
}),
105112
AffinityGroup: types.StringValue("group_id"),
106113
CreatedAt: types.StringValue(testTimestampValue),
107114
UpdatedAt: types.StringValue(testTimestampValue),
@@ -132,6 +139,7 @@ func TestMapDataSourceFields(t *testing.T) {
132139
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{}),
133140
ImageId: types.StringNull(),
134141
NetworkInterfaces: types.ListNull(types.StringType),
142+
Agent: types.ObjectNull(agentTypes),
135143
KeypairName: types.StringNull(),
136144
AffinityGroup: types.StringNull(),
137145
UserData: types.StringNull(),

stackit/internal/services/iaas/server/resource.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ type Model struct {
6161
ServerId types.String `tfsdk:"server_id"`
6262
MachineType types.String `tfsdk:"machine_type"`
6363
Name types.String `tfsdk:"name"`
64+
Agent types.Object `tfsdk:"agent"`
6465
AvailabilityZone types.String `tfsdk:"availability_zone"`
6566
BootVolume types.Object `tfsdk:"boot_volume"`
6667
ImageId types.String `tfsdk:"image_id"`
@@ -75,6 +76,11 @@ type Model struct {
7576
DesiredStatus types.String `tfsdk:"desired_status"`
7677
}
7778

79+
// Struct corresponding to Model.Agent
80+
type agentModel struct {
81+
Provisioned types.Bool `tfsdk:"provisioned"`
82+
}
83+
7884
// Struct corresponding to Model.BootVolume
7985
type bootVolumeModel struct {
8086
Id types.String `tfsdk:"id"`
@@ -95,6 +101,11 @@ var bootVolumeTypes = map[string]attr.Type{
95101
"id": basetypes.StringType{},
96102
}
97103

104+
// Types corresponding to agentModel
105+
var agentTypes = map[string]attr.Type{
106+
"provisioned": basetypes.BoolType{},
107+
}
108+
98109
// NewServerResource is a helper function to simplify the provider implementation.
99110
func NewServerResource() resource.Resource {
100111
return &serverResource{}
@@ -163,6 +174,14 @@ func (r *serverResource) ValidateConfig(ctx context.Context, req resource.Valida
163174
}
164175
}
165176

177+
var agent = &agentModel{}
178+
if !(model.Agent.IsNull() || model.Agent.IsUnknown()) {
179+
diags := model.Agent.As(ctx, agent, basetypes.ObjectAsOptions{})
180+
if diags.HasError() {
181+
return
182+
}
183+
}
184+
166185
if model.NetworkInterfaces.IsNull() || model.NetworkInterfaces.IsUnknown() || len(model.NetworkInterfaces.Elements()) < 1 {
167186
core.LogAndAddWarning(ctx, &resp.Diagnostics, "No network interfaces configured", "You have no network interfaces configured for this server. This will be a problem when you want to (re-)create this server. Please note that modifying the network interfaces for an existing server will result in a replacement of the resource. We will provide a clear migration path soon.")
168187
}
@@ -273,6 +292,23 @@ func (r *serverResource) Schema(_ context.Context, _ resource.SchemaRequest, res
273292
Optional: true,
274293
Computed: true,
275294
},
295+
"agent": schema.SingleNestedAttribute{
296+
Description: "The STACKIT Server Agent configured for the server",
297+
Optional: true,
298+
PlanModifiers: []planmodifier.Object{
299+
objectplanmodifier.RequiresReplace(),
300+
},
301+
Attributes: map[string]schema.Attribute{
302+
"provisioned": schema.BoolAttribute{
303+
Description: "Whether a STACKIT Server Agent should be provisioned at the server",
304+
Optional: true,
305+
Computed: true,
306+
PlanModifiers: []planmodifier.Bool{
307+
boolplanmodifier.RequiresReplace(),
308+
},
309+
},
310+
},
311+
},
276312
"boot_volume": schema.SingleNestedAttribute{
277313
Description: "The boot volume for the server",
278314
Optional: true,
@@ -962,6 +998,26 @@ func mapFields(ctx context.Context, serverResp *iaas.Server, model *Model, regio
962998
model.NetworkInterfaces = types.ListNull(types.StringType)
963999
}
9641000

1001+
if serverResp.Agent != nil {
1002+
// convert agent model
1003+
var modelAgent = &agentModel{}
1004+
if !(model.Agent.IsNull() || model.Agent.IsUnknown()) {
1005+
diags := model.Agent.As(ctx, modelAgent, basetypes.ObjectAsOptions{})
1006+
if diags.HasError() {
1007+
return fmt.Errorf("failed to map agent: %w", core.DiagsToError(diags))
1008+
}
1009+
}
1010+
agent, diags := types.ObjectValue(agentTypes, map[string]attr.Value{
1011+
"provisioned": types.BoolPointerValue(serverResp.Agent.Provisioned),
1012+
})
1013+
if diags.HasError() {
1014+
return fmt.Errorf("failed to map agentModel: %w", core.DiagsToError(diags))
1015+
}
1016+
model.Agent = agent
1017+
} else {
1018+
model.Agent = types.ObjectNull(agentTypes)
1019+
}
1020+
9651021
if serverResp.BootVolume != nil {
9661022
// convert boot volume model
9671023
var bootVolumeModel = &bootVolumeModel{}
@@ -1030,6 +1086,14 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
10301086
}
10311087
}
10321088

1089+
var agent = &agentModel{}
1090+
if !(model.Agent.IsNull() || model.Agent.IsUnknown()) {
1091+
diags := model.Agent.As(ctx, agent, basetypes.ObjectAsOptions{})
1092+
if diags.HasError() {
1093+
return nil, fmt.Errorf("convert agent object to struct: %w", core.DiagsToError(diags))
1094+
}
1095+
}
1096+
10331097
labels, err := conversion.ToStringInterfaceMap(ctx, model.Labels)
10341098
if err != nil {
10351099
return nil, fmt.Errorf("converting to Go map: %w", err)
@@ -1051,6 +1115,14 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
10511115
}
10521116
}
10531117

1118+
var agentPayload *iaas.ServerAgent
1119+
// it is set and true, adjust payload
1120+
if !agent.Provisioned.IsNull() && !agent.Provisioned.IsUnknown() {
1121+
agentPayload = &iaas.ServerAgent{
1122+
Provisioned: conversion.BoolValueToPointer(agent.Provisioned),
1123+
}
1124+
}
1125+
10541126
var userData *[]byte
10551127
if !model.UserData.IsNull() && !model.UserData.IsUnknown() {
10561128
src := []byte(model.UserData.ValueString())
@@ -1080,6 +1152,7 @@ func toCreatePayload(ctx context.Context, model *Model) (*iaas.CreateServerPaylo
10801152
return &iaas.CreateServerPayload{
10811153
AffinityGroup: conversion.StringValueToPointer(model.AffinityGroup),
10821154
AvailabilityZone: conversion.StringValueToPointer(model.AvailabilityZone),
1155+
Agent: agentPayload,
10831156
BootVolume: bootVolumePayload,
10841157
ImageId: conversion.StringValueToPointer(model.ImageId),
10851158
KeypairName: conversion.StringValueToPointer(model.KeypairName),

0 commit comments

Comments
 (0)