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  }