fast-build-update-tool/internal/gamelift/get_instances.go (113 lines of code) (raw):

package gamelift import ( "context" "errors" "fmt" "log/slog" "strings" "github.com/aws/amazon-gamelift-toolkit/fast-build-update-tool/internal/config" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/gamelift" "github.com/aws/aws-sdk-go-v2/service/gamelift/types" ) // Instance represents a single instance a GameLift fleet type Instance struct { // IpAddress the ip address of the instance IpAddress string // InstanceId the instance id of the instance InstanceId string // Region the region the instance is running in (us-east-1, etc...) Region string // FleetId the id of the fleet this instance belongs to FleetId string // OperatingSystem the operating system that this instance is running OperatingSystem config.OperatingSystem } // GetInstances will return all active instances in the provided fleet. // Optionally this function can filter out any instances not found in allowedInstanceIds. func (g *GameLiftClient) GetInstances(ctx context.Context, fleetId string, allowedInstanceIds []string) ([]*Instance, error) { // First we need to fetch locations, as there may be instances outside of the home region locations, err := g.getLocations(ctx, fleetId, make([]string, 0, 1), nil) if err != nil { return make([]*Instance, 0), err } // Fetch each instance in each location and add it to results result := make([]*Instance, 0, 1) if len(locations) > 0 { // If locations were returned, fetch an instance in each location for _, location := range locations { result, err = g.getInstancesInternal(ctx, fleetId, location, result, nil) if err != nil { return make([]*Instance, 0), err } } } else { // If no locations were returned (no multi-location support), search for instances in the fleet without the location parameter result, err = g.getInstancesInternal(ctx, fleetId, "", result, nil) if err != nil { return make([]*Instance, 0), err } } // Return the filtered slice of any instances we found return g.filterInstances(result, allowedInstanceIds) } func (g *GameLiftClient) getLocations(ctx context.Context, fleetId string, locations []string, nextToken *string) ([]string, error) { locationAttributesOutput, err := g.gamelift.DescribeFleetLocationAttributes(ctx, &gamelift.DescribeFleetLocationAttributesInput{ FleetId: aws.String(fleetId), NextToken: nextToken, }) // If the fleet does not have multi-location support, return an empty slice var unsupportedRegion *types.UnsupportedRegionException if errors.As(err, &unsupportedRegion) { return []string{}, nil } if err != nil { return locations, fmt.Errorf("error checking locations for fleet: %w", err) } for _, locationAttributes := range locationAttributesOutput.LocationAttributes { // Filter out anything that is not active if strings.EqualFold(string(locationAttributes.LocationState.Status), string(types.FleetStatusActive)) { locations = append(locations, *locationAttributes.LocationState.Location) } } // If the results are paginated, fetch the next page if locationAttributesOutput.NextToken != nil { return g.getLocations(ctx, fleetId, locations, locationAttributesOutput.NextToken) } return locations, nil } func (g *GameLiftClient) getInstancesInternal(ctx context.Context, fleetId, location string, instances []*Instance, nextToken *string) ([]*Instance, error) { describeInstancesInput := &gamelift.DescribeInstancesInput{ FleetId: aws.String(fleetId), NextToken: nextToken, } if location != "" { describeInstancesInput.Location = aws.String(location) } instanceOutput, err := g.gamelift.DescribeInstances(ctx, describeInstancesInput) if err != nil { return instances, fmt.Errorf("error describing instances: %w", err) } for _, instance := range instanceOutput.Instances { // Filter out anything that is not active if !strings.EqualFold(string(instance.Status), string(types.InstanceStatusActive)) { slog.Debug("instance not active, skipping...", "instanceId", *instance.InstanceId, "status", instance.Status) continue } os, err := operatingSystemLookup(instance.OperatingSystem) if err != nil { return instances, err } instances = append(instances, &Instance{ IpAddress: *instance.IpAddress, InstanceId: *instance.InstanceId, Region: *instance.Location, FleetId: fleetId, OperatingSystem: os, }) } // If the results are paginated, fetch the next page if instanceOutput.NextToken != nil { return g.getInstancesInternal(ctx, fleetId, location, instances, instanceOutput.NextToken) } return instances, err } // filterInstances will filter out any instances not in the allow list func (g *GameLiftClient) filterInstances(instances []*Instance, allowedInstanceIds []string) ([]*Instance, error) { // Nothing to filter, break early if len(allowedInstanceIds) == 0 { return instances, nil } // Loop through allow list, and add any matches to the result result := make([]*Instance, 0, len(allowedInstanceIds)) for _, instance := range instances { for _, allowedInstanceId := range allowedInstanceIds { if instance.InstanceId == allowedInstanceId { result = append(result, instance) } } } if len(result) != len(allowedInstanceIds) { return result, fmt.Errorf("one or more instance ids not found in fleet: %s", strings.Join(allowedInstanceIds, ",")) } return result, nil }