4
4
"context"
5
5
"fmt"
6
6
"log"
7
+ "sort"
7
8
"strconv"
8
9
"time"
9
10
@@ -12,7 +13,6 @@ import (
12
13
"github.com/G-Core/gcorelabscloud-go/gcore/instance/v1/instances"
13
14
"github.com/G-Core/gcorelabscloud-go/gcore/instance/v1/types"
14
15
"github.com/G-Core/gcorelabscloud-go/gcore/task/v1/tasks"
15
- "github.com/hashicorp/go-cty/cty"
16
16
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
17
17
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
18
18
)
@@ -114,20 +114,18 @@ func resourceBmInstance() *schema.Resource {
114
114
"interface" : & schema.Schema {
115
115
Type : schema .TypeSet ,
116
116
Set : interfaceUniqueID ,
117
- Optional : true ,
117
+ Required : true ,
118
118
Elem : & schema.Resource {
119
119
Schema : map [string ]* schema.Schema {
120
120
"type" : {
121
121
Type : schema .TypeString ,
122
122
Required : true ,
123
123
Description : fmt .Sprintf ("Avalilable value is '%s', '%s'" , types .SubnetInterfaceType , types .ExternalInterfaceType ),
124
- ValidateDiagFunc : func (val interface {}, path cty.Path ) diag.Diagnostics {
125
- v := types .InterfaceType (val .(string ))
126
- if v != types .SubnetInterfaceType {
127
- return diag .Errorf ("subnet type only" )
128
- }
129
- return nil
130
- },
124
+ },
125
+ "is_trunk" : {
126
+ Type : schema .TypeBool ,
127
+ Computed : true ,
128
+ Description : "Calculated after creation. Can't detach interface if is_trunk true" ,
131
129
},
132
130
"network_id" : {
133
131
Type : schema .TypeString ,
@@ -273,10 +271,6 @@ func resourceBmInstanceCreate(ctx context.Context, d *schema.ResourceData, m int
273
271
}
274
272
newInterface [i ] = newIface
275
273
}
276
- //for api interface is required, but we cant attach external, and not receive it in interfaceList method
277
- if len (ints ) == 0 {
278
- newInterface = []bminstances.InterfaceOpts {{Type : types .ExternalInterfaceType }}
279
- }
280
274
281
275
opts := bminstances.CreateOpts {
282
276
Flavor : d .Get ("flavor_id" ).(string ),
@@ -372,33 +366,80 @@ func resourceBmInstanceRead(ctx context.Context, d *schema.ResourceData, m inter
372
366
}
373
367
374
368
var cleanInterfaces []interface {}
375
- for _ , iface := range ifs [0 ].SubPorts {
376
- if len (iface .IPAssignments ) == 0 {
377
- continue
378
- }
379
-
369
+ for _ , iface := range ifs {
380
370
for _ , assignment := range iface .IPAssignments {
381
371
subnetID := assignment .SubnetID
382
372
383
- _ , ok := interfaces [subnetID ]
373
+ //bad idea, but what to do
374
+ var iOpts instances.InterfaceOpts
375
+ var orderedIOpts OrderedInterfaceOpts
376
+ var ok bool
377
+ // we need to match our interfaces with api's interfaces
378
+ // but with don't have any unique value, that's why we use exactly that list of keys
379
+ for _ , k := range []string {subnetID , iface .PortID , iface .NetworkID , types .ExternalInterfaceType .String ()} {
380
+ if orderedIOpts , ok = interfaces [k ]; ok {
381
+ iOpts = orderedIOpts .InterfaceOpts
382
+ break
383
+ }
384
+ }
385
+
384
386
if ! ok {
385
387
continue
386
388
}
387
389
388
390
i := make (map [string ]interface {})
389
391
390
- i ["type" ] = interfaces [ subnetID ] .Type .String ()
392
+ i ["type" ] = iOpts .Type .String ()
391
393
i ["network_id" ] = iface .NetworkID
392
394
i ["subnet_id" ] = subnetID
393
395
i ["port_id" ] = iface .PortID
394
- if interfaces [subnetID ].FloatingIP != nil {
395
- i ["fip_source" ] = interfaces [subnetID ].FloatingIP .Source .String ()
396
- i ["existing_fip_id" ] = interfaces [subnetID ].FloatingIP .ExistingFloatingID
396
+ i ["is_trunk" ] = true
397
+ if iOpts .FloatingIP != nil {
398
+ i ["fip_source" ] = iOpts .FloatingIP .Source .String ()
399
+ i ["existing_fip_id" ] = iOpts .FloatingIP .ExistingFloatingID
397
400
}
398
- i ["ip_address" ] = iface . IPAssignments [ 0 ] .IPAddress .String ()
401
+ i ["ip_address" ] = assignment .IPAddress .String ()
399
402
400
403
cleanInterfaces = append (cleanInterfaces , i )
401
404
}
405
+
406
+ for _ , iface1 := range iface .SubPorts {
407
+ for _ , assignment := range iface1 .IPAssignments {
408
+ subnetID := assignment .SubnetID
409
+
410
+ //bad idea, but what to do
411
+ var iOpts instances.InterfaceOpts
412
+ var orderedIOpts OrderedInterfaceOpts
413
+ var ok bool
414
+ // we need to match our interfaces with api's interfaces
415
+ // but with don't have any unique value, that's why we use exactly that list of keys
416
+ for _ , k := range []string {subnetID , iface1 .PortID , iface1 .NetworkID , types .ExternalInterfaceType .String ()} {
417
+ if orderedIOpts , ok = interfaces [k ]; ok {
418
+ iOpts = orderedIOpts .InterfaceOpts
419
+ break
420
+ }
421
+ }
422
+
423
+ if ! ok {
424
+ continue
425
+ }
426
+
427
+ i := make (map [string ]interface {})
428
+
429
+ i ["type" ] = iOpts .Type .String ()
430
+ i ["network_id" ] = iface1 .NetworkID
431
+ i ["subnet_id" ] = subnetID
432
+ i ["port_id" ] = iface1 .PortID
433
+ i ["is_trunk" ] = false
434
+ if iOpts .FloatingIP != nil {
435
+ i ["fip_source" ] = iOpts .FloatingIP .Source .String ()
436
+ i ["existing_fip_id" ] = iOpts .FloatingIP .ExistingFloatingID
437
+ }
438
+ i ["ip_address" ] = assignment .IPAddress .String ()
439
+
440
+ cleanInterfaces = append (cleanInterfaces , i )
441
+ }
442
+ }
402
443
}
403
444
if err := d .Set ("interface" , schema .NewSet (interfaceUniqueID , cleanInterfaces )); err != nil {
404
445
return diag .FromErr (err )
@@ -500,33 +541,62 @@ func resourceBmInstanceUpdate(ctx context.Context, d *schema.ResourceData, m int
500
541
}
501
542
502
543
iface := i .(map [string ]interface {})
544
+ if iface ["is_trunk" ].(bool ) {
545
+ return diag .Errorf ("could not detach trunk interface" )
546
+ }
547
+
503
548
var opts instances.InterfaceOpts
504
549
opts .PortID = iface ["port_id" ].(string )
505
550
opts .IpAddress = iface ["ip_address" ].(string )
506
551
507
- if err := instances .DetachInterface (client , instanceID , opts ).Err ; err != nil {
552
+ log .Printf ("[DEBUG] detach interface: %+v" , opts )
553
+ results , err := instances .DetachInterface (client , instanceID , opts ).Extract ()
554
+ if err != nil {
508
555
return diag .FromErr (err )
509
556
}
557
+ taskID := results .Tasks [0 ]
558
+ _ , err = tasks .WaitTaskAndReturnResult (client , taskID , true , InstanceCreatingTimeout , func (task tasks.TaskID ) (interface {}, error ) {
559
+ taskInfo , err := tasks .Get (client , string (task )).Extract ()
560
+ if err != nil {
561
+ return nil , fmt .Errorf ("cannot get task with ID: %s. Error: %w, task: %+v" , task , err , taskInfo )
562
+ }
563
+ return nil , nil
564
+ },
565
+ )
566
+ if err != nil {
567
+ return diag .FromErr (err )
568
+ }
569
+ }
570
+
571
+ currentIfs , err := instances .ListInterfacesAll (client , d .Id ())
572
+ if err != nil {
573
+ return diag .FromErr (err )
510
574
}
511
575
512
- for _ , i := range ifsNew .List () {
576
+ sortedNewIfs := ifsNew .List ()
577
+ sort .Sort (instanceInterfaces (sortedNewIfs ))
578
+ for _ , i := range sortedNewIfs {
513
579
if ifsOld .Contains (i ) {
514
580
log .Println ("[DEBUG] Skipped, dont need attach" )
515
581
continue
516
582
}
517
-
518
583
iface := i .(map [string ]interface {})
584
+ if isInterfaceAttached (currentIfs , iface ) {
585
+ continue
586
+ }
519
587
520
588
iType := types .InterfaceType (iface ["type" ].(string ))
521
589
opts := instances.InterfaceOpts {Type : iType }
522
590
switch iType {
523
591
case types .SubnetInterfaceType :
524
592
opts .SubnetID = iface ["subnet_id" ].(string )
525
- //opts.NetworkID = iface["network_id"].(string)
526
- case types .ExternalInterfaceType :
527
- continue
593
+ case types .AnySubnetInterfaceType :
594
+ opts .NetworkID = iface ["network_id" ].(string )
595
+ case types .ReservedFixedIpType :
596
+ opts .PortID = iface ["port_id" ].(string )
528
597
}
529
598
599
+ log .Printf ("[DEBUG] attach interface: %+v" , opts )
530
600
results , err := instances .AttachInterface (client , instanceID , opts ).Extract ()
531
601
if err != nil {
532
602
return diag .Errorf ("cannot attach interface: %s. Error: %s" , iType , err )
0 commit comments