github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/mdbd/loadAws.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/Cloud-Foundations/Dominator/lib/awsutil"
     8  	libjson "github.com/Cloud-Foundations/Dominator/lib/json"
     9  	"github.com/Cloud-Foundations/Dominator/lib/log"
    10  	"github.com/Cloud-Foundations/Dominator/lib/mdb"
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/service/ec2"
    13  )
    14  
    15  type awsGeneratorType struct {
    16  	targets        awsutil.TargetList
    17  	filterTagsFile string
    18  }
    19  
    20  type resultType struct {
    21  	mdb *mdb.Mdb
    22  	err error
    23  }
    24  
    25  type tagFilterType struct {
    26  	Key    string
    27  	Values []string
    28  }
    29  
    30  var credentialsStore *awsutil.CredentialsStore
    31  
    32  func loadCredentials() error {
    33  	if credentialsStore == nil {
    34  		var err error
    35  		credentialsStore, err = awsutil.LoadCredentials()
    36  		if err != nil {
    37  			return err
    38  		}
    39  	}
    40  	return nil
    41  }
    42  
    43  func newAwsGenerator(params makeGeneratorParams) (generator, error) {
    44  	if err := loadCredentials(); err != nil {
    45  		return nil, err
    46  	}
    47  	return &awsGeneratorType{
    48  			targets: awsutil.TargetList{awsutil.Target{params.args[1],
    49  				params.args[0]}}},
    50  		nil
    51  }
    52  
    53  func newAwsFilteredGenerator(params makeGeneratorParams) (generator, error) {
    54  	if err := loadCredentials(); err != nil {
    55  		return nil, err
    56  	}
    57  	gen := awsGeneratorType{
    58  		filterTagsFile: params.args[1],
    59  	}
    60  	if err := gen.targets.Set(params.args[0]); err != nil {
    61  		return nil, err
    62  	}
    63  	return &gen, nil
    64  }
    65  
    66  func newAwsLocalGenerator(params makeGeneratorParams) (generator, error) {
    67  	if err := loadCredentials(); err != nil {
    68  		return nil, err
    69  	}
    70  	region, err := awsutil.GetLocalRegion()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return &awsGeneratorType{
    75  			targets: awsutil.TargetList{awsutil.Target{"", region}}},
    76  		nil
    77  }
    78  
    79  func (g *awsGeneratorType) Generate(unused_datacentre string,
    80  	logger log.DebugLogger) (*mdb.Mdb, error) {
    81  	resultsChannel := make(chan resultType, 1)
    82  	numTargets, err := credentialsStore.ForEachEC2Target(g.targets, nil,
    83  		func(awsService *ec2.EC2, account, region string, logger log.Logger) {
    84  			var result resultType
    85  			result.mdb, result.err = g.generateForTarget(awsService, account,
    86  				region, logger)
    87  			resultsChannel <- result
    88  		},
    89  		false, logger)
    90  	// Collect results.
    91  	var newMdb mdb.Mdb
    92  	hostnames := make(map[string]struct{})
    93  	for i := 0; i < numTargets; i++ {
    94  		result := <-resultsChannel
    95  		if result.err != nil {
    96  			if err == nil {
    97  				err = result.err
    98  				logger.Println(err)
    99  			}
   100  			continue
   101  		}
   102  		for _, machine := range result.mdb.Machines {
   103  			if _, ok := hostnames[machine.Hostname]; ok {
   104  				txt := "duplicate hostname: " + machine.Hostname
   105  				logger.Println(txt)
   106  				if err == nil {
   107  					err = errors.New(txt)
   108  				}
   109  				break
   110  			}
   111  			newMdb.Machines = append(newMdb.Machines, machine)
   112  		}
   113  	}
   114  	return &newMdb, err
   115  }
   116  
   117  func (g *awsGeneratorType) generateForTarget(svc *ec2.EC2, accountName string,
   118  	region string, logger log.Logger) (
   119  	*mdb.Mdb, error) {
   120  	filters, err := g.makeFilters()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	resp, err := svc.DescribeInstances(&ec2.DescribeInstancesInput{
   125  		Filters: filters,
   126  	})
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	return extractMdb(resp, accountName, region), nil
   131  }
   132  
   133  func (g *awsGeneratorType) makeFilters() ([]*ec2.Filter, error) {
   134  	filters := make([]*ec2.Filter, 1, 1)
   135  	filters[0] = &ec2.Filter{
   136  		Name:   aws.String("instance-state-name"),
   137  		Values: []*string{aws.String(ec2.InstanceStateNameRunning)},
   138  	}
   139  	if g.filterTagsFile == "" {
   140  		return filters, nil
   141  	}
   142  	var tags []tagFilterType
   143  	if err := libjson.ReadFromFile(g.filterTagsFile, &tags); err != nil {
   144  		return nil, fmt.Errorf("error loading tags file: %s", err)
   145  	}
   146  	for _, tag := range tags {
   147  		filters = append(filters, &ec2.Filter{
   148  			Name:   aws.String("tag:" + tag.Key),
   149  			Values: aws.StringSlice(tag.Values),
   150  		})
   151  	}
   152  	return filters, nil
   153  }
   154  
   155  func extractMdb(output *ec2.DescribeInstancesOutput, accountName string,
   156  	region string) *mdb.Mdb {
   157  	var result mdb.Mdb
   158  	for _, reservation := range output.Reservations {
   159  		accountId := aws.StringValue(reservation.OwnerId)
   160  		for _, instance := range reservation.Instances {
   161  			if instance.PrivateDnsName != nil {
   162  				machine := mdb.Machine{
   163  					Hostname: *instance.PrivateDnsName,
   164  					AwsMetadata: &mdb.AwsMetadata{
   165  						AccountId:   accountId,
   166  						AccountName: accountName,
   167  						InstanceId:  *instance.InstanceId,
   168  						Region:      region,
   169  						Tags:        make(map[string]string),
   170  					},
   171  				}
   172  				if instance.PrivateIpAddress != nil {
   173  					machine.IpAddress = *instance.PrivateIpAddress
   174  				}
   175  				extractTags(instance.Tags, &machine)
   176  				result.Machines = append(result.Machines, machine)
   177  			}
   178  		}
   179  	}
   180  	return &result
   181  }
   182  
   183  func extractTags(tags []*ec2.Tag, machine *mdb.Machine) {
   184  	for _, tag := range tags {
   185  		if tag.Key == nil || tag.Value == nil {
   186  			continue
   187  		}
   188  		machine.AwsMetadata.Tags[*tag.Key] = *tag.Value
   189  		switch *tag.Key {
   190  		case "RequiredImage":
   191  			machine.RequiredImage = *tag.Value
   192  		case "PlannedImage":
   193  			machine.PlannedImage = *tag.Value
   194  		case "DisableUpdates":
   195  			machine.DisableUpdates = true
   196  		case "OwnerGroup":
   197  			machine.OwnerGroup = *tag.Value
   198  		}
   199  	}
   200  }