@@ -46,23 +46,24 @@ type instanceResource struct {
46
46
}
47
47
48
48
type instanceResourceModel struct {
49
- BootDiskID types.String `tfsdk:"boot_disk_id"`
50
- Description types.String `tfsdk:"description"`
51
- DiskAttachments types.Set `tfsdk:"disk_attachments"`
52
- ExternalIPs []instanceResourceExternalIPModel `tfsdk:"external_ips"`
53
- HostName types.String `tfsdk:"host_name"`
54
- ID types.String `tfsdk:"id"`
55
- Memory types.Int64 `tfsdk:"memory"`
56
- Name types.String `tfsdk:"name"`
57
- NetworkInterfaces []instanceResourceNICModel `tfsdk:"network_interfaces"`
58
- NCPUs types.Int64 `tfsdk:"ncpus"`
59
- ProjectID types.String `tfsdk:"project_id"`
60
- SSHPublicKeys types.Set `tfsdk:"ssh_public_keys"`
61
- StartOnCreate types.Bool `tfsdk:"start_on_create"`
62
- TimeCreated types.String `tfsdk:"time_created"`
63
- TimeModified types.String `tfsdk:"time_modified"`
64
- Timeouts timeouts.Value `tfsdk:"timeouts"`
65
- UserData types.String `tfsdk:"user_data"`
49
+ AntiAffinityGroups types.Set `tfsdk:"anti_affinity_groups"`
50
+ BootDiskID types.String `tfsdk:"boot_disk_id"`
51
+ Description types.String `tfsdk:"description"`
52
+ DiskAttachments types.Set `tfsdk:"disk_attachments"`
53
+ ExternalIPs []instanceResourceExternalIPModel `tfsdk:"external_ips"`
54
+ HostName types.String `tfsdk:"host_name"`
55
+ ID types.String `tfsdk:"id"`
56
+ Memory types.Int64 `tfsdk:"memory"`
57
+ Name types.String `tfsdk:"name"`
58
+ NetworkInterfaces []instanceResourceNICModel `tfsdk:"network_interfaces"`
59
+ NCPUs types.Int64 `tfsdk:"ncpus"`
60
+ ProjectID types.String `tfsdk:"project_id"`
61
+ SSHPublicKeys types.Set `tfsdk:"ssh_public_keys"`
62
+ StartOnCreate types.Bool `tfsdk:"start_on_create"`
63
+ TimeCreated types.String `tfsdk:"time_created"`
64
+ TimeModified types.String `tfsdk:"time_modified"`
65
+ Timeouts timeouts.Value `tfsdk:"timeouts"`
66
+ UserData types.String `tfsdk:"user_data"`
66
67
}
67
68
68
69
type instanceResourceNICModel struct {
@@ -141,6 +142,11 @@ func (r *instanceResource) Schema(ctx context.Context, _ resource.SchemaRequest,
141
142
Required : true ,
142
143
Description : "Number of CPUs allocated for this instance." ,
143
144
},
145
+ "anti_affinity_groups" : schema.SetAttribute {
146
+ Optional : true ,
147
+ Description : "IDs of the anti-affinity groups this instance should belong to." ,
148
+ ElementType : types .StringType ,
149
+ },
144
150
"boot_disk_id" : schema.StringAttribute {
145
151
Optional : true ,
146
152
Description : "ID of the disk the instance should be booted from." ,
@@ -372,14 +378,20 @@ func (r *instanceResource) Create(ctx context.Context, req resource.CreateReques
372
378
}
373
379
}
374
380
375
- // TODO: Double check we're not triggering the default "add all keys" behaviour
376
- sshKeys , diags := newSSHKeysOnCreate (plan .SSHPublicKeys )
381
+ sshKeys , diags := newNameOrIdList (plan .SSHPublicKeys )
377
382
resp .Diagnostics .Append (diags ... )
378
383
if resp .Diagnostics .HasError () {
379
384
return
380
385
}
381
386
params .Body .SshPublicKeys = sshKeys
382
387
388
+ antiAffinityGroupIDs , diags := newNameOrIdList (plan .AntiAffinityGroups )
389
+ resp .Diagnostics .Append (diags ... )
390
+ if resp .Diagnostics .HasError () {
391
+ return
392
+ }
393
+ params .Body .AntiAffinityGroups = antiAffinityGroupIDs
394
+
383
395
disks , diags := newDiskAttachmentsOnCreate (ctx , r .client , plan .DiskAttachments )
384
396
resp .Diagnostics .Append (diags ... )
385
397
if resp .Diagnostics .HasError () {
@@ -508,6 +520,16 @@ func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, r
508
520
state .SSHPublicKeys = keySet
509
521
}
510
522
523
+ antiAffinityGroupSet , diags := newAssociatedAntiAffinityGroupsOnCreateSet (ctx , r .client , state .ID .ValueString ())
524
+ resp .Diagnostics .Append (diags ... )
525
+ if resp .Diagnostics .HasError () {
526
+ return
527
+ }
528
+ // Only set the anti-affinity group list if there are any associated groups
529
+ if len (antiAffinityGroupSet .Elements ()) > 0 {
530
+ state .AntiAffinityGroups = antiAffinityGroupSet
531
+ }
532
+
511
533
diskSet , diags := newAttachedDisksSet (ctx , r .client , state .ID .ValueString ())
512
534
resp .Diagnostics .Append (diags ... )
513
535
if resp .Diagnostics .HasError () {
@@ -655,6 +677,24 @@ func (r *instanceResource) Update(ctx context.Context, req resource.UpdateReques
655
677
return
656
678
}
657
679
680
+ // Update anti-affinity groups
681
+ planAntiAffinityGroups := plan .AntiAffinityGroups .Elements ()
682
+ stateAntiAffinityGroups := state .AntiAffinityGroups .Elements ()
683
+
684
+ // Check plan and if it has an ID that the state doesn't then add it
685
+ antiAffinityGroupsToAdd := sliceDiff (planAntiAffinityGroups , stateAntiAffinityGroups )
686
+ resp .Diagnostics .Append (addAntiAffinityGroups (ctx , r .client , antiAffinityGroupsToAdd , state .ID .ValueString ())... )
687
+ if resp .Diagnostics .HasError () {
688
+ return
689
+ }
690
+
691
+ // Check state and if it has an ID that the plan doesn't then remove it
692
+ antiAffinityGroupsToRemove := sliceDiff (stateAntiAffinityGroups , planAntiAffinityGroups )
693
+ resp .Diagnostics .Append (removeAntiAffinityGroups (ctx , r .client , antiAffinityGroupsToRemove , state .ID .ValueString ())... )
694
+ if resp .Diagnostics .HasError () {
695
+ return
696
+ }
697
+
658
698
startParams := oxide.InstanceStartParams {Instance : oxide .NameOrId (state .ID .ValueString ())}
659
699
_ , err = r .client .InstanceStart (ctx , startParams )
660
700
if err != nil {
@@ -877,6 +917,36 @@ func newAssociatedSSHKeysOnCreateSet(ctx context.Context, client *oxide.Client,
877
917
return keySet , nil
878
918
}
879
919
920
+ func newAssociatedAntiAffinityGroupsOnCreateSet (ctx context.Context , client * oxide.Client , instanceID string ) (types.Set , diag.Diagnostics ) {
921
+ var diags diag.Diagnostics
922
+
923
+ params := oxide.InstanceAntiAffinityGroupListParams {
924
+ Limit : oxide .NewPointer (1000000000 ),
925
+ Instance : oxide .NameOrId (instanceID ),
926
+ }
927
+ groups , err := client .InstanceAntiAffinityGroupList (ctx , params )
928
+ if err != nil {
929
+ diags .AddError (
930
+ "Unable to list associated anti-affinity groups:" ,
931
+ "API error: " + err .Error (),
932
+ )
933
+ return types .SetNull (types .StringType ), diags
934
+ }
935
+
936
+ d := []attr.Value {}
937
+ for _ , group := range groups .Items {
938
+ id := types .StringValue (group .Id )
939
+ d = append (d , id )
940
+ }
941
+ groupSet , diags := types .SetValue (types .StringType , d )
942
+ diags .Append (diags ... )
943
+ if diags .HasError () {
944
+ return types .SetNull (types .StringType ), diags
945
+ }
946
+
947
+ return groupSet , nil
948
+ }
949
+
880
950
func newNetworkInterfaceAttachment (ctx context.Context , client * oxide.Client , model []instanceResourceNICModel ) (
881
951
oxide.InstanceNetworkInterfaceAttachment , diag.Diagnostics ) {
882
952
var diags diag.Diagnostics
@@ -1030,26 +1100,6 @@ func newDiskAttachmentsOnCreate(ctx context.Context, client *oxide.Client, diskI
1030
1100
return disks , diags
1031
1101
}
1032
1102
1033
- func newSSHKeysOnCreate (sshKeyIDs types.Set ) ([]oxide.NameOrId , diag.Diagnostics ) {
1034
- var diags diag.Diagnostics
1035
- var sshKeys = []oxide.NameOrId {}
1036
- for _ , sshKeyID := range sshKeyIDs .Elements () {
1037
- id , err := strconv .Unquote (sshKeyID .String ())
1038
- if err != nil {
1039
- diags .AddError (
1040
- "Error retrieving SSH public key ID information" ,
1041
- "SSH public key ID parse error: " + err .Error (),
1042
- )
1043
- return []oxide.NameOrId {}, diags
1044
- }
1045
-
1046
- da := oxide .NameOrId (id )
1047
- sshKeys = append (sshKeys , da )
1048
- }
1049
-
1050
- return sshKeys , diags
1051
- }
1052
-
1053
1103
func newExternalIPsOnCreate (externalIPs []instanceResourceExternalIPModel ) []oxide.ExternalIpCreate {
1054
1104
var ips []oxide.ExternalIpCreate
1055
1105
@@ -1201,6 +1251,70 @@ func detachDisks(ctx context.Context, client *oxide.Client, disks []attr.Value,
1201
1251
return nil
1202
1252
}
1203
1253
1254
+ func addAntiAffinityGroups (
1255
+ ctx context.Context , client * oxide.Client , groups []attr.Value , instanceID string ) diag.Diagnostics {
1256
+ var diags diag.Diagnostics
1257
+
1258
+ for _ , v := range groups {
1259
+ id , err := strconv .Unquote (v .String ())
1260
+ if err != nil {
1261
+ diags .AddError (
1262
+ "Error adding anti-affinity group" ,
1263
+ "anti-affinity group ID parse error: " + err .Error (),
1264
+ )
1265
+ return diags
1266
+ }
1267
+
1268
+ params := oxide.AntiAffinityGroupMemberInstanceAddParams {
1269
+ Instance : oxide .NameOrId (instanceID ),
1270
+ AntiAffinityGroup : oxide .NameOrId (id ),
1271
+ }
1272
+ _ , err = client .AntiAffinityGroupMemberInstanceAdd (ctx , params )
1273
+ if err != nil {
1274
+ diags .AddError (
1275
+ "Error adding anti-affinity group" ,
1276
+ "API error: " + err .Error (),
1277
+ )
1278
+ return diags
1279
+ }
1280
+ tflog .Trace (ctx , fmt .Sprintf ("added anti-affinity group with ID: %v" , v ), map [string ]any {"success" : true })
1281
+ }
1282
+
1283
+ return nil
1284
+ }
1285
+
1286
+ func removeAntiAffinityGroups (
1287
+ ctx context.Context , client * oxide.Client , groups []attr.Value , instanceID string ) diag.Diagnostics {
1288
+ var diags diag.Diagnostics
1289
+
1290
+ for _ , v := range groups {
1291
+ id , err := strconv .Unquote (v .String ())
1292
+ if err != nil {
1293
+ diags .AddError (
1294
+ "Error removing anti-affinity group" ,
1295
+ "anti-affinity group ID parse error: " + err .Error (),
1296
+ )
1297
+ return diags
1298
+ }
1299
+
1300
+ params := oxide.AntiAffinityGroupMemberInstanceDeleteParams {
1301
+ Instance : oxide .NameOrId (instanceID ),
1302
+ AntiAffinityGroup : oxide .NameOrId (id ),
1303
+ }
1304
+ err = client .AntiAffinityGroupMemberInstanceDelete (ctx , params )
1305
+ if err != nil {
1306
+ diags .AddError (
1307
+ "Error removing anti-affinity group" ,
1308
+ "API error: " + err .Error (),
1309
+ )
1310
+ return diags
1311
+ }
1312
+ tflog .Trace (ctx , fmt .Sprintf ("removed anit-affinity group with ID: %v" , v ), map [string ]any {"success" : true })
1313
+ }
1314
+
1315
+ return nil
1316
+ }
1317
+
1204
1318
func attrValueSliceContains (s []attr.Value , str string ) (bool , error ) {
1205
1319
for _ , a := range s {
1206
1320
v , err := strconv .Unquote (a .String ())
0 commit comments