Skip to content

Commit

Permalink
fix(cli): make new CLI recognize old assembly versions
Browse files Browse the repository at this point in the history
In the latest release, we changed the CX protocol, requiring users of a
newer CLI to upgrade their CDK framework libraries (to be safe).

This particular change was actually backwards compatible, so add the
requisite code to the CLI to recognize and fix that condition.

Fixes #4294.
  • Loading branch information
rix0rrr committed Oct 1, 2019
1 parent 22a5ada commit 5b7f938
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 13 deletions.
6 changes: 4 additions & 2 deletions packages/@aws-cdk/cx-api/lib/cloud-assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ArtifactManifest, ArtifactType, CloudArtifact } from './cloud-artifact'
import { CloudFormationStackArtifact } from './cloudformation-artifact';
import { topologicalSort } from './toposort';
import { TreeCloudArtifact } from './tree-cloud-artifact';
import { CLOUD_ASSEMBLY_VERSION, verifyManifestVersion } from './versioning';
import { CLOUD_ASSEMBLY_VERSION, upgradeAssemblyManifest, verifyManifestVersion } from './versioning';

/**
* A manifest which describes the cloud assembly.
Expand Down Expand Up @@ -73,7 +73,9 @@ export class CloudAssembly {
*/
constructor(directory: string) {
this.directory = directory;
this.manifest = JSON.parse(fs.readFileSync(path.join(directory, MANIFEST_FILE), 'UTF-8'));

const manifest = JSON.parse(fs.readFileSync(path.join(directory, MANIFEST_FILE), 'UTF-8'));
this.manifest = upgradeAssemblyManifest(manifest);

this.version = this.manifest.version;
verifyManifestVersion(this.version);
Expand Down
59 changes: 50 additions & 9 deletions packages/@aws-cdk/cx-api/lib/versioning.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
import semver = require('semver');
import { AssemblyManifest } from './cloud-assembly';

// ----------------------------------------------------------------------
//
// READ THIS FIRST WHEN CHANGING THIS FILE
//
// ----------------------------------------------------------------------
//
// You need (and only need) to bump the CLOUD_ASSEMBLY_VERSION if the cloud
// assembly needs new features from the CDK CLI. Examples: new fields, new
// behavior, new artifact types.
//
// If that happens, you set the CLOUD_ASSEMBLY_VERSION to the *next* (not the
// current!) CDK version that will be released. This is done to produce
// useful error messages.
//
// When you do this, you will force users of a new library to upgrade the CLI
// (good), but UNLESS YOU ALSO IMPLEMENT 'upgradeAssemblyManifest' you will also
// force people who have installed a newer CLI to upgrade their libraries (bad!).
// Do that too, unless you have a very good reason not to.

/**
* Bump this to the library version if and only if the CX protocol changes.
Expand All @@ -8,13 +28,8 @@ import semver = require('semver');
* might as well use the software version as protocol version and immediately
* generate a useful error message from this.
*
* Note the following:
*
* - The versions are not compared in a semver way, they are used as
* opaque ordered tokens.
* - The version needs to be set to the NEXT releasable version when it's
* updated (as the current verison in package.json has already been released!)
* - The request does not have versioning yet, only the response.
* Note that the versions are not compared in a semver way, they are used as
* opaque ordered tokens.
*/
export const CLOUD_ASSEMBLY_VERSION = '1.10.0';

Expand All @@ -27,14 +42,30 @@ export function verifyManifestVersion(manifetVersion: string) {

// if framework > cli, we require a newer cli version
if (semver.gt(frameworkVersion, toolkitVersion)) {
throw new Error(`CDK CLI >= ${frameworkVersion} is required to interact with this app`);
throw new Error(`A newer version of the CDK CLI (>= ${frameworkVersion}) is necessary to interact with this app`);
}

// if framework < cli, we require a newer framework version
if (semver.lt(frameworkVersion, toolkitVersion)) {
throw new Error(
`CDK CLI can only be used with apps created by CDK >= ${CLOUD_ASSEMBLY_VERSION}`);
`A newer version of the CDK framework (>= ${CLOUD_ASSEMBLY_VERSION}) is necessary to interact with this version of the CLI`);
}
}

/**
* Upgrade old manifest versions to later manifest version here (if possible).
*
* Use this to make the toolkit recognize old assembly versions. This function should
* add newly required fields with appropriate default values, etc.
*/
export function upgradeAssemblyManifest(manifest: AssemblyManifest): AssemblyManifest {

if (manifest.version === '0.36.0') {
// Adding a new artifact type, old version will not have it so painless upgrade.
manifest = justUpgradeVersion(manifest, '1.10.0');
}

return manifest;
}

function parseSemver(version: string) {
Expand All @@ -45,3 +76,13 @@ function parseSemver(version: string) {

return ver;
}

/**
* Return a copy of the manifest with just the version field updated
*
* Useful if there are protocol changes that are automatically backwards
* compatible.
*/
function justUpgradeVersion(manifest: AssemblyManifest, version: string): AssemblyManifest {
return Object.assign({}, manifest, { version });
}
11 changes: 9 additions & 2 deletions packages/@aws-cdk/cx-api/test/cloud-assembly.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ test('assets', () => {
expect(assembly.stacks[0].assets).toMatchSnapshot();
});

test('can-read-0.36.0', () => {
// WHEN
new CloudAssembly(path.join(FIXTURES, 'single-stack-0.36'));
// THEN: no eexception
expect(true).toBeTruthy();
});

test('dependencies', () => {
const assembly = new CloudAssembly(path.join(FIXTURES, 'depends'));
expect(assembly.stacks).toHaveLength(4);
Expand All @@ -106,6 +113,6 @@ test('fails for invalid dependencies', () => {

test('verifyManifestVersion', () => {
verifyManifestVersion(CLOUD_ASSEMBLY_VERSION);
expect(() => verifyManifestVersion('0.31.0')).toThrow(`CDK CLI can only be used with apps created by CDK >= ${CLOUD_ASSEMBLY_VERSION}`);
expect(() => verifyManifestVersion('99.99.99')).toThrow(`CDK CLI >= 99.99.99 is required to interact with this app`);
expect(() => verifyManifestVersion('0.31.0')).toThrow(`A newer version of the CDK framework (>= ${CLOUD_ASSEMBLY_VERSION}) is necessary to interact with this version of the CLI`);
expect(() => verifyManifestVersion('99.99.99')).toThrow(`A newer version of the CDK CLI (>= 99.99.99) is necessary to interact with this app`);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"version": "1.10.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "foo.tree.json"
}
},
"MyStackName": {
"type": "aws:cloudformation:stack",
"environment": "aws://37736633/us-region-1",
"properties": {
"templateFile": "template.json"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"Resources": {
"MyBucket": {
"Type": "AWS::S3::Bucket"
}
}
}

0 comments on commit 5b7f938

Please sign in to comment.