Skip to content

Commit

Permalink
Add command to cleanup tinkerbell test resources
Browse files Browse the repository at this point in the history
  • Loading branch information
g-gaston committed Jul 2, 2024
1 parent 46e3955 commit 3e9c9a9
Show file tree
Hide file tree
Showing 14 changed files with 579 additions and 179 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,10 @@ e2e-tests-binary:
GOOS=$(GO_OS) GOARCH=$(GO_ARCH) $(GO) test ./test/e2e -c -o "$(E2E_OUTPUT_FILE)" -tags "$(E2E_TAGS)" -ldflags "-X github.com/aws/eks-anywhere/pkg/version.gitVersion=$(DEV_GIT_VERSION) -X github.com/aws/eks-anywhere/pkg/manifests/releases.manifestURL=$(RELEASE_MANIFEST_URL)"

.PHONY: build-integration-test-binary
build-integration-test-binary: ALL_LINKER_FLAGS := $(LINKER_FLAGS) -X github.com/aws/eks-anywhere/pkg/version.gitVersion=$(DEV_GIT_VERSION) -X github.com/aws/eks-anywhere/pkg/manifests/releases.manifestURL=$(RELEASE_MANIFEST_URL) -s -w -buildid='' -extldflags -static
build-integration-test-binary: LINKER_FLAGS_ARG := -ldflags "$(ALL_LINKER_FLAGS)"
build-integration-test-binary:
GOOS=$(GO_OS) GOARCH=$(GO_ARCH) $(GO) build -o bin/test github.com/aws/eks-anywhere/cmd/integration_test
GOOS=$(GO_OS) GOARCH=$(GO_ARCH) $(GO) build $(LINKER_FLAGS_ARG) -o bin/test github.com/aws/eks-anywhere/cmd/integration_test

.PHONY: conformance
conformance:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ phases:
--insecure
--ignoreErrors
-v 4
- >
./bin/test e2e cleanup tinkerbell
--storage-bucket ${INTEGRATION_TEST_STORAGE_BUCKET}
--instance-config ${INTEGRATION_TEST_INFRA_CONFIG}
--dry-run
-v 4
build:
commands:
- export JOB_ID=$CODEBUILD_BUILD_ID
Expand Down
156 changes: 156 additions & 0 deletions cmd/integration_test/cmd/cleanuptinkerbell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package cmd

import (
"context"
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/spf13/cobra"

"github.com/aws/eks-anywhere/internal/pkg/ssm"
"github.com/aws/eks-anywhere/internal/test/cleanup"
"github.com/aws/eks-anywhere/internal/test/e2e"
"github.com/aws/eks-anywhere/pkg/dependencies"
"github.com/aws/eks-anywhere/pkg/errors"
"github.com/aws/eks-anywhere/pkg/executables"
"github.com/aws/eks-anywhere/pkg/logger"
"github.com/aws/eks-anywhere/pkg/providers/tinkerbell/hardware"
)

var cleanUpTinkerbellCmd = &cobra.Command{
Use: "tinkerbell",
Short: "Clean up tinkerbell e2e resources",
Long: "Deletes vms created for e2e testing on vsphere and powers off metal machines",
SilenceUsage: true,
PreRun: preRunCleanUpNutanixSetup,
RunE: func(cmd *cobra.Command, _ []string) error {
return cleanUpTinkerbellTestResources(cmd.Context())
},
}

var (
storageBucket string
instanceConfig string
dryRun bool
)

func init() {
cleanUpInstancesCmd.AddCommand(cleanUpTinkerbellCmd)
cleanUpTinkerbellCmd.Flags().StringVarP(&storageBucket, storageBucketFlagName, "s", "", "S3 bucket name where tinkerbell hardware inventory files are stored")
cleanUpTinkerbellCmd.Flags().StringVar(&instanceConfig, instanceConfigFlagName, "", "File path to the instance-config.yml config")
cleanUpTinkerbellCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Run command without deleting or powering off any resources")

if err := cleanUpTinkerbellCmd.MarkFlagRequired(storageBucketFlagName); err != nil {
log.Fatalf("Error marking flag %s as required: %v", storageBucketFlagName, err)
}

if err := cleanUpTinkerbellCmd.MarkFlagRequired(instanceConfigFlagName); err != nil {
log.Fatalf("Error marking flag %s as required: %v", instanceConfigFlagName, err)
}
}

