mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-29 09:26:17 -05:00
0d12e37c96
* Include EXC0014 and fix issues. * Include EXC0012 and fix issues. Signed-off-by: Fabian Kammel <fk@edgeless.systems> Co-authored-by: Otto Bittner <cobittner@posteo.net>
201 lines
7.3 KiB
Go
201 lines
7.3 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"google.golang.org/api/iterator"
|
|
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
|
)
|
|
|
|
// GetScalingGroupImage returns the image URI of the scaling group.
|
|
func (c *Client) GetScalingGroupImage(ctx context.Context, scalingGroupID string) (string, error) {
|
|
instanceTemplate, err := c.getScalingGroupTemplate(ctx, scalingGroupID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return instanceTemplateSourceImage(instanceTemplate)
|
|
}
|
|
|
|
// SetScalingGroupImage sets the image URI of the scaling group.
|
|
func (c *Client) SetScalingGroupImage(ctx context.Context, scalingGroupID, imageURI string) error {
|
|
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// get current template
|
|
instanceTemplate, err := c.getScalingGroupTemplate(ctx, scalingGroupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if template already uses the same image
|
|
oldImageURI, err := instanceTemplateSourceImage(instanceTemplate)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if oldImageURI == imageURI {
|
|
return nil
|
|
}
|
|
|
|
// clone template with desired image
|
|
if instanceTemplate.Name == nil {
|
|
return fmt.Errorf("instance template of scaling group %q has no name", scalingGroupID)
|
|
}
|
|
instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage = &imageURI
|
|
newTemplateName, err := generateInstanceTemplateName(*instanceTemplate.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
instanceTemplate.Name = &newTemplateName
|
|
op, err := c.instanceTemplateAPI.Insert(ctx, &computepb.InsertInstanceTemplateRequest{
|
|
Project: project,
|
|
InstanceTemplateResource: instanceTemplate,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("cloning instance template: %w", err)
|
|
}
|
|
if err := op.Wait(ctx); err != nil {
|
|
return fmt.Errorf("waiting for cloned instance template: %w", err)
|
|
}
|
|
|
|
newTemplateURI := joinInstanceTemplateURI(project, newTemplateName)
|
|
// update instance group manager to use new template
|
|
op, err = c.instanceGroupManagersAPI.SetInstanceTemplate(ctx, &computepb.SetInstanceTemplateInstanceGroupManagerRequest{
|
|
InstanceGroupManager: instanceGroupName,
|
|
Project: project,
|
|
Zone: zone,
|
|
InstanceGroupManagersSetInstanceTemplateRequestResource: &computepb.InstanceGroupManagersSetInstanceTemplateRequest{
|
|
InstanceTemplate: &newTemplateURI,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("setting instance template: %w", err)
|
|
}
|
|
if err := op.Wait(ctx); err != nil {
|
|
return fmt.Errorf("waiting for setting instance template: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetScalingGroupName retrieves the name of a scaling group.
|
|
// This keeps the casing of the original name, but Kubernetes requires the name to be lowercase,
|
|
// so use strings.ToLower() on the result if using the name in a Kubernetes context.
|
|
func (c *Client) GetScalingGroupName(scalingGroupID string) (string, error) {
|
|
_, _, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("getting scaling group name: %w", err)
|
|
}
|
|
return instanceGroupName, nil
|
|
}
|
|
|
|
// GetAutoscalingGroupName retrieves the name of a scaling group as needed by the cluster-autoscaler.
|
|
func (c *Client) GetAutoscalingGroupName(scalingGroupID string) (string, error) {
|
|
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("getting autoscaling scaling group name: %w", err)
|
|
}
|
|
return ensureURIPrefixed(fmt.Sprintf("projects/%s/zones/%s/instanceGroups/%s", project, zone, instanceGroupName)), nil
|
|
}
|
|
|
|
// ListScalingGroups retrieves a list of scaling groups for the cluster.
|
|
func (c *Client) ListScalingGroups(ctx context.Context, uid string) (controlPlaneGroupIDs []string, workerGroupIDs []string, err error) {
|
|
iter := c.instanceGroupManagersAPI.AggregatedList(ctx, &computepb.AggregatedListInstanceGroupManagersRequest{
|
|
Project: c.projectID,
|
|
})
|
|
for instanceGroupManagerScopedListPair, err := iter.Next(); ; instanceGroupManagerScopedListPair, err = iter.Next() {
|
|
if err == iterator.Done {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("listing instance group managers: %w", err)
|
|
}
|
|
if instanceGroupManagerScopedListPair.Value == nil {
|
|
continue
|
|
}
|
|
for _, grpManager := range instanceGroupManagerScopedListPair.Value.InstanceGroupManagers {
|
|
if grpManager == nil || grpManager.Name == nil || grpManager.SelfLink == nil || grpManager.InstanceTemplate == nil {
|
|
continue
|
|
}
|
|
|
|
templateURI := strings.Split(*grpManager.InstanceTemplate, "/")
|
|
if len(templateURI) < 1 {
|
|
continue // invalid template URI
|
|
}
|
|
template, err := c.instanceTemplateAPI.Get(ctx, &computepb.GetInstanceTemplateRequest{
|
|
Project: c.projectID,
|
|
InstanceTemplate: templateURI[len(templateURI)-1],
|
|
})
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("getting instance template: %w", err)
|
|
}
|
|
if template.Properties == nil || template.Properties.Labels == nil {
|
|
continue
|
|
}
|
|
if template.Properties.Labels["constellation-uid"] != uid {
|
|
continue
|
|
}
|
|
|
|
groupID, err := c.canonicalInstanceGroupID(ctx, *grpManager.SelfLink)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("normalizing instance group ID: %w", err)
|
|
}
|
|
|
|
switch strings.ToLower(template.Properties.Labels["constellation-role"]) {
|
|
case "control-plane", "controlplane":
|
|
controlPlaneGroupIDs = append(controlPlaneGroupIDs, groupID)
|
|
case "worker":
|
|
workerGroupIDs = append(workerGroupIDs, groupID)
|
|
}
|
|
}
|
|
}
|
|
return controlPlaneGroupIDs, workerGroupIDs, nil
|
|
}
|
|
|
|
func (c *Client) getScalingGroupTemplate(ctx context.Context, scalingGroupID string) (*computepb.InstanceTemplate, error) {
|
|
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
instanceGroupManager, err := c.instanceGroupManagersAPI.Get(ctx, &computepb.GetInstanceGroupManagerRequest{
|
|
InstanceGroupManager: instanceGroupName,
|
|
Project: project,
|
|
Zone: zone,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getting instance group manager %q: %w", instanceGroupName, err)
|
|
}
|
|
if instanceGroupManager.InstanceTemplate == nil {
|
|
return nil, fmt.Errorf("instance group manager %q has no instance template", instanceGroupName)
|
|
}
|
|
instanceTemplateProject, instanceTemplateName, err := splitInstanceTemplateID(uriNormalize(*instanceGroupManager.InstanceTemplate))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("splitting instance template name: %w", err)
|
|
}
|
|
instanceTemplate, err := c.instanceTemplateAPI.Get(ctx, &computepb.GetInstanceTemplateRequest{
|
|
InstanceTemplate: instanceTemplateName,
|
|
Project: instanceTemplateProject,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getting instance template %q: %w", instanceTemplateName, err)
|
|
}
|
|
return instanceTemplate, nil
|
|
}
|
|
|
|
func instanceTemplateSourceImage(instanceTemplate *computepb.InstanceTemplate) (string, error) {
|
|
if instanceTemplate.Properties == nil ||
|
|
len(instanceTemplate.Properties.Disks) == 0 ||
|
|
instanceTemplate.Properties.Disks[0].InitializeParams == nil ||
|
|
instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage == nil {
|
|
return "", errors.New("instance template has no source image")
|
|
}
|
|
return uriNormalize(*instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage), nil
|
|
}
|