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 }