mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-02-17 13:24:21 -05:00
cli: azure scale set poller: check for power state of every instance (#78)
This commit is contained in:
parent
020cf51fc6
commit
47b3195bac
@ -51,6 +51,12 @@ type scaleSetsAPI interface {
|
|||||||
*runtime.Poller[armcomputev2.VirtualMachineScaleSetsClientCreateOrUpdateResponse], error)
|
*runtime.Poller[armcomputev2.VirtualMachineScaleSetsClientCreateOrUpdateResponse], error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type virtualMachineScaleSetVMsAPI interface {
|
||||||
|
GetInstanceView(ctx context.Context, resourceGroupName string, vmScaleSetName string, instanceID string,
|
||||||
|
options *armcomputev2.VirtualMachineScaleSetVMsClientGetInstanceViewOptions,
|
||||||
|
) (armcomputev2.VirtualMachineScaleSetVMsClientGetInstanceViewResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
type publicIPAddressesAPI interface {
|
type publicIPAddressesAPI interface {
|
||||||
NewListVirtualMachineScaleSetVMPublicIPAddressesPager(
|
NewListVirtualMachineScaleSetVMPublicIPAddressesPager(
|
||||||
resourceGroupName string, virtualMachineScaleSetName string,
|
resourceGroupName string, virtualMachineScaleSetName string,
|
||||||
|
@ -37,6 +37,7 @@ type Client struct {
|
|||||||
networkSecurityGroupsAPI
|
networkSecurityGroupsAPI
|
||||||
resourceAPI
|
resourceAPI
|
||||||
scaleSetsAPI
|
scaleSetsAPI
|
||||||
|
virtualMachineScaleSetVMsAPI
|
||||||
publicIPAddressesAPI
|
publicIPAddressesAPI
|
||||||
networkInterfacesAPI
|
networkInterfacesAPI
|
||||||
loadBalancersAPI
|
loadBalancersAPI
|
||||||
@ -91,6 +92,10 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
virtualMachineScaleSetVMsAPI, err := armcomputev2.NewVirtualMachineScaleSetVMsClient(subscriptionID, cred, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
publicIPAddressesAPI, err := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
|
publicIPAddressesAPI, err := armnetwork.NewPublicIPAddressesClient(subscriptionID, cred, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -119,22 +124,23 @@ func NewFromDefault(subscriptionID, tenantID string) (*Client, error) {
|
|||||||
roleAssignmentsAPI.Authorizer = managementAuthorizer
|
roleAssignmentsAPI.Authorizer = managementAuthorizer
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
networksAPI: netAPI,
|
networksAPI: netAPI,
|
||||||
networkSecurityGroupsAPI: netSecGrpAPI,
|
networkSecurityGroupsAPI: netSecGrpAPI,
|
||||||
resourceAPI: resourceAPI,
|
resourceAPI: resourceAPI,
|
||||||
scaleSetsAPI: scaleSetAPI,
|
scaleSetsAPI: scaleSetAPI,
|
||||||
publicIPAddressesAPI: publicIPAddressesAPI,
|
virtualMachineScaleSetVMsAPI: virtualMachineScaleSetVMsAPI,
|
||||||
networkInterfacesAPI: networkInterfacesAPI,
|
publicIPAddressesAPI: publicIPAddressesAPI,
|
||||||
loadBalancersAPI: loadBalancersAPI,
|
networkInterfacesAPI: networkInterfacesAPI,
|
||||||
applicationsAPI: applicationsAPI,
|
loadBalancersAPI: loadBalancersAPI,
|
||||||
servicePrincipalsAPI: servicePrincipalsAPI,
|
applicationsAPI: applicationsAPI,
|
||||||
roleAssignmentsAPI: roleAssignmentsAPI,
|
servicePrincipalsAPI: servicePrincipalsAPI,
|
||||||
applicationInsightsAPI: applicationInsightsAPI,
|
roleAssignmentsAPI: roleAssignmentsAPI,
|
||||||
subscriptionID: subscriptionID,
|
applicationInsightsAPI: applicationInsightsAPI,
|
||||||
tenantID: tenantID,
|
subscriptionID: subscriptionID,
|
||||||
workers: cloudtypes.Instances{},
|
tenantID: tenantID,
|
||||||
controlPlanes: cloudtypes.Instances{},
|
workers: cloudtypes.Instances{},
|
||||||
pollFrequency: time.Second * 5,
|
controlPlanes: cloudtypes.Instances{},
|
||||||
|
pollFrequency: time.Second * 5,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,18 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||||
|
armcomputev2 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v2"
|
||||||
"github.com/edgelesssys/constellation/cli/internal/azure"
|
"github.com/edgelesssys/constellation/cli/internal/azure"
|
||||||
"github.com/edgelesssys/constellation/cli/internal/azure/internal/poller"
|
"github.com/edgelesssys/constellation/cli/internal/azure/internal/poller"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
|
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// scaleSetCreateTimeout maximum timeout to wait for scale set creation.
|
const (
|
||||||
const scaleSetCreateTimeout = 5 * time.Minute
|
// scaleSetCreateTimeout maximum timeout to wait for scale set creation.
|
||||||
|
scaleSetCreateTimeout = 5 * time.Minute
|
||||||
|
powerStateStarting = "PowerState/starting"
|
||||||
|
powerStateRunning = "PowerState/running"
|
||||||
|
)
|
||||||
|
|
||||||
func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput) error {
|
func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput) error {
|
||||||
// Create worker scale set
|
// Create worker scale set
|
||||||
@ -150,9 +155,10 @@ func (c *Client) createScaleSet(ctx context.Context, input CreateScaleSetInput)
|
|||||||
// use custom poller to wait for resource creation but skip waiting for OS provisioning.
|
// use custom poller to wait for resource creation but skip waiting for OS provisioning.
|
||||||
// OS provisioning does not work reliably without the azure guest agent installed.
|
// OS provisioning does not work reliably without the azure guest agent installed.
|
||||||
poller := poller.New[bool](&scaleSetCreationPollingHandler{
|
poller := poller.New[bool](&scaleSetCreationPollingHandler{
|
||||||
resourceGroup: c.resourceGroup,
|
resourceGroup: c.resourceGroup,
|
||||||
scaleSet: input.Name,
|
scaleSet: input.Name,
|
||||||
scaleSetsAPI: c.scaleSetsAPI,
|
scaleSetsAPI: c.scaleSetsAPI,
|
||||||
|
virtualMachineScaleSetVMsAPI: c.virtualMachineScaleSetVMsAPI,
|
||||||
})
|
})
|
||||||
|
|
||||||
pollCtx, cancel := context.WithTimeout(ctx, scaleSetCreateTimeout)
|
pollCtx, cancel := context.WithTimeout(ctx, scaleSetCreateTimeout)
|
||||||
@ -220,10 +226,12 @@ type CreateScaleSetInput struct {
|
|||||||
|
|
||||||
// scaleSetCreationPollingHandler is a custom poller used to check if a scale set was created successfully.
|
// scaleSetCreationPollingHandler is a custom poller used to check if a scale set was created successfully.
|
||||||
type scaleSetCreationPollingHandler struct {
|
type scaleSetCreationPollingHandler struct {
|
||||||
done bool
|
done bool
|
||||||
resourceGroup string
|
instanceIDOffset int
|
||||||
scaleSet string
|
resourceGroup string
|
||||||
scaleSetsAPI scaleSetsAPI
|
scaleSet string
|
||||||
|
scaleSetsAPI scaleSetsAPI
|
||||||
|
virtualMachineScaleSetVMsAPI virtualMachineScaleSetVMsAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done returns true if the condition is met.
|
// Done returns true if the condition is met.
|
||||||
@ -231,19 +239,29 @@ func (h *scaleSetCreationPollingHandler) Done() bool {
|
|||||||
return h.done
|
return h.done
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll checks if the scale set resource was created successfully.
|
// Poll checks if the scale set resource was created successfully and every VM is starting or running.
|
||||||
func (h *scaleSetCreationPollingHandler) Poll(ctx context.Context) error {
|
func (h *scaleSetCreationPollingHandler) Poll(ctx context.Context) error {
|
||||||
_, err := h.scaleSetsAPI.Get(ctx, h.resourceGroup, h.scaleSet, nil)
|
// check if scale set can be retrieved from API
|
||||||
if err == nil {
|
scaleSet, err := h.scaleSetsAPI.Get(ctx, h.resourceGroup, h.scaleSet, nil)
|
||||||
h.done = true
|
if err != nil {
|
||||||
return nil
|
return ignoreNotFoundError(err)
|
||||||
}
|
}
|
||||||
var respErr *azcore.ResponseError
|
if scaleSet.SKU == nil || scaleSet.SKU.Capacity == nil {
|
||||||
if errors.As(err, &respErr) && respErr.StatusCode == http.StatusNotFound {
|
return errors.New("invalid scale set capacity")
|
||||||
// resource does not exist yet - retry later
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return err
|
// check if every VM in the scale set has power state starting or running
|
||||||
|
for i := h.instanceIDOffset; i < int(*scaleSet.SKU.Capacity); i++ {
|
||||||
|
instanceView, err := h.virtualMachineScaleSetVMsAPI.GetInstanceView(ctx, h.resourceGroup, h.scaleSet, strconv.Itoa(i), nil)
|
||||||
|
if err != nil {
|
||||||
|
return ignoreNotFoundError(err)
|
||||||
|
}
|
||||||
|
if !vmIsStartingOrRunning(instanceView.Statuses) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
h.instanceIDOffset = i + 1 // skip this VM in the next Poll() invocation
|
||||||
|
}
|
||||||
|
h.done = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result returns the result of the poller if the condition is met.
|
// Result returns the result of the poller if the condition is met.
|
||||||
@ -255,3 +273,27 @@ func (h *scaleSetCreationPollingHandler) Result(ctx context.Context, out *bool)
|
|||||||
*out = h.done
|
*out = h.done
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ignoreNotFoundError(err error) error {
|
||||||
|
var respErr *azcore.ResponseError
|
||||||
|
if errors.As(err, &respErr) && respErr.StatusCode == http.StatusNotFound {
|
||||||
|
// resource does not exist yet - retry later
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func vmIsStartingOrRunning(statuses []*armcomputev2.InstanceViewStatus) bool {
|
||||||
|
for _, status := range statuses {
|
||||||
|
if status == nil || status.Code == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch *status.Code {
|
||||||
|
case powerStateStarting:
|
||||||
|
return true
|
||||||
|
case powerStateRunning:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -30,8 +30,8 @@ func TestCreateInstances(t *testing.T) {
|
|||||||
publicIPAddressesAPI: stubPublicIPAddressesAPI{},
|
publicIPAddressesAPI: stubPublicIPAddressesAPI{},
|
||||||
networkInterfacesAPI: stubNetworkInterfacesAPI{},
|
networkInterfacesAPI: stubNetworkInterfacesAPI{},
|
||||||
scaleSetsAPI: stubScaleSetsAPI{
|
scaleSetsAPI: stubScaleSetsAPI{
|
||||||
stubResponse: armcomputev2.VirtualMachineScaleSetsClientCreateOrUpdateResponse{
|
getResponse: armcomputev2.VirtualMachineScaleSet{
|
||||||
VirtualMachineScaleSet: armcomputev2.VirtualMachineScaleSet{Identity: &armcomputev2.VirtualMachineScaleSetIdentity{PrincipalID: to.Ptr("principal-id")}},
|
Identity: &armcomputev2.VirtualMachineScaleSetIdentity{PrincipalID: to.Ptr("principal-id")}, SKU: &armcomputev2.SKU{Capacity: to.Ptr[int64](0)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
createInstancesInput: CreateInstancesInput{
|
createInstancesInput: CreateInstancesInput{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user