// SendMessage sends an SNS message to an SNS region.
// See http://docs.aws.amazon.com/sdk-for-go/api/service/sns.html#type-PublishInput
func SendMessage(region string, topic string, subject string, message string) {
	if region == "" {
		log.Fatal("SNS region is required")
	}

	if topic == "" {
		log.Fatal("SNS topic is required")
	}

	if subject == "" {
		log.Fatal("SNS subject is required")
	}

	if message == "" {
		log.Fatal("SNS message is required")
	}

	params := &sns.PublishInput{
		Subject:   aws.String(subject),
		Message:   aws.String(message),
		TargetArn: aws.String(topic),
	}

	sns := sns.New(session.New(), aws.NewConfig().WithRegion(region))

	_, err := sns.Publish(params)
	awserror.HandleError(err)
}
// DestroySnapshots deletes snapshots greater than SnapshotManager's NumSnapshotsToRetain for a given volume
func (mgr *SnapshotManager) DestroySnapshots(volume *ec2.Volume) (snapshotsDestroyed int) {
	if mgr.NumSnapshotsToRetain < 1 {
		log.Fatal("NumSnapshotsToRetain should be great than 0")
	}

	params := &ec2.DescribeSnapshotsInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("volume-id"),
				Values: []*string{
					aws.String(*volume.VolumeId),
				},
			},
		},
	}
	resp, err := mgr.ec2.DescribeSnapshots(params)
	awserror.HandleError(err)

	snapshots := resp.Snapshots
	sort.Sort(ByStartTime(snapshots))

	numberSnapshotsToDelete := len(snapshots) - mgr.NumSnapshotsToRetain
	for numberSnapshotsToDelete > 0 {
		params := &ec2.DeleteSnapshotInput{
			SnapshotId: aws.String(*resp.Snapshots[numberSnapshotsToDelete-1].SnapshotId),
		}

		_, err := mgr.ec2.DeleteSnapshot(params)
		if err != nil && err.(awserr.Error).Code() != "InvalidSnapshot.InUse" {
			awserror.HandleError(err)
		}

		numberSnapshotsToDelete--
		snapshotsDestroyed++
	}

	return snapshotsDestroyed
}
// TagResource creates tags for an EBS snapshot
func (mgr *SnapshotManager) TagResource(id *string, tags []*ec2.Tag) {
	// remove tags containing "aws:", as these are reserved by AWS
	// and cannot be duplicated for other resources
	filteredTags := tags[:0]
	for _, tag := range tags {
		if !strings.Contains(*tag.Key, "aws:") {
			filteredTags = append(filteredTags, tag)
		}
	}

	params := &ec2.CreateTagsInput{
		Resources: []*string{
			aws.String(*id),
		},
		Tags: filteredTags,
	}

	_, err := mgr.ec2.CreateTags(params)
	awserror.HandleError(err)
}
// CreateSnapshot creates an EBS snapshot for a specific EBS volume, optionally copying
// any volume tags depending on SnapshotManager's CopyVolumeTags. If the volume has a Name tag,
// it is used as the snapshot's description.
func (mgr *SnapshotManager) CreateSnapshot(volume *ec2.Volume) {
	description := fmt.Sprintf("Snapshot for volume %s", *volume.VolumeId)
	for _, tag := range volume.Tags {
		if *tag.Key == "Name" {
			description = *tag.Value
		}
	}

	params := &ec2.CreateSnapshotInput{
		Description: aws.String(description),
		VolumeId:    aws.String(*volume.VolumeId),
	}

	log.Printf("Starting snapshot for %s in region %s", *volume.VolumeId, mgr.Region)

	snapshot, err := mgr.ec2.CreateSnapshot(params)
	awserror.HandleError(err)

	if mgr.CopyVolumeTags && len(volume.Tags) > 0 {
		mgr.TagResource(snapshot.SnapshotId, volume.Tags)
	}
}
// SnapshotVolumes is a helper method that wraps several operations. It queries for attached
// EBS volumes in the SnapshotManager's region, generates new snapshots, optionally
// copying any volume tags, and keeps the last X snapshots as specified by retainCount.
func (mgr *SnapshotManager) SnapshotVolumes() (snapshotsGenerated int) {
	params := &ec2.DescribeVolumesInput{
		Filters: []*ec2.Filter{
			{
				Name: aws.String("attachment.status"),
				Values: []*string{
					aws.String("attached"),
				},
			},
		},
	}

	resp, err := mgr.ec2.DescribeVolumes(params)
	awserror.HandleError(err)

	for _, volume := range resp.Volumes {
		mgr.CreateSnapshot(volume)
		mgr.DestroySnapshots(volume)
		snapshotsGenerated++
	}

	return snapshotsGenerated
}