Skip to content

Instantly share code, notes, and snippets.

@kszarek
Created October 17, 2015 17:37
Show Gist options
  • Save kszarek/e5cc7a95521741951f36 to your computer and use it in GitHub Desktop.
Save kszarek/e5cc7a95521741951f36 to your computer and use it in GitHub Desktop.
ec2-snapshot-copy.go
package main
import (
"flag"
"fmt"
"os"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
)
var (
Version string
BuildTime string
GitCommit string
)
// got it from aws console
var awsOwnerID = "763925042285"
// regexp for snapshots from AMI
// we don't need to copy those between regions
var amiRegexp = regexp.MustCompile("^(Created by CreateImage)")
var sourceAwsRegion string
var destinationAwsRegion string
type Tag struct {
Key string
Value string
}
type Snapshot struct {
Id string
Desc string
Tags []*ec2.Tag
}
type AwsRegion struct {
src string
dest string
}
func createTag(svc *ec2.EC2, resouceID string, tags []*ec2.Tag) error {
params := &ec2.CreateTagsInput{
Resources: []*string{
aws.String(resouceID),
},
Tags: tags,
DryRun: aws.Bool(false),
}
// returns {} if successfully
_, err := svc.CreateTags(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Fprintln(os.Stderr, err.Error())
return err
}
fmt.Println("Create Tag", tags, "for", resouceID)
return nil
}
func findSnapshotsToCopy(svc *ec2.EC2) ([]Snapshot, error) {
var snapshotsToCopy []Snapshot
params := &ec2.DescribeSnapshotsInput{
OwnerIds: []*string{
aws.String(awsOwnerID),
},
// Filters: []*ec2.Filter{
// &ec2.Filter{
// Name: aws.String("tag:test"),
// Values: []*string{
// aws.String("true"),
// },
// },
// },
}
resp, err := svc.DescribeSnapshots(params)
if err != nil {
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Fprintln(os.Stderr, err.Error())
return snapshotsToCopy, err
}
if len(resp.Snapshots) == 0 {
fmt.Fprintln(os.Stderr, "No snapshots were found")
return snapshotsToCopy, nil
}
// go through all snapshots that we owns on given region
for _, snap := range resp.Snapshots {
// fmt.Println()
// skip snapshots older then 1 day
dayBefore := time.Now().AddDate(0, 0, -1)
if snap.StartTime.Before(dayBefore) {
fmt.Println("Skip old snapshot", *snap.SnapshotId, *snap.Description)
continue
}
// skip snapshots from AMI
m := amiRegexp.FindStringSubmatch(*snap.Description)
doCopy := true
if m == nil {
fmt.Println("Checking tags for", *snap.SnapshotId)
// checks snapshot tags
for _, t := range snap.Tags {
if *t.Key == "RegionCopy" {
fmt.Println(*snap.SnapshotId, "has been already copied to another region")
doCopy = false
break
}
if *t.Key == "OS_Version" {
fmt.Println(*snap.SnapshotId, "is AMI snapshot")
doCopy = false
break
}
}
if doCopy {
fmt.Println("Will copy:", *snap.SnapshotId, *snap.Description)
snapshotsToCopy = append(snapshotsToCopy, Snapshot{Id: *snap.SnapshotId, Desc: *snap.Description, Tags: snap.Tags})
}
}
}
return snapshotsToCopy, nil
}
func copySnapshot(a AwsRegion, snapshotID string, snapshotDescription string) (*ec2.CopySnapshotOutput, error) {
newDescription := "[Copied " + snapshotID + " from " + a.src + " ] " + snapshotDescription
fmt.Println("Copy snapshot id: " + snapshotID)
// make connection to destination region
svc := ec2.New(&aws.Config{Region: aws.String(a.dest)})
params := &ec2.CopySnapshotInput{
SourceRegion: aws.String(a.src), // Required
SourceSnapshotId: aws.String(snapshotID), // Required
Description: aws.String(newDescription),
DestinationRegion: aws.String(a.dest),
DryRun: aws.Bool(false),
}
resp, err := svc.CopySnapshot(params)
if err != nil {
return nil, err
}
// Pretty-print the response data.
fmt.Println("Copy snapshot", resp)
return resp, nil
}
func main() {
// source aws region
sourceAwsRegion = *flag.String("src", "us-east-1", "Source AWS Region")
// destination aws region
destinationAwsRegion := *flag.String("dest", "us-west-2", "Destination AWS Region")
// version
cli_version := flag.Bool("version", false, "Show version")
cli_help := flag.Bool("help", false, "Show help")
flag.Parse()
if *cli_version {
fmt.Println("Application version:", Version)
fmt.Println("UTC Build Time:", BuildTime)
fmt.Println("Git Commit Hash:", GitCommit)
os.Exit(0)
}
if *cli_help {
fmt.Println("Usage: ec2-snapshot-copy -src REGION -dest REGION")
os.Exit(0)
}
a := &AwsRegion{src: sourceAwsRegion, dest: destinationAwsRegion}
// make connection to AWS API
svc := ec2.New(&aws.Config{Region: aws.String(a.src)})
dest_svc := ec2.New(&aws.Config{Region: aws.String(a.dest)})
snapshotsToCopy, err := findSnapshotsToCopy(svc)
if err != nil {
panic(err)
}
fmt.Println("Snapshot list to copy", snapshotsToCopy)
for _, snapshot := range snapshotsToCopy {
resp, err := copySnapshot(*a, snapshot.Id, snapshot.Desc)
if err != nil {
if ec2err := err.(awserr.Error); ec2err != nil {
// A service error occurred.
fmt.Println("Error:", ec2err.Code(), ec2err.Message())
if ec2err.Code() == "ResourceLimitExceeded" {
fmt.Println("Too many snapshot copies in progress. Exit")
os.Exit(0)
} else {
panic(ec2err)
}
} else {
// A non-service error occurred.
panic(err)
}
} else {
var tag ec2.Tag
tag.Key = aws.String("RegionCopy")
tag.Value = aws.String("true")
tags := []*ec2.Tag{&tag}
// Tag{Key: "RegionCopy", Value: "true"}
createTag(svc, snapshot.Id, tags)
if err != nil {
panic(err)
}
createTag(dest_svc, *resp.SnapshotId, snapshot.Tags)
if err != nil {
panic(err)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment