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