diff --git a/mantle/cmd/ore/aws/upload.go b/mantle/cmd/ore/aws/upload.go index 11f1151a97..486d133a7d 100644 --- a/mantle/cmd/ore/aws/upload.go +++ b/mantle/cmd/ore/aws/upload.go @@ -47,29 +47,27 @@ After a successful run, the final line of output will be a line of JSON describi SilenceUsage: true, } - uploadSourceObject string - uploadBucket string - uploadImageName string - uploadImageArchitecture string - uploadFile string - uploadDiskSizeGiB uint - uploadDiskSizeInspect bool - uploadDeleteObject bool - uploadForce bool - uploadSourceSnapshot string - uploadObjectFormat aws.EC2ImageFormat - uploadAMIName string - uploadAMIDescription string - uploadPublic bool - uploadGrantUsers []string - uploadGrantUsersSnapshot []string - uploadTags []string - uploadIMDSv2Only bool - uploadVolumeType string - uploadX86BootMode string - uploadCreateWinLIAMI bool - uploadWinLIwindowsServerAMI string - uploadWinLIInstanceType string + uploadSourceObject string + uploadBucket string + uploadImageName string + uploadImageArchitecture string + uploadFile string + uploadDiskSizeGiB uint + uploadDiskSizeInspect bool + uploadDeleteObject bool + uploadForce bool + uploadSourceSnapshot string + uploadObjectFormat aws.EC2ImageFormat + uploadAMIName string + uploadAMIDescription string + uploadPublic bool + uploadGrantUsers []string + uploadGrantUsersSnapshot []string + uploadTags []string + uploadIMDSv2Only bool + uploadVolumeType string + uploadX86BootMode string + uploadBillingProductCode string ) func init() { @@ -94,9 +92,7 @@ func init() { cmdUpload.Flags().BoolVar(&uploadIMDSv2Only, "imdsv2-only", false, "enable IMDSv2-only support") cmdUpload.Flags().StringVar(&uploadVolumeType, "volume-type", "gp3", "EBS volume type (gp3, gp2, io1, st1, sc1, standard, etc.)") cmdUpload.Flags().StringVar(&uploadX86BootMode, "x86-boot-mode", "uefi-preferred", "Set boot mode (uefi-preferred, uefi)") - cmdUpload.Flags().BoolVar(&uploadCreateWinLIAMI, "winli", false, "Create a Windows LI AMI") - cmdUpload.Flags().StringVar(&uploadWinLIwindowsServerAMI, "windows-ami", "", "Windows Server AMI used to create a Windows LI AMI") - cmdUpload.Flags().StringVar(&uploadWinLIInstanceType, "winli-instance-type", "t2.large", "ec2 instance type used to create a Windows LI AMI") + cmdUpload.Flags().StringVar(&uploadBillingProductCode, "billing-product-code", "", "set billing product code") } func defaultBucketNameForRegion(region string) string { @@ -140,10 +136,18 @@ func runUpload(cmd *cobra.Command, args []string) error { fmt.Fprintf(os.Stderr, "Unrecognized args in aws upload cmd: %v\n", args) os.Exit(2) } + if uploadSourceObject != "" && uploadSourceSnapshot != "" { + fmt.Fprintf(os.Stderr, "At most one of --source-object and --source-snapshot may be specified.\n") + os.Exit(2) + } if uploadDiskSizeInspect && (uploadSourceObject != "" || uploadSourceSnapshot != "") { fmt.Fprintf(os.Stderr, "--disk-size-inspect cannot be used with --source-object or --source-snapshot.\n") os.Exit(2) } + if uploadFile == "" { + fmt.Fprintf(os.Stderr, "specify --file\n") + os.Exit(2) + } if uploadImageName == "" { fmt.Fprintf(os.Stderr, "unknown image name; specify --name\n") os.Exit(2) @@ -152,27 +156,31 @@ func runUpload(cmd *cobra.Command, args []string) error { fmt.Fprintf(os.Stderr, "unknown AMI name; specify --ami-name\n") os.Exit(2) } - if uploadCreateWinLIAMI { - if uploadWinLIwindowsServerAMI == "" { - fmt.Fprintf(os.Stderr, "--windows-ami must be provided with --winli\n") - os.Exit(2) - } - if uploadSourceSnapshot == "" { - fmt.Fprintf(os.Stderr, "--source-snapshot must be provided with --winli\n") - os.Exit(2) + + var err error + if uploadDiskSizeInspect { + imageInfo, err := util.GetImageInfo(uploadFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to query size of disk: %v\n", err) + os.Exit(1) } - } else { - if uploadSourceObject != "" && uploadSourceSnapshot != "" { - fmt.Fprintf(os.Stderr, "At most one of --source-object and --source-snapshot may be specified.\n") - os.Exit(2) + plog.Debugf("Image size: %v\n", imageInfo.VirtualSize) + const GiB = 1024 * 1024 * 1024 + uploadDiskSizeGiB = uint(imageInfo.VirtualSize / GiB) + // Round up if there's leftover + if imageInfo.VirtualSize%GiB > 0 { + uploadDiskSizeGiB += 1 } - if uploadFile == "" { - fmt.Fprintf(os.Stderr, "specify --file\n") - os.Exit(2) + } + + if uploadSourceSnapshot != "" { + uploadDiskSizeGiB, err = API.FindSnapshotDiskSizeGiB(uploadSourceSnapshot) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to query size of disk from snapshot: %v\n", err) + os.Exit(1) } } - var err error var s3URL *url.URL if uploadSourceObject != "" { s3URL, err = url.Parse(uploadSourceObject) @@ -191,21 +199,6 @@ func runUpload(cmd *cobra.Command, args []string) error { s3BucketName := s3URL.Host s3ObjectPath := strings.TrimPrefix(s3URL.Path, "/") - if uploadDiskSizeInspect { - imageInfo, err := util.GetImageInfo(uploadFile) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to query size of disk: %v\n", err) - os.Exit(1) - } - plog.Debugf("Image size: %v\n", imageInfo.VirtualSize) - const GiB = 1024 * 1024 * 1024 - uploadDiskSizeGiB = uint(imageInfo.VirtualSize / GiB) - // Round up if there's leftover - if imageInfo.VirtualSize%GiB > 0 { - uploadDiskSizeGiB += 1 - } - } - if uploadForce { err := API.RemoveImage(uploadAMIName) if err != nil { @@ -275,20 +268,10 @@ func runUpload(cmd *cobra.Command, args []string) error { } } - // create AMIs and grant permissions - var amiID string - if uploadWinLIwindowsServerAMI == "" { - amiID, err = API.CreateHVMImage(sourceSnapshot, uploadDiskSizeGiB, uploadAMIName, uploadAMIDescription, uploadImageArchitecture, uploadVolumeType, uploadIMDSv2Only, uploadX86BootMode) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to create HVM image: %v\n", err) - os.Exit(1) - } - } else { - amiID, sourceSnapshot, err = API.CreateWinLiImage(sourceSnapshot, uploadAMIName, uploadAMIDescription, uploadImageArchitecture, uploadWinLIwindowsServerAMI, uploadWinLIInstanceType, uploadVolumeType) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to create WinLI image: %v\n", err) - os.Exit(1) - } + amiID, err = API.CreateHVMImage(sourceSnapshot, uploadDiskSizeGiB, uploadAMIName, uploadAMIDescription, uploadImageArchitecture, uploadVolumeType, uploadIMDSv2Only, uploadX86BootMode, uploadBillingProductCode) + if err != nil { + fmt.Fprintf(os.Stderr, "unable to create HVM image: %v\n", err) + os.Exit(1) } if len(uploadGrantUsers) > 0 { diff --git a/mantle/platform/api/aws/ec2.go b/mantle/platform/api/aws/ec2.go index 0195d76dc8..6cb530c956 100644 --- a/mantle/platform/api/aws/ec2.go +++ b/mantle/platform/api/aws/ec2.go @@ -263,258 +263,6 @@ func (a *API) CreateInstances(name, keyname, userdata string, count uint64, minD return insts, nil } -// StopInstances will stop all instances provided in the ids slice and will -// block until all instances are in the "stopped" state -func (a *API) StopInstances(ids []string) error { - if len(ids) == 0 { - return nil - } - input := &ec2.StopInstancesInput{ - InstanceIds: aws.StringSlice(ids), - } - - if _, err := a.ec2.StopInstances(input); err != nil { - return err - } - - // loop until all machines are stopped - var insts []*ec2.Instance - timeout := 10 * time.Minute - delay := 10 * time.Second - err := util.WaitUntilReady(timeout, delay, func() (bool, error) { - desc, err := a.ec2.DescribeInstances(&ec2.DescribeInstancesInput{ - InstanceIds: aws.StringSlice(ids), - }) - if err != nil { - // Keep retrying if the InstanceID disappears momentarily - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidInstanceID.NotFound" { - plog.Debugf("instance ID not found, retrying: %v", err) - return false, nil - } - return false, err - } - insts = desc.Reservations[0].Instances - - for _, i := range insts { - if *i.State.Name != ec2.InstanceStateNameStopped { - return false, nil - } - } - return true, nil - }) - - if err != nil { - if errTerminate := a.TerminateInstances(ids); errTerminate != nil { - return fmt.Errorf("terminating instances failed: %v after instances failed to stop: %v", errTerminate, err) - } - return fmt.Errorf("waiting for instances to stop: %v", err) - } - - return nil -} - -// AttachVolume will attach the provided volume and will block until -// the volume is in the "In-Use" state -func (a *API) AttachVolume(instanceID string, volumeID string, device string) error { - _, err := a.ec2.AttachVolume(&ec2.AttachVolumeInput{ - VolumeId: aws.String(volumeID), - InstanceId: aws.String(instanceID), - Device: aws.String(device), - }) - if err != nil { - return fmt.Errorf("error attaching volume: %v", err) - } - - // loop until the volume is attached - var vol *ec2.Volume - timeout := 10 * time.Minute - delay := 10 * time.Second - err = util.WaitUntilReady(timeout, delay, func() (bool, error) { - desc, err := a.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{ - VolumeIds: aws.StringSlice([]string{volumeID}), - }) - if err != nil { - // Keep retrying if the VolumeID disappears momentarily - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidVolume.NotFound" { - plog.Debugf("volume ID not found, retrying: %v", err) - return false, nil - } - return false, err - } - - vol = desc.Volumes[0] - if *vol.State != ec2.VolumeStateInUse { - return false, nil - } - return true, nil - }) - - if err != nil { - return fmt.Errorf("waiting for volume to attach: %v", err) - } - - return nil -} - -// DetachVolume will detach the provided volume and will block until -// the volume is in the "Avalailable" state -func (a *API) DetachVolume(volumeID string) error { - _, err := a.ec2.DetachVolume(&ec2.DetachVolumeInput{ - VolumeId: aws.String(volumeID), - }) - if err != nil { - return fmt.Errorf("error detaching volume: %v", err) - } - - // loop until the volume is detached - var vol *ec2.Volume - timeout := 10 * time.Minute - delay := 10 * time.Second - err = util.WaitUntilReady(timeout, delay, func() (bool, error) { - desc, err := a.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{ - VolumeIds: aws.StringSlice([]string{volumeID}), - }) - if err != nil { - // Keep retrying if the VolumeID disappears momentarily - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidVolume.NotFound" { - plog.Debugf("volume ID not found, retrying: %v", err) - return false, nil - } - return false, err - } - - vol = desc.Volumes[0] - if *vol.State != ec2.VolumeStateAvailable { - return false, nil - } - return true, nil - }) - - if err != nil { - return fmt.Errorf("waiting for volume to detach: %v", err) - } - - return nil -} - -// DeleteVolumes schedules ec2 volumes for deletion -func (a *API) DeleteVolumes(volumeIDs []string) error { - for _, volumeID := range volumeIDs { - _, err := a.ec2.DeleteVolume(&ec2.DeleteVolumeInput{ - VolumeId: aws.String(volumeID), - }) - if err != nil { - return fmt.Errorf("error deleting volume: %v", err) - } - } - - return nil -} - -// GetInstanceVolumeIdByDevice returns the VolumeId of the volume -// attached to the instance at the specified device name (e.g., "/dev/xvda"). -func (a *API) GetInstanceVolumeIdByDevice(instanceID string, deviceName string) (string, error) { - timeout := 1 * time.Minute - delay := 1 * time.Second - var volume string - err := util.RetryUntilTimeout(timeout, delay, func() error { - desc, err := a.ec2.DescribeInstances(&ec2.DescribeInstancesInput{ - InstanceIds: aws.StringSlice([]string{instanceID}), - }) - if err != nil { - return fmt.Errorf("error describing instances: %v", err) - } - - for _, vol := range desc.Reservations[0].Instances[0].BlockDeviceMappings { - if *vol.DeviceName == deviceName { - volume = *vol.Ebs.VolumeId - return nil - } - } - return fmt.Errorf("failed to find volume id by device: %v", deviceName) - }) - - if err != nil { - return "", err - } - - return volume, nil -} - -// returns the VolumeID after creating a volume from a provided snapshot -func (a *API) CreateVolumeFromSnapshot(name string, snapshotID string, volumetype string, availabilityZone string) (string, error) { - newVolume, err := a.ec2.CreateVolume(&ec2.CreateVolumeInput{ - AvailabilityZone: aws.String(availabilityZone), - SnapshotId: aws.String(snapshotID), - VolumeType: aws.String(volumetype), - TagSpecifications: tagSpecCreatedByMantle(name, ec2.ResourceTypeVolume), - }) - if err != nil { - return "", fmt.Errorf("failed to create volume: %v", err) - } - - // loop until the volume is available - timeout := 10 * time.Minute - delay := 10 * time.Second - err = util.WaitUntilReady(timeout, delay, func() (bool, error) { - desc, err := a.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{ - VolumeIds: aws.StringSlice([]string{*newVolume.VolumeId}), - }) - if err != nil { - // Keep retrying if the VolumeID disappears momentarily - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "InvalidVolume.NotFound" { - plog.Debugf("volume ID not found, retrying: %v", err) - return false, nil - } - return false, err - } - - vol := desc.Volumes[0] - if *vol.State != ec2.VolumeStateAvailable { - return false, nil - } - return true, nil - }) - - if err != nil { - return "", fmt.Errorf("waiting for volume to detach: %v", err) - } - - return *newVolume.VolumeId, nil -} - -// ReplaceRootVolume will swap the root volume of `instanceID` at `deviceName` with `newRootVolumeID` -// the replaced root volume is detached and deleted in the process -func (a *API) ReplaceRootVolume(instanceID string, deviceName string, newRootVolumeID string) error { - replacedRootVolume, err := a.GetInstanceVolumeIdByDevice(instanceID, deviceName) - if err != nil { - return fmt.Errorf("failed to find root volume %q", err) - } - - if err = a.DetachVolume(replacedRootVolume); err != nil { - return fmt.Errorf("error detaching root volume: %v", err) - } - - if err = a.DeleteVolumes([]string{replacedRootVolume}); err != nil { - return fmt.Errorf("error deleting root volume: %v", err) - } - - if err = a.AttachVolume(instanceID, newRootVolumeID, deviceName); err != nil { - return fmt.Errorf("error attaching new root volume: %v", err) - } - - // verify the root volume of the instance matches the target volume - vol, err := a.GetInstanceVolumeIdByDevice(instanceID, deviceName) - if err != nil { - return fmt.Errorf("failed to find replaced root volume %q", err) - } - if vol != newRootVolumeID { - return fmt.Errorf("failed to replace root volume") - } - - return nil -} - // gcEC2 will terminate ec2 instances older than gracePeriod. // It will only operate on ec2 instances tagged with 'mantle' to avoid stomping // on other resources in the account. @@ -557,45 +305,7 @@ func (a *API) gcEC2(gracePeriod time.Duration) error { } } - err = a.TerminateInstances(toTerminate) - if err != nil { - return fmt.Errorf("error terminating instances: %v", err) - } - - volumesRes, err := a.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("tag:CreatedBy"), - Values: aws.StringSlice([]string{"mantle"}), - }, - }, - }) - if err != nil { - return fmt.Errorf("error describing volumes: %v", err) - } - - toDelete := []string{} - - for _, volume := range volumesRes.Volumes { - if volume.CreateTime.After(durationAgo) { - plog.Debugf("ec2: skipping volume %s due to being too new", *volume.VolumeId) - // skip, still too new - continue - } - - if *volume.State == ec2.VolumeStateAvailable { - toDelete = append(toDelete, *volume.VolumeId) - } else { - plog.Infof("ec2: skipping volume in state %s", *volume.State) - } - } - - err = a.DeleteVolumes(toDelete) - if err != nil { - return fmt.Errorf("error deleteing volumes: %v", err) - } - - return nil + return a.TerminateInstances(toTerminate) } // TerminateInstances schedules EC2 instances to be terminated. diff --git a/mantle/platform/api/aws/images.go b/mantle/platform/api/aws/images.go index fab3d5f77b..95c4e51bd4 100644 --- a/mantle/platform/api/aws/images.go +++ b/mantle/platform/api/aws/images.go @@ -338,78 +338,7 @@ func (a *API) CreateImportRole(bucket string) error { return nil } -func (a *API) CreateWinLiImage(snapshotID string, name string, description string, architecture string, windowsAMI string, instanceType string, volumetype string) (string, string, error) { - //var awsArch string - if architecture != "x86_64" { - return "", "", fmt.Errorf("unsupported WinLi architecture %q", architecture) - } - - opts := *a.opts - opts.AMI = windowsAMI - opts.InstanceType = instanceType - opts.SecurityGroup = "winli-builder" - aa, err := New(&opts) - if err != nil { - return "", "", err - } - - instanceName := util.RandomName("winli-builder") - keyname := "" - userdata := "" - count := 1 - minDiskSize := 0 - useInstanceProfile := false - - insts, err := aa.CreateInstances(instanceName, keyname, userdata, uint64(count), int64(minDiskSize), useInstanceProfile) - if err != nil { - return "", "", fmt.Errorf("failed to create windows server instance %q", err) - } - - winliInstanceId := *insts[0].InstanceId - - // stop the instance in order to replace the root volume - err = aa.StopInstances([]string{winliInstanceId}) - if err != nil { - return "", "", fmt.Errorf("stop instances failed: %v", err) - } - - // create a new root volume from the CoreOS snapshot - availabilityZone := *insts[0].Placement.AvailabilityZone - newRootVolumeName := name + "-winli-root-volume" - newRootVolumeID, err := aa.CreateVolumeFromSnapshot(newRootVolumeName, snapshotID, volumetype, availabilityZone) - if err != nil { - return "", "", fmt.Errorf("error creating volume from snapshot: %v", err) - } - - rootVolumeDevName := "/dev/sda1" - err = aa.ReplaceRootVolume(winliInstanceId, rootVolumeDevName, newRootVolumeID) - if err != nil { - return "", "", fmt.Errorf("error replacing root volume: %v", err) - } - - // if we made it here, we have a windows instance with an CoreOS root volume. - // create an image based on the modified instance and return the ImageId - params := &ec2.CreateImageInput{ - Name: aws.String(name), - Description: aws.String(description), - InstanceId: aws.String(winliInstanceId), - } - - // create AMI from the modified windows instance - imageID, snapshotID, err := aa.CreateImageFromInstance(params) - - // delete the windows instance - terminateErr := aa.TerminateInstances([]string{winliInstanceId}) - if terminateErr != nil { - // log the failure and continue - plog.Infof("failed to terminate instance %v: %v", winliInstanceId, terminateErr) - } - - return imageID, snapshotID, err - -} - -func (a *API) CreateHVMImage(snapshotID string, diskSizeGiB uint, name string, description string, architecture string, volumetype string, imdsv2Only bool, X86BootMode string) (string, error) { +func (a *API) CreateHVMImage(snapshotID string, diskSizeGiB uint, name string, description string, architecture string, volumetype string, imdsv2Only bool, X86BootMode string, billingCode string) (string, error) { var awsArch string var bootmode string if architecture == "" { @@ -458,6 +387,13 @@ func (a *API) CreateHVMImage(snapshotID string, diskSizeGiB uint, name string, d if imdsv2Only { params.ImdsSupport = aws.String("v2.0") } + // Set the billing product code for this AMI, if provided. The account must be + // authorized by AWS to specify billing product codes. This is used by the + // Windows License Included creation workflow to set the Windows LI billing + // product code on an AMI. + if billingCode != "" { + params.BillingProducts = []*string{aws.String(billingCode)} + } return a.createImage(params) } @@ -559,89 +495,6 @@ func (a *API) createImage(params *ec2.RegisterImageInput) (string, error) { return imageID, nil } -// CreateImageFromInstance will return the ImageID of the image created from an ec2 instance -// A new snapshot is created in the process, so the SnapshotID is also returned -func (a *API) CreateImageFromInstance(params *ec2.CreateImageInput) (string, string, error) { - var imageID string - timeout := 5 * time.Minute - delay := 10 * time.Second - - res, err := a.ec2.CreateImage(params) - if err == nil { - imageID = *res.ImageId - plog.Infof("created image %v", imageID) - } else if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "InvalidAMIName.Duplicate" { - // The AMI already exists. Get its ID. Due to races, this - // may take several attempts. - err = util.RetryUntilTimeout(timeout, delay, func() error { - imageID, err = a.FindImage(*params.Name) - if err != nil { - return err - } - if imageID != "" { - plog.Infof("found existing image %v, reusing", imageID) - return nil - } - return fmt.Errorf("failed to locate image %q", *params.Name) - }) - if err != nil { - return "", "", fmt.Errorf("error finding duplicate image id: %v", err) - } - } else { - return "", "", fmt.Errorf("error creating AMI: %v", err) - } - - // wait for the AMI to be in the "available" state - timeout = 10 * time.Minute - err = util.WaitUntilReady(timeout, delay, func() (bool, error) { - image, err := a.DescribeImage(imageID) - if err != nil { - return false, err - } - - if *image.State != ec2.ImageStateAvailable { - return false, nil - } - return true, nil - }) - - if err != nil { - return "", "", fmt.Errorf("waiting for image to be available: %v", err) - } - - image, err := a.DescribeImage(imageID) - if err != nil { - return "", "", err - } - - snapshotID, err := getImageSnapshotID(image) - if err != nil { - return "", "", err - } - - // Attempt to tag inside of a retry loop; AWS eventual consistency means that just because - // the FindImage call found the AMI it might not be found by the CreateTags call - err = util.RetryConditional(6, 5*time.Second, func(err error) bool { - if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "InvalidAMIID.NotFound" { - return true - } - return false - }, func() error { - // tag the new image and the new snapshot - // We do this even in the already-exists path in case the previous - // run was interrupted. - return a.CreateTags([]string{imageID, snapshotID}, map[string]string{ - "Name": *params.Name, - }) - }) - if err != nil { - return "", "", fmt.Errorf("couldn't tag image name: %v", err) - } - - return imageID, snapshotID, nil - -} - // GrantVolumePermission grants permission to access an EC2 snapshot volume (referenced by its snapshot ID) // to a list of AWS users (referenced by their 12-digit numerical user IDs). func (a *API) GrantVolumePermission(snapshotID string, userIDs []string) error { @@ -999,3 +852,18 @@ func getImageSnapshotID(image *ec2.Image) (string, error) { // and it's just a sorta eventual consistency thing return "", fmt.Errorf("no backing block device for %v", image.ImageId) } + +func (a *API) FindSnapshotDiskSizeGiB(snapshotID string) (uint, error) { + result, err := a.ec2.DescribeSnapshots(&ec2.DescribeSnapshotsInput{ + SnapshotIds: []*string{&snapshotID}, + }) + if err != nil { + return 0, fmt.Errorf("failed to describe snapshot: %v", err) + } + + if len(result.Snapshots) == 0 { + return 0, fmt.Errorf("no snapshot found with ID %s", snapshotID) + } + + return uint(aws.Int64Value(result.Snapshots[0].VolumeSize)), nil +} diff --git a/mantle/platform/api/aws/network.go b/mantle/platform/api/aws/network.go index 3e2a531f20..e5eb8c54a3 100644 --- a/mantle/platform/api/aws/network.go +++ b/mantle/platform/api/aws/network.go @@ -56,16 +56,9 @@ func (a *API) createSecurityGroup(name string) (string, error) { if err != nil { return "", err } - - var desc string - if name == "winli-builder" { - desc = "mantle security group for winli" - } else { - desc = "mantle security group for testing" - } sg, err := a.ec2.CreateSecurityGroup(&ec2.CreateSecurityGroupInput{ GroupName: aws.String(name), - Description: aws.String(desc), + Description: aws.String("mantle security group for testing"), VpcId: aws.String(vpcId), TagSpecifications: tagSpecCreatedByMantle(name, ec2.ResourceTypeSecurityGroup), }) diff --git a/src/cosalib/aws.py b/src/cosalib/aws.py index ab1fb6a7c8..bbac584d3b 100644 --- a/src/cosalib/aws.py +++ b/src/cosalib/aws.py @@ -135,8 +135,10 @@ def aws_run_ore(build, args): if 'aws-x86-boot-mode' in image_json: ore_args.extend(['--x86-boot-mode', image_json['aws-x86-boot-mode']]) + assert bool(args.winli) == bool(args.winli_billing_product), \ + "--winli-billing-product and --winli must be specified together" + if args.winli: - ore_args.extend(["--winli"]) winli_name = "-winli" winli_description = " Windows License Included" buildmeta_key = "aws-winli" @@ -150,22 +152,16 @@ def aws_run_ore(build, args): if source_snapshot is None: raise Exception(("Unable to find AMI source snapshot for " f"{region} region")) - ore_args.extend(['--source-snapshot', f"{source_snapshot}"]) - else: ore_args.extend([ - '--file', f"{build.image_path}", - '--disk-size-inspect' + '--source-snapshot', f"{source_snapshot}", + '--billing-product-code', f"{args.winli_billing_product}" ]) + else: + ore_args.extend(['--disk-size-inspect']) winli_name = "" winli_description = "" buildmeta_key = "amis" - if args.windows_ami: - ore_args.extend(['--windows-ami', f"{args.windows_ami}"]) - - if args.winli_instance_type: - ore_args.extend(['--winli-instance-type', f"{args.winli_instance_type}"]) - if args.bucket: ore_args.extend(['--bucket', f"{args.bucket}"]) @@ -175,6 +171,7 @@ def aws_run_ore(build, args): '--name', f"{build.build_name}{winli_name}-{build.build_id}-{build.basearch}", '--ami-description', f"{build.summary} {build.build_id} {build.basearch}{winli_description}", '--arch', f"{build.basearch}", + '--file', f"{build.image_path}", '--delete-object' ]) for user in args.grant_user: @@ -223,6 +220,5 @@ def aws_cli(parser): parser.add_argument("--tags", help="list of key=value tags to attach to the AMI", action='append', default=[]) parser.add_argument("--winli", action="store_true", help="create an AWS Windows LI Ami") - parser.add_argument("--windows-ami", help="Windows Server AMI ID used to create AWS Windows LI image") - parser.add_argument("--winli-instance-type", help="ec2 instance type used to create AWS Windows LI image") + parser.add_argument("--winli-billing-product", help="Windows billing product code used to create a Windows LI AMI") return parser