aws-cdk icon indicating copy to clipboard operation
aws-cdk copied to clipboard

@aws-cdk/aws-ec2: Wrong enum declare enum AmazonLinuxKernel

Open adonisd opened this issue 3 years ago • 6 comments

Describe the bug

the enum declare enum AmazonLinuxKernel return 5.10 as latest kernel where it should be 5.15 for AMAZON_LINUX_2022 This leads to an error in CDK when trying to deploy:

failed: Error [ValidationError]: Unable to fetch parameters [/aws/service/ami-amazon-linux-latest/al2022-ami-kernel-5.10-x86_64] from parameter store for this account.
    at Request.extractError (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/protocol/query.js:50:29)
    at Request.callListeners (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/request.js:686:14)
    at Request.transition (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/request.js:688:12)
    at Request.callListeners (/home/adonis/work/FOLDER/FOLDER-cicd/CORE/node_modules/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
  code: 'ValidationError',
  time: 2022-07-06T10:59:21.163Z,
  requestId: '4b933ef2-11b7-4525-8ac2-59252ad34825',
  statusCode: 400,
  retryable: false,
  retryDelay: 311.0208741007581
}

Unable to fetch parameters [/aws/service/ami-amazon-linux-latest/al2022-ami-kernel-5.10-x86_64] from parameter store for this account.

Expected Behavior

to return 5.15

Current Behavior

it returns 5.10

Reproduction Steps

new ec2.Instance(that, "id", {
    instanceName: "name",
    vpc: options.vpc,
    instanceType: ec2.InstanceType.of(
      ec2.InstanceClass.T3A,
      ec2.InstanceSize.MICRO
    ),
    machineImage: new ec2.AmazonLinuxImage({
      generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2022,
    }),
    vpcSubnets: {
      subnetType: ec2.SubnetType.PUBLIC,
    },
    securityGroup: securityGroup,
    keyName: "key",
    userData: ec2.UserData.custom(
      fs.readFileSync("./lib/scripts/ssh-tunnel.sh", "utf8")
    ),
  });

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

1.162.0 (build 0bad87b)

Framework Version

No response

Node.js Version

v16.13.1

OS

Linux (X86_64)

Language

Typescript

Language Version

4.6.3

Other information

No response

adonisd avatar Jul 06 '22 11:07 adonisd

This should be correct according to AL2022 docs https://docs.aws.amazon.com/linux/al2022/ug/get-started.html#launch-from-cloudformation

However, you can see here that as you say, it's been updated to 5.15

We can fix this pretty easily, all we have to do is update the enum in the code here https://github.com/aws/aws-cdk/blob/v1.162.0/packages/@aws-cdk/aws-ec2/lib/machine-image.ts#L480-L485

@robertd @corymhall I think that having the 5_X is confusing if we were to add a 5_15. Should we update the 5_X enum to be equal to 5.15, or should we deprecate 5_X and create two new values for 5_10 and 5_15?

peterwoodworth avatar Jul 06 '22 17:07 peterwoodworth

I think what we may want to do here is to use the default kernel version instead of specifying a specific version. For example /aws/service/ami-amazon-linux-latest/al2022-ami-kernel-default-x86_64 will default to the latest version.

AMIs are the same.

aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/al2022-ami-kernel-default-x86_64 /aws/service/ami-amazon-linux-latest/al2022-ami-kernel-5.15-x86_64 --profile sandbox --query 'Parameters[].Value'

Another, larger effort option is to change the kernel version enum into an abstract class the provides all of the options + an override.

e.g.

AmazonLinuxKernel.DEFAULT;
AmazonLinuxKernel.5_10;
AmazonLinuxKernel.of('kernel-5.10');

It seems like this issue impacts a significant number of customers, and I've tagged it as P1, which means it should be on our near-term roadmap.

We welcome community contributions! If you are able, we encourage you to contribute (https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) a bug fix or new feature to the CDK. If you decide to contribute, please start an engineering discussion in this issue to ensure there is a commonly understood design before submitting code. This will minimize the number of review cycles and get your code merged faster.