// cleanUpTinkerbellTestResources deletes any test runner vm in vsphere and powers off all metal machines.
func cleanUpTinkerbellTestResources(ctx context.Context) error {
session, err := session.NewSession()
if err != nil {
return fmt.Errorf("creating session: %w", err)
}

deps, err := dependencies.NewFactory().WithGovc().Build(ctx)
if err != nil {
return err
}
defer deps.Close(ctx)
govc := deps.Govc

infraConfig, err := e2e.ReadRunnerConfig(instanceConfig)
if err != nil {
return fmt.Errorf("reading vms config for tests: %v", err)
}

govc.Configure(
executables.GovcConfig{
Username: infraConfig.Username,
Password: infraConfig.Password,
URL: infraConfig.URL,
Insecure: infraConfig.Insecure,
Datacenter: infraConfig.Datacenter,
},
)

var errs []error

if err := deleteSSMInstances(ctx, session); len(err) != 0 {
errs = append(errs, err...)
}

if err := deleteRunners(ctx, govc, infraConfig.Folder); len(err) != 0 {
errs = append(errs, err...)
}

if err := powerOffMachines(ctx, session); len(err) != 0 {
errs = append(errs, err...)
}

return errors.NewAggregate(errs)
}

func deleteSSMInstances(ctx context.Context, session *session.Session) []error {
var errs []error
if ssmInstances, err := e2e.ListTinkerbellSSMInstances(ctx, session); err != nil {
errs = append(errs, fmt.Errorf("listing ssm instances: %w", err))
} else if dryRun {
logger.Info("Found SSM instances", "instanceIDs", ssmInstances.InstanceIDs, "activationIDs", ssmInstances.ActivationIDs)
} else {
if _, err := ssm.DeregisterInstances(session, ssmInstances.InstanceIDs...); err != nil {
errs = append(errs, fmt.Errorf("deleting ssm instances: %w", err))
}
if _, err := ssm.DeleteActivations(session, ssmInstances.ActivationIDs...); err != nil {
errs = append(errs, fmt.Errorf("deleting ssm activations: %w", err))
}
}

return errs
}

func deleteRunners(ctx context.Context, govc *executables.Govc, folder string) []error {
var errs []error
if runners, err := govc.ListVMs(ctx, folder); err != nil {
errs = append(errs, fmt.Errorf("listing tinkerbell runners: %w", err))
} else if dryRun {
logger.Info("Found VM Runners", "vms", runners)
} else {
for _, vm := range runners {
if err := govc.DeleteVM(ctx, vm.Path); err != nil {
errs = append(errs, fmt.Errorf("deleting tinkerbell runner %s: %w", vm, err))
}
}
}

return errs
}

func powerOffMachines(_ context.Context, session *session.Session) []error {
var errs []error
if machines, err := e2e.ReadTinkerbellMachinePool(session, storageBucket); err != nil {
errs = append(errs, fmt.Errorf("reading tinkerbell machine pool: %v", err))
} else if dryRun {
logger.Info("Metal machine pool", "machines", names(machines))
} else {
if err = cleanup.PowerOffTinkerbellMachines(machines, true); err != nil {
errs = append(errs, fmt.Errorf("powering off tinkerbell machines: %v", err))
}
}

return errs
}

func names(h []*hardware.Machine) []string {
names := make([]string, 0, len(h))
for _, m := range h {
names = append(names, m.Hostname)
}

return names
}
37 changes: 28 additions & 9 deletions internal/pkg/ssm/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ssm
import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
)
Expand All @@ -12,7 +13,14 @@ type ActivationInfo struct {
ActivationID string
}

