github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/imagepublishers/amipublisher/launchInstances.go (about) 1 package amipublisher 2 3 import ( 4 "errors" 5 "strconv" 6 "time" 7 8 "github.com/Cloud-Foundations/Dominator/lib/awsutil" 9 "github.com/Cloud-Foundations/Dominator/lib/constants" 10 "github.com/Cloud-Foundations/Dominator/lib/log" 11 libtags "github.com/Cloud-Foundations/Dominator/lib/tags" 12 "github.com/aws/aws-sdk-go/aws" 13 "github.com/aws/aws-sdk-go/aws/session" 14 "github.com/aws/aws-sdk-go/service/ec2" 15 ) 16 17 func launchInstances(targets awsutil.TargetList, skipList awsutil.TargetList, 18 imageSearchTags, vpcSearchTags, subnetSearchTags, 19 securityGroupSearchTags libtags.Tags, instanceType string, 20 rootVolumeSize uint, sshKeyName string, tags map[string]string, 21 replaceInstances bool, logger log.Logger) ([]InstanceResult, error) { 22 if imageSearchTags["Name"] == "" { 23 return nil, errors.New("no image Name search tag") 24 } 25 resultsChannel := make(chan InstanceResult, 1) 26 numTargets, err := awsutil.ForEachTarget(targets, skipList, 27 func(awsService *ec2.EC2, account, region string, logger log.Logger) { 28 instanceId, privateIp, err := launchInstanceInTarget(awsService, 29 imageSearchTags, vpcSearchTags, subnetSearchTags, 30 securityGroupSearchTags, instanceType, rootVolumeSize, 31 sshKeyName, tags, replaceInstances, logger) 32 if err != nil { 33 logger.Println(err) 34 } 35 resultsChannel <- InstanceResult{ 36 awsutil.Target{account, region}, 37 instanceId, 38 privateIp, 39 err, 40 } 41 }, 42 logger) 43 // Collect results. 44 results := make([]InstanceResult, 0, numTargets) 45 for i := 0; i < numTargets; i++ { 46 result := <-resultsChannel 47 if result.AccountName == "" || result.Region == "" { 48 continue 49 } 50 results = append(results, result) 51 } 52 return results, err 53 } 54 55 func launchInstanceInTarget(awsService *ec2.EC2, 56 imageSearchTags, vpcSearchTags, subnetSearchTags, 57 securityGroupSearchTags libtags.Tags, 58 instanceType string, rootVolumeSize uint, sshKeyName string, 59 tags libtags.Tags, replaceInstances bool, 60 logger log.Logger) (string, string, error) { 61 oldInstances, err := getInstances(awsService, tags["Name"]) 62 if err != nil { 63 return "", "", err 64 } 65 if len(oldInstances) > 0 { 66 if !replaceInstances { 67 return "", "", nil 68 } 69 } 70 image, err := findImage(awsService, imageSearchTags) 71 if err != nil { 72 return "", "", err 73 } 74 if image == nil { 75 // TODO(rgooch): Create bootstrap image (for unpackers only). 76 return "", "", errors.New("no image found") 77 } 78 instance, err := launchInstance(awsService, image, rootVolumeSize, tags, 79 vpcSearchTags, subnetSearchTags, securityGroupSearchTags, instanceType, 80 sshKeyName) 81 if err != nil { 82 return "", "", err 83 } 84 instanceId := aws.StringValue(instance.InstanceId) 85 privateIp := aws.StringValue(instance.PrivateIpAddress) 86 logger.Printf("launched: %s with private IP: %s\n", instanceId, privateIp) 87 err = awsService.WaitUntilInstanceRunning(&ec2.DescribeInstancesInput{ 88 InstanceIds: aws.StringSlice([]string{instanceId}), 89 }) 90 if err != nil { 91 if err := libTerminateInstances(awsService, instanceId); err != nil { 92 logger.Printf("error terminating: %s\n", err) 93 } 94 return "", "", err 95 } 96 logger.Printf("running: %s, connecting...\n", instanceId) 97 address := privateIp + ":" + strconv.Itoa(constants.ImageUnpackerPortNumber) 98 retryUntil := time.Now().Add(time.Minute * 10) 99 srpcClient, err := connectToUnpacker(address, retryUntil, logger) 100 if err != nil { 101 if err := libTerminateInstances(awsService, instanceId); err != nil { 102 logger.Printf("error terminating: %s\n", err) 103 } 104 return "", "", err 105 } 106 srpcClient.Close() 107 if len(oldInstances) > 0 { 108 ids := getInstanceIds(oldInstances) 109 if err := libTerminateInstances(awsService, ids...); err != nil { 110 logger.Printf("error terminating: %s\n", err) 111 } else { 112 logger.Println("terminated old instance(s): ", ids) 113 } 114 } 115 return instanceId, privateIp, nil 116 } 117 118 func launchInstancesForImages(resources []Resource, 119 vpcSearchTags, subnetSearchTags, securityGroupSearchTags libtags.Tags, 120 instanceType string, rootVolumeSize uint, sshKeyName string, 121 tags map[string]string, logger log.Logger) ([]InstanceResult, error) { 122 resultsChannel := make(chan InstanceResult, 1) 123 err := forEachResource(resources, false, 124 func(session *session.Session, awsService *ec2.EC2, resource Resource, 125 logger log.Logger) error { 126 instanceId, privateIp, err := launchInstanceForImage(awsService, 127 resource, vpcSearchTags, subnetSearchTags, 128 securityGroupSearchTags, instanceType, rootVolumeSize, 129 sshKeyName, tags, logger) 130 if err != nil { 131 logger.Println(err) 132 } 133 resultsChannel <- InstanceResult{ 134 awsutil.Target{resource.AccountName, resource.Region}, 135 instanceId, 136 privateIp, 137 err, 138 } 139 return err 140 }, 141 logger) 142 // Collect results. 143 results := make([]InstanceResult, 0, len(resources)) 144 for i := 0; i < len(resources); i++ { 145 result := <-resultsChannel 146 if result.AccountName == "" || result.Region == "" { 147 continue 148 } 149 results = append(results, result) 150 } 151 return results, err 152 } 153 154 func launchInstanceForImage(awsService *ec2.EC2, resource Resource, 155 vpcSearchTags, subnetSearchTags, 156 securityGroupSearchTags libtags.Tags, 157 instanceType string, rootVolumeSize uint, sshKeyName string, 158 tags libtags.Tags, logger log.Logger) (string, string, error) { 159 instance, err := launchInstance(awsService, 160 &ec2.Image{ImageId: aws.String(resource.AmiId)}, 161 rootVolumeSize, 162 tags, vpcSearchTags, subnetSearchTags, securityGroupSearchTags, 163 instanceType, sshKeyName) 164 if err != nil { 165 return "", "", err 166 } 167 instanceId := aws.StringValue(instance.InstanceId) 168 privateIp := aws.StringValue(instance.PrivateIpAddress) 169 logger.Printf("launched: %s with private IP: %s\n", instanceId, privateIp) 170 if err := createTags(awsService, instanceId, tags); err != nil { 171 return "", "", nil 172 } 173 err = awsService.WaitUntilInstanceRunning(&ec2.DescribeInstancesInput{ 174 InstanceIds: aws.StringSlice([]string{instanceId}), 175 }) 176 if err != nil { 177 return "", "", err 178 } 179 logger.Printf("running: %s: \n", instanceId) 180 return instanceId, privateIp, nil 181 }