Hello!

Today I will show how you can filter ebs snapshots by date with golang.

To begin with I will create a folder and in it, I will initialize the go module

mkdir snapshotFilter
cd snapshotFilter
go mod init snapshotFilter

You need to create main.go which will contain the code. Let’s start with the main function and imports

package main

import (
	"flag"
	"fmt"
	"log"
	"sort"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
)

func main() {
	sess := session.Must(session.NewSession())
	ec2Client := ec2.New(sess)

	filterDate := flag.String("snapshot-date", time.Now().Format(time.RFC3339), "Provide snapshot creation date")
	snapshotName := flag.String("snapshot-name", nil, "Provide snapshot name")

	flag.Parse()

	parsedTime, err := time.Parse(time.RFC3339, *filterDate)
	if err != nil {
		log.Fatalln("Not able to parse time", err)
	}

	if *snapshotName == "" {
		log.Fatalln("Please provide snapshot name")
	}

	fmt.Println(filterSnapshotByDate(parsedTime, snapshotName, ec2Client))
}

The file starts with the name of the package and the imports. Next is the main function. Main function creates the ec2 client, snapshotName and the filterDate command line parameter. These parameters will be used to enter the date of the snapshot from the terminal and also snapshot name. If filterDate parameter is not entered the current date will be used in RFC3339 format (2006-01-02T15: 04: 05Z07: 00). To specify this value from the console it will look like this - go run main.go -snapshot-date=2020-03-20T06:24:05Z. If snapshotName parameter is not specified, the program will end with the message Please provide snapshot name. I also need to convert the date passed by the user of type string to type time.Time. I will use the time.Parse method, the first argument is the layout, and because I know that my format is RFC3339 I can specify time.RFC3339, the second parameter it is the time value provided by the user. filterSnapshotByDate(filterDate, snapshotName, ec2Client) this is the method that will do the filtering.

To filter a snapshot, first get a list of all snapshots. I will use the Name tag to get exactly the snapshot I need.

func filterSnapshotByDate(filterDate time.Time, snapshotName *string, client *ec2.EC2) string {
	input := &ec2.DescribeSnapshotsInput{Filters: []*ec2.Filter{
		{Name: aws.String("tag:Name"), Values: []*string{snapshotName}},
	}}

	var result []*ec2.Snapshot
	err := client.DescribeSnapshotsPages(input,
		func(page *ec2.DescribeSnapshotsOutput, lastPage bool) bool {
			result = append(result, page.Snapshots...)
			return !lastPage
		})
	if err != nil {
		log.Fatalln("Was not able to get list of snapshots", err)
	}

In the input variable, I will create a filter that will filter the snapshots by name. And then using the pagination function DescribeSnapshotsPages I will get a list of all snapshots and add them to the list var result []*ec2.Snapshot.

In order to filter the snapshot by date, I must first sort them.

	sort.Slice(result, func(i, j int) bool {
		return result[i].StartTime.After(*result[j].StartTime)
	})

Because the objects I filter are of type time.Time, I can’t compare them with > or <. For such cases go has an After method. With his help I sort the slice with snapshots.

Now I can filter the snapshots, with cycle I will walk through the sorted snapshots and when the date entered by the user will be greater than the date of the snapshot I will return the snapshot id.

	for _, snapshot := range result {
		if filterDate.After(*snapshot.StartTime) {
			log.Println("Found snapshot with date", *snapshot.StartTime, "which are the closest date to", filterDate.String)
			return *snapshot.SnapshotId
		}
	}

If such a snapshot is not found, I will return the id of the last snapshot

	log.Println("Not found spashot with specific date", filterDate.String, "going to use latest", *result[0].StartTime)
	return *result[0].SnapshotId
}