Skip to content

Commit

Permalink
tests(e2e): add script to destroy old Azure image versions (#991)
Browse files Browse the repository at this point in the history
While checking on our Azure costs I noticed that Gallery costs were
slightly increasing over time. This happens because we are charged per
VM image version and up until now we had no policy to clean up unused
versions.

To mitigate this I've devised a script intended to be run just after the
image finishes building (as part of the weekly image build workflow).

The script destroys the oldest N-2 image versions of the specified
codename by default, with the number of versions to keep being
configurable via CLI argument in case we ever need to run it by hand.

A future commit will integrate this step as part of the scheduled
workflow.

Fixes UDENG-2786

-------------

I've tested this script locally and it's currently running on my fork
for `mantic`:
https://github.com/GabrielNagy/adsys/actions/runs/9031928826/job/24819142947
  • Loading branch information
GabrielNagy authored May 10, 2024
2 parents c6e96bd + 43cf693 commit f6733d2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/e2e-build-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,6 @@ jobs:
if: steps.check-vm-template.outputs.image-version != ''
run: |
go run ./e2e/cmd/build_base_image/02_create_vm_template
- name: Purge old template versions
run: |
go run ./e2e/cmd/build_base_image/99_destroy_previous_versions --codename ${{ matrix.codename }}
96 changes: 96 additions & 0 deletions e2e/cmd/build_base_image/99_destroy_previous_versions/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Package main provides a script to destroy previous versions of an Azure VM
// image in order to optimize storage costs.
package main

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

log "github.com/sirupsen/logrus"
"github.com/ubuntu/adsys/e2e/internal/az"
"github.com/ubuntu/adsys/e2e/internal/command"
)

var codename string
var versionsToKeep int

func main() {
os.Exit(run())
}

func run() int {
cmd := command.New(action, command.WithValidateFunc(validate))
cmd.Usage = fmt.Sprintf(`go run ./%s [options]
Generalize an Azure VM to use as a template for E2E tests.
Options:
--codename codename for which to delete image versions
--versions-to-keep number of versions to keep in storage (default: 2)
This script will:
- query all image versions for the specified codename
- delete all but the latest N versions, as specified by --versions-to-keep
The machine must be authenticated to Azure via the Azure CLI.`, filepath.Base(os.Args[0]))

cmd.AddStringFlag(&codename, "codename", "", "")
cmd.AddIntFlag(&versionsToKeep, "versions-to-keep", 2, "")

return cmd.Execute(context.Background())
}

func action(ctx context.Context, _ *command.Command) error {
log.Infof("Getting image versions for %q", codename)

out, _, err := az.RunCommand(ctx, "sig", "image-version", "list",
"--resource-group", "AD",
"--gallery-name", "AD",
"--gallery-image-definition", az.ImageDefinitionName(codename),
"--output", "tsv",
"--query", "[].name",
)
if err != nil {
return err
}

versions := strings.Split(string(out), "\n")
if len(versions) <= versionsToKeep {
log.Infof("No versions to delete for %q", codename)
return nil
}

versionsToDelete := versions[:len(versions)-versionsToKeep-1]
log.Infof("Deleting %d versions for %q: %v", len(versionsToDelete), codename, versionsToDelete)

for _, version := range versionsToDelete {
_, _, err := az.RunCommand(ctx, "sig", "image-version", "delete",
"--resource-group", "AD",
"--gallery-name", "AD",
"--gallery-image-definition", az.ImageDefinitionName(codename),
"--gallery-image-version", version,
)
if err != nil {
return err
}
}

log.Infof("Successfully deleted %d image versions", len(versionsToDelete))

return nil
}

func validate(_ context.Context, _ *command.Command) (err error) {
if codename == "" {
return errors.New("codename must be specified")
}
if versionsToKeep < 1 {
return errors.New("versions-to-keep must be a positive number")
}

return nil
}
5 changes: 5 additions & 0 deletions e2e/internal/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ func (c *Command) AddBoolFlag(param *bool, name string, value bool, usage string
c.fSet.BoolVar(param, name, value, usage)
}

// AddIntFlag adds an integer flag to the command.
func (c *Command) AddIntFlag(param *int, name string, value int, usage string) {
c.fSet.IntVar(param, name, value, usage)
}

func (c *Command) setGlobalFlags() {
c.fSet.StringVar(&c.GlobalFlags.InventoryFile, "i", inventory.DefaultPath, "Use custom inventory file")
c.fSet.StringVar(&c.GlobalFlags.InventoryFile, "inventory-file", inventory.DefaultPath, "Use custom inventory file")
Expand Down

0 comments on commit f6733d2

Please sign in to comment.