func CreateActivation(session *session.Session, instanceName, role string) (*ActivationInfo, error) {
// Tag is an SSM tag.
type Tag struct {
Key string
Value string
}

// CreateActivation creates an SSM Hybrid activation.
func CreateActivation(session *session.Session, instanceName, role string, tags ...Tag) (*ActivationInfo, error) {
s := ssm.New(session)

request := ssm.CreateActivationInput{
Expand All @@ -21,6 +29,12 @@ func CreateActivation(session *session.Session, instanceName, role string) (*Act
IamRole: &role,
}

for _, tag := range tags {
request.Tags = append(request.Tags,
&ssm.Tag{Key: aws.String(tag.Key), Value: aws.String(tag.Value)},
)
}

result, err := s.CreateActivation(&request)
if err != nil {
return nil, fmt.Errorf("failed to activate ssm instance %s: %v", instanceName, err)
Expand All @@ -29,17 +43,22 @@ func CreateActivation(session *session.Session, instanceName, role string) (*Act
return &ActivationInfo{ActivationCode: *result.ActivationCode, ActivationID: *result.ActivationId}, nil
}

func DeleteActivation(session *session.Session, activationId string) (*ssm.DeleteActivationOutput, error) {
// DeleteActivations deletes SSM activations.
func DeleteActivations(session *session.Session, ids ...string) ([]*ssm.DeleteActivationOutput, error) {
s := ssm.New(session)
var outputs []*ssm.DeleteActivationOutput
for _, id := range ids {
request := ssm.DeleteActivationInput{
ActivationId: &id,
}

request := ssm.DeleteActivationInput{
ActivationId: &activationId,
}
result, err := s.DeleteActivation(&request)
if err != nil {
return nil, fmt.Errorf("failed to delete ssm activation: %v", err)
}

result, err := s.DeleteActivation(&request)
if err != nil {
return nil, fmt.Errorf("failed to delete ssm activation: %v", err)
outputs = append(outputs, result)
}

return result, nil
return outputs, nil
}
41 changes: 35 additions & 6 deletions internal/pkg/ssm/instance.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package ssm

import (
"context"
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
)
Expand Down Expand Up @@ -30,16 +32,43 @@ func GetInstanceByActivationId(session *session.Session, id string) (*ssm.Instan
return infoList[0], nil
}

func DeregisterInstance(session *session.Session, id string) (*ssm.DeregisterManagedInstanceOutput, error) {
// DeregisterInstances deregisters SSM instances.
func DeregisterInstances(session *session.Session, ids ...string) ([]*ssm.DeregisterManagedInstanceOutput, error) {
s := ssm.New(session)
input := ssm.DeregisterManagedInstanceInput{
InstanceId: &id,
var outputs []*ssm.DeregisterManagedInstanceOutput
for _, id := range ids {
input := ssm.DeregisterManagedInstanceInput{
InstanceId: &id,
}

output, err := s.DeregisterManagedInstance(&input)
if err != nil {
return nil, fmt.Errorf("failed to deregister ssm instance %s: %v", id, err)
}

outputs = append(outputs, output)
}

output, err := s.DeregisterManagedInstance(&input)
return outputs, nil
}

func ListInstancesByTags(ctx context.Context, session *session.Session, tags ...Tag) ([]*ssm.InstanceInformation, error) {

Check failure on line 55 in internal/pkg/ssm/instance.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported function ListInstancesByTags should have comment or be unexported (revive)
s := ssm.New(session)
input := ssm.DescribeInstanceInformationInput{
Filters: make([]*ssm.InstanceInformationStringFilter, 0, len(tags)),
}

for _, tag := range tags {
input.Filters = append(input.Filters, &ssm.InstanceInformationStringFilter{
Key: aws.String("tag:" + tag.Key),
Values: aws.StringSlice([]string{tag.Value}),
})
}

output, err := s.DescribeInstanceInformation(&input)
if err != nil {
return nil, fmt.Errorf("failed to deregister ssm instance %s: %v", id, err)
return nil, fmt.Errorf("listing ssm instances by tags: %v", err)
}

return output, nil
return output.InstanceInformationList, nil
}
Loading

0 comments on commit 3e9c9a9

Please sign in to comment.