package client import ( "context" "errors" "fmt" 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 } 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 }