corymhall avatar Jul 06 '22 17:07 corymhall

Ugh... I remember this being such a pain in the rear to add more options to it w/o introducing a breaking change. #18117

robertd avatar Jul 07 '22 16:07 robertd

I gave it a shot – would love to be able to run cdk deploy again with my stack ❤️

tomfa avatar Aug 03 '22 11:08 tomfa

Thanks for the contribution @tomfa! We should be able to take a look soon

peterwoodworth avatar Aug 03 '22 18:08 peterwoodworth

Encountered the same issue with 2.37.0 (build aba5ef6) when tried to use AMAZON_LINUX_2022.

xZero707 avatar Aug 10 '22 09:08 xZero707

So just for the record, a workaround is to look up a valid image here and directly reference the SSM parameter like so?

machineImage: ec2.MachineImage.fromSsmParameter('/aws/service/ami-amazon-linux-latest/al2022-ami-kernel-5.15-x86_64')

486 avatar Aug 23 '22 09:08 486

@486 yes that is the workaround for now. Long term we will be refactoring the AmazonLinuxImage to better handle the differences between the generations.

corymhall avatar Aug 23 '22 12:08 corymhall

@corymhall Any update on this blocker? Even if just the AmazonLInuxKernel enum allowed for KERNEL_DEFAULT, this would make this much better than current state, where the AmazonLinuxImage class is unusable for AL2022.

jsamuel1 avatar Jan 05 '23 02:01 jsamuel1

@jsamuel1 I've thought a little bit about the design of a new implementation, but haven't had the time to start working on it. I'm just putting it here if someone wants to pick it up since I probably won't have time in the near term.


This is an interesting use case and one of the only places that we are using “latest” today. This works by pulling the AMI to use from an AWS managed SSM parameter. To view the currently available options run the following command:

As an aside, Currently this uses a value that is resolved at deploy time, i.e. AWS::SSM::Parameter::ValueAWS::EC2::Image::Id

This can cause errors on deploy if the parameter doesn't exist (it could have been removed on deprecation). Another option could be to use a fromLookup instead and resolve the AMI at synth time. This would provide another interpretation of "latest" since we would cache the value in cdk.context.json. "latest" would mean latest version at the time of initial lookup, but after that the user would have to "pull" updates by clearing and updating the context.

aws ssm get-parameters-by-path --path /aws/service/ami-amazon-linux-latest

latestAmazonLinux constructs the correct path based on a bunch of different criteria.

  • The generation (Amazon Linux(2)(2022))
  • Edition
  • Kernel version
  • virtualization
  • CPU type (x86 or arm)
  • storage

You end up with something like this /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2.

I propose we depricate latestAmazonLinux and create three+ new methods, latestAmznLinux, latestAmznLinux2, & latestAmznLinux2022. Each generation should be treated as a separate option because they are different distributions. They also have different supported criteria (listed above).

Here is a rough high level implementation to illustrate. I’ve also separated out AmznLinux2 into latestAmznLinux2 & defaultAmznLinux2. This is similar to how we would implement a LATEST and an LTS. The default kernel version installed in Amazon Linux 2 is 4.14 and will be supported until Amazon Linux 2 goes EOL, but there is a newer version that is available. Users can select defaultAmznLinux2 and still get updated AMIs, but know that the kernel version will stay the default. An important caveat is that it may be possible for the default to change (just like how LTS versions change), but it will change less frequently.

/**
 * Common options across all generations. These options
 * do not affect the "version" of the AMI so the user
 * can specify. Note that the user cannot specify
 * the kernelVersion because that should always be the "latest"
 */
export interface LatestAmznLinuxOptions {
  readonly edition?: AmazonLinuxEdition;
  readonly cpuType?: AmazonLinuxCpuType;
}

/**
 * Storage doesn't apply to al2022 for example
 */
export interface LatestAmznLinuxProps extends LatestAmznLinuxOptions {
  readonly storage?: AmazonLinuxStorage;
 readonly virtualization?: AmazonLinuxVirt;
}

export interface LatestAmznLinux2022Props extends LatestAmznLinuxOptions {}

export class MachineImage {
  public latestAmznLinux(props: LatestAmznLinuxProps): MachineImage {
    return new AmznLinuxImage(props);
  };
  
  public latestAmznLinxu2(props: LatestAmznLinux2Props): MachineImage {
    return new AmznLinux2Image({
      ...props,
      kernelVersion: AmznLinux2Kernel.LATEST,
    });
  };
  
  public defaultAmznLinux2(props: LatestAmznLinux2Props): MachineImage {
    return new AmznLinux2Image({
      ...props,
      kernelVersion: AmznLinux2Kernel.DEFAULT,
    });
  };
}

export class AmznLinux2Kernel extends AmazonLinuxKernelVersion {
   /**
   * The latest kernel version available as an SSM parameter is 5.10.
   * Version 5.15 is technically the "latest" kernel version
   * but it is not yet available in an AMI
   * (users have to manually upgrade the kernel).
   *
   * When an AMI for 5.15 is released this will be updated to 5.15
   */
   public static readonly LATEST = new AmznLinux2Kernel('5.10');
   
  /**
   * The default kernel version for Amazon Linux 2 is 4.14 and
   * the SSM parameter does not include it in the name
   * (i.e. 
   * /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2)
   */
  public static readonly DEFAULT = new AmznLinux2Kernel('default');
  
  public static readonly KERNEL_5_10 = new AmznLinux2Kernel('5.10');
  
  /**
   *
   * @deprecated - this specific version can no longer be specified.
   * 4.14 is now available as the DEFAULT version.
   */
  public static readonly KERNEL_4_14 = new AmznLinux2Kernel('4.14');
  
  constructor(private readonly version: string) {}
  
  public toString(): {
    return this.version === 'default'
      ? undefined
      : `kernel-${this.version}`;
  }
}

Now if a user wants to consume this.

new ec2.Instance(this, 'Instance', {
   machineImage: MachineImage.latestAmznLinux2(),
});

// or you can still be very specific

new ec2.Instance(this, 'Instance', {
  machineImage: new AmznLinux2Image({
    kernelVersion: AmznLinux2Kernel.KERNEL_5_10,
    edition: AmazonLinuxEdition.MINIMAL,
  });
});

Benefits of this approach

  • Users must opt-in to using a “latest” version. It is explicitly clear that when they do so this will always be the latest version in that category.
  • Users can otherwise specify a specific version to deploy, which they can manually update when they are ready.

Limitations of this approach

  • We need to keep up with “latest”.

corymhall avatar Jan 05 '23 12:01 corymhall

I like the approach proposed by @corymhall above. It also allows us to inject a warning message similar to when certain properties are deprecated to advise customers to stop using Amazon Linux 1 and (eventually) Amazon Linux 2 when they go into EoS.

The downside, about needing to keep up with "latest", can be mitigated by having an internal discussion with the Amazon Linux AMI publishing team. Insist that they provide more "generic" parameters that we can take advantage of, for example, Amazon Linux 2 should have either a "5.x" kernel version, or a "default".

It also allows us to expand the scope into different families using this as a template. For example, we could look into doing the same for Ubuntu AMIs.

We will, until Version 3, need to maintain a deprecated "meta" class that points to the variant classes, but that doesn't seem too bad. At least we can provide warnings like if someone uses Amazon Linux 2022 in the meta class we can say "please instead use this class as you will run into issues when attempting to deploy this template" with an error.


Aside: I do thing there is scope to discuss things like "schema" vs "functionality" updates. At the moment they're coupled, but it may be worth separating them out, and the "schema" can be auto-generated based upon upstream inputs. It would complicate the troubleshooting a little bit, but would allow people who experience a breaking change in a functionality still to use the latest "schema", but that's probably out of scope for this thread.

taylorb-syd avatar Feb 16 '23 23:02 taylorb-syd

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

github-actions[bot] avatar Apr 13 '23 15:04 github-actions[bot]