This repository has been archived by the owner on Nov 30, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
create_deallocate_legacy_instance.go
142 lines (116 loc) · 4.82 KB
/
create_deallocate_legacy_instance.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package masters
import (
"context"
"fmt"
"strings"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
"github.com/giantswarm/microerror"
"github.com/giantswarm/azure-operator/service/controller/internal/state"
"github.com/giantswarm/azure-operator/service/controller/key"
)
const (
PowerStateLabelPrefix = "PowerState/"
PowerStateDeallocated = "PowerState/deallocated"
)
func (r *Resource) deallocateLegacyInstanceTransition(ctx context.Context, obj interface{}, currentState state.State) (state.State, error) {
cr, err := key.ToCustomResource(obj)
if err != nil {
return Empty, microerror.Mask(err)
}
deallocated, err := r.isVMSSInstanceDeallocated(ctx, key.ResourceGroupName(cr), key.LegacyMasterVMSSName(cr))
if IsNotFound(err) {
return BlockAPICalls, nil
} else if err != nil {
return Empty, microerror.Mask(err)
}
if !deallocated {
r.logger.LogCtx(ctx, "level", "info", "message", "Legacy VMSS instance is not deallocated yet.")
r.logger.LogCtx(ctx, "level", "info", "message", "Deallocating legacy VMSS instances.")
err := r.deallocateAllInstances(ctx, key.ResourceGroupName(cr), key.LegacyMasterVMSSName(cr))
if err != nil {
return Empty, microerror.Mask(err)
}
r.logger.LogCtx(ctx, "level", "info", "message", "Deallocated legacy VMSS instances.")
return currentState, nil
}
return BlockAPICalls, nil
}
func (r *Resource) deallocateAllInstances(ctx context.Context, resourceGroup string, vmssName string) error {
vmssInstancesClient, err := r.getVMsClient(ctx)
if err != nil {
return microerror.Mask(err)
}
instancesRunning, err := r.getRunningInstances(ctx, resourceGroup, vmssName)
if err != nil {
return microerror.Mask(err)
}
if len(instancesRunning) > 0 {
// There are instances still not deallocated.
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("There are %d instances to be deallocated.", len(instancesRunning)))
for _, instance := range instancesRunning {
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("Requesting Deallocate for %s", *instance.Name))
_, err = vmssInstancesClient.Deallocate(ctx, resourceGroup, vmssName, *instance.InstanceID)
if err != nil {
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("Error requesting Deallocate for %s: %s", *instance.Name, err.Error()))
continue
}
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("Requested Deallocate for %s", *instance.Name))
}
} else {
r.logger.LogCtx(ctx, "level", "debug", "message", "All instances are deallocated.")
}
return nil
}
func (r *Resource) getRunningInstances(ctx context.Context, resourceGroup string, vmssName string) ([]compute.VirtualMachineScaleSetVM, error) {
vmssInstancesClient, err := r.getVMsClient(ctx)
if err != nil {
return []compute.VirtualMachineScaleSetVM{}, microerror.Mask(err)
}
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("Iterating on the %s instances to find any instance still running", vmssName))
result, err := vmssInstancesClient.List(ctx, resourceGroup, vmssName, "", "", "")
if err != nil {
return []compute.VirtualMachineScaleSetVM{}, microerror.Mask(err)
}
var instancesRunning []compute.VirtualMachineScaleSetVM
// The List response doesn't contain the PowerState data for the instances.
// We need to call GetInstanceView on every instance to get such information.
for result.NotDone() {
for _, instance := range result.Values() {
details, err := vmssInstancesClient.GetInstanceView(context.Background(), resourceGroup, vmssName, *instance.InstanceID)
if err != nil {
return []compute.VirtualMachineScaleSetVM{}, microerror.Mask(err)
}
for _, instanceState := range *details.Statuses {
if strings.HasPrefix(*instanceState.Code, PowerStateLabelPrefix) && *instanceState.Code != PowerStateDeallocated {
r.logger.LogCtx(ctx, "level", "debug", "message", fmt.Sprintf("Instance %s is in status %s", *instance.Name, *instanceState.Code))
// Machine is not deallocated.
instancesRunning = append(instancesRunning, instance)
continue
}
}
}
err := result.Next()
if err != nil {
return []compute.VirtualMachineScaleSetVM{}, microerror.Mask(err)
}
}
return instancesRunning, nil
}
func (r *Resource) getVMSS(ctx context.Context, resourceGroup string, vmssName string) (*compute.VirtualMachineScaleSet, error) {
c, err := r.getScaleSetsClient(ctx)
if err != nil {
return nil, microerror.Mask(err)
}
vmss, err := c.Get(ctx, resourceGroup, vmssName)
if err != nil {
return nil, microerror.Mask(err)
}
return &vmss, nil
}
func (r *Resource) isVMSSInstanceDeallocated(ctx context.Context, resourceGroup string, vmssName string) (bool, error) {
instancesRunning, err := r.getRunningInstances(ctx, resourceGroup, vmssName)
if err != nil {
return false, microerror.Mask(err)
}
return len(instancesRunning) == 0, nil
}