Skip to content
Merged

Seed #2546

Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
e3900e3
seed setup and seed secrets
Jan 22, 2025
8260a2e
skeleton of sandbox seed command
Jan 22, 2025
8935019
merge main
Jan 28, 2025
31c6da1
seed policy generation
Jan 30, 2025
bf92de4
grants for storage
Jan 30, 2025
6db8fbc
changed seed policy name to amplifySeedPolicy
Jan 31, 2025
889e903
test for secrets
Jan 31, 2025
1dd9c54
removed managed seed policy
Feb 5, 2025
ec798d4
set secret and seed command
Feb 5, 2025
0747b04
generate seed policy command
Feb 5, 2025
a996f26
auth APIs - incomplete
Feb 6, 2025
e1a2bc7
created folders, getting data from generateConfig
Feb 7, 2025
0f1bd95
removed static, changed name of AuthOutputsReader to ConfigReader
Feb 7, 2025
5dd262d
fixed seed commands, figured out callback for TOTP
Feb 10, 2025
f548f3f
conditional typing
Feb 10, 2025
b5d18f5
mfa updates and seed command test
Feb 13, 2025
47123a9
adjustments and tests added
Feb 17, 2025
c55f39e
update to user group type
Feb 17, 2025
e9991c4
merge main into branch
Feb 17, 2025
51f47c7
changeset
Feb 24, 2025
8ce4880
update api
Feb 24, 2025
b34532e
unit tests
Feb 27, 2025
c395733
adjust MFA type
Feb 27, 2025
8b3dc92
Merge branch 'main' into seed-feat
Feb 27, 2025
c447235
remove edit to construct container
Feb 27, 2025
892628b
removed unnecessary changeset
Feb 27, 2025
5485bd6
fixed some tests
Feb 27, 2025
7c2287c
fix dependencies tests
Feb 27, 2025
7828ae0
adjust seed command test
Feb 27, 2025
f98fee9
fix dep inconsistencies
Feb 27, 2025
ef27369
updated challenge response to use secretValue instead of input
Feb 27, 2025
8212b46
PR feedback
Feb 28, 2025
9ace2f2
more PR feedback
Feb 28, 2025
7f9915d
Merge branch 'main' into seed-feat
Feb 28, 2025
5852d7b
even more PR feedback
Feb 28, 2025
a189d11
testing something
Mar 1, 2025
446018b
reverted
Mar 1, 2025
0b5910c
e2e tests
Mar 3, 2025
7a7b2f9
e2e-tests
Mar 3, 2025
c7defa8
revert some package-lock changes
Mar 3, 2025
587586d
e2e tests
Mar 5, 2025
d89dd6c
removed user check from seed test
Mar 5, 2025
5949f2e
remove describeUserpoolCommand
Mar 5, 2025
c9755a7
update test
Mar 5, 2025
725ef86
sts package-lock update
Mar 5, 2025
9505ba1
e2e tests
Mar 5, 2025
909813c
fix package versions
Mar 5, 2025
32a77d9
fixing permissions
Mar 5, 2025
682b22a
import crypto
Mar 5, 2025
72bc973
appeasing json
Mar 5, 2025
9f8c5ab
fixing json
Mar 5, 2025
c05e494
cleaned up e2e file
Mar 5, 2025
b1dcca9
crpyto polyfill in e2e test seed script
Mar 5, 2025
10d4717
added context
Mar 12, 2025
2dd52a5
Merge branch 'main' into seed-feat
Mar 12, 2025
20cee9f
fix dependency issues
Mar 12, 2025
a0682e0
added spinner and success message to seed command
Mar 17, 2025
241d6e6
Merge branch 'main' into seed-feat
Mar 17, 2025
0f2ae13
updated success message
Mar 18, 2025
9949198
lint fixes from prettier update
Mar 18, 2025
bfea93a
updated some tests
Mar 19, 2025
e6de640
Merge branch 'main' into seed-feat
ShadowCat567 Mar 20, 2025
da3d96b
adjustments
ShadowCat567 Mar 20, 2025
47836d8
fixes after prettier sandbox merge
Mar 20, 2025
99d4ff2
Merge branch 'main' into seed-feat
Mar 20, 2025
3cf5bec
small api adjustment
Mar 20, 2025
31d458e
Merge branch 'main' into seed-feat
Mar 21, 2025
8488ca1
seed version bump changed to major
Mar 21, 2025
a40f891
reverted some package changes
Mar 21, 2025
5941f88
PR feedback
Mar 21, 2025
cfe281d
Merge branch 'main' into seed-feat
Mar 21, 2025
83a15e0
make codeQL happy
Mar 21, 2025
a1ee08d
Potential fix for code scanning alert no. 327: Insecure randomness
ShadowCat567 Mar 21, 2025
8ef43dc
Merge branch 'seed-feat' of github.com:aws-amplify/amplify-backend in…
Mar 21, 2025
252b150
e2e tests did not like readFile, using import
Mar 22, 2025
55a5253
fixed seed e2e tests
Mar 24, 2025
07adffb
Merge branch 'main' into seed-feat
Mar 24, 2025
dbde683
removed outputs import
Mar 24, 2025
a81efe3
Merge branch 'main' into seed-feat
Mar 24, 2025
b7decfa
Update packages/cli/src/commands/sandbox/sandbox-seed/sandbox_seed_co…
ShadowCat567 Mar 24, 2025
15b5277
fixed tests
Mar 25, 2025
2bf0970
Merge branch 'main' into seed-feat
Mar 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/little-kids-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@aws-amplify/seed': minor
'@aws-amplify/backend-cli': minor
---

adding seed feature
1,991 changes: 1,464 additions & 527 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@
"@aws-amplify/schema-generator": "^1.2.7",
"@aws-sdk/client-amplify": "^3.624.0",
"@aws-sdk/client-cloudformation": "^3.624.0",
"@aws-sdk/client-cognito-identity-provider": "^3.741.0",
"@aws-sdk/client-s3": "^3.624.0",
"@aws-sdk/credential-provider-ini": "^3.624.0",
"@aws-sdk/credential-providers": "^3.624.0",
"@aws-sdk/region-config-resolver": "^3.614.0",
"@smithy/node-config-provider": "^2.1.3",
"@smithy/shared-ini-file-loader": "^2.2.5",
"aws-cdk-lib": "^2.177.0",
"envinfo": "^7.11.0",
"execa": "^9.5.1",
"is-ci": "^3.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { after, before, describe, it, mock } from 'node:test';
import fsp from 'fs/promises';
import fs from 'fs';
import * as path from 'path';
import { SandboxSeedCommand } from './sandbox_seed_command.js';
import yargs, { CommandModule } from 'yargs';
import {
TestCommandError,
TestCommandRunner,
} from '../../../test-utils/command_runner.js';
import { createSandboxSecretCommand } from '../sandbox-secret/sandbox_secret_command_factory.js';
import { EventHandler, SandboxCommand } from '../sandbox_command.js';
import { SandboxSingletonFactory } from '@aws-amplify/sandbox';
import { SandboxDeleteCommand } from '../sandbox-delete/sandbox_delete_command.js';
import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js';
import { format, printer } from '@aws-amplify/cli-core';
import { CommandMiddleware } from '../../../command_middleware.js';
import { SandboxSeedGeneratePolicyCommand } from './sandbox_seed_policy_command.js';
import assert from 'node:assert';
import { BackendIdentifier } from '@aws-amplify/plugin-types';
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js';

const seedFileContents = 'console.log(`seed has been run`);';

const testBackendNameSpace = 'testSandboxId';
const testSandboxName = 'testSandboxName';

const testBackendId: BackendIdentifier = {
namespace: testBackendNameSpace,
name: testSandboxName,
type: 'sandbox',
};

//const testBackendIdStr = 'testBackendId';

void describe('sandbox seed command', () => {
let commandRunner: TestCommandRunner;

const clientConfigGenerationMock = mock.fn<EventHandler>();
const clientConfigDeletionMock = mock.fn<EventHandler>();

const clientConfigGeneratorAdapterMock = {
generateClientConfigToFile: clientConfigGenerationMock,
} as unknown as ClientConfigGeneratorAdapter;

const commandMiddleware = new CommandMiddleware(printer);
let amplifySeedDir: string;
let fullPath: string;

const sandboxIdResolver: SandboxBackendIdResolver = {
resolve: () => Promise.resolve(testBackendId),
} as SandboxBackendIdResolver;

before(async () => {
const sandboxFactory = new SandboxSingletonFactory(
() => Promise.resolve(testBackendId),
printer,
format
);

const sandboxSeedCommand = new SandboxSeedCommand(sandboxIdResolver, [
new SandboxSeedGeneratePolicyCommand(sandboxIdResolver),
]);

const sandboxCommand = new SandboxCommand(
sandboxFactory,
[
new SandboxDeleteCommand(sandboxFactory),
createSandboxSecretCommand(),
sandboxSeedCommand,
],
clientConfigGeneratorAdapterMock,
commandMiddleware,
() => ({
successfulDeployment: [clientConfigGenerationMock],
successfulDeletion: [clientConfigDeletionMock],
failedDeployment: [],
})
);
const parser = yargs().command(sandboxCommand as unknown as CommandModule);
commandRunner = new TestCommandRunner(parser);
});

void describe('seed script exists', () => {
before(async () => {
await fsp.mkdir(path.join(process.cwd(), 'amplify', 'seed'), {
recursive: true,
});
amplifySeedDir = path.join(process.cwd(), 'amplify');
fullPath = path.join(process.cwd(), 'amplify', 'seed', 'seed.ts');
await fsp.writeFile(fullPath, seedFileContents, 'utf8');
});

after(async () => {
await fsp.rm(amplifySeedDir, { recursive: true, force: true });
if (process.env.AMPLIFY_SANDBOX_IDENTIFIER) {
delete process.env.AMPLIFY_SANDBOX_IDENTIFIER;
}
});

void it('runs seed if seed script is found', async () => {
const fsOpenSyncMock = mock.method(fs, 'openSync');
await commandRunner.runCommand('sandbox seed');

assert.equal(fsOpenSyncMock.mock.callCount(), 1);
mock.restoreAll();
});

// TO DO: should have a test to see if backendID is set, that will need to happen while seed command is running
});

void describe('seed script does not exist', () => {
before(async () => {
await fsp.mkdir(path.join(process.cwd(), 'amplify', 'seed'), {
recursive: true,
});
amplifySeedDir = path.join(process.cwd(), 'amplify');
});

after(async () => {
await fsp.rm(amplifySeedDir, { recursive: true, force: true });
if (process.env.AMPLIFY_SANDBOX_IDENTIFIER) {
delete process.env.AMPLIFY_SANDBOX_IDENTIFIER;
}
});

void it('throws error if seed script does not exist', async () => {
await assert.rejects(
() => commandRunner.runCommand('sandbox seed'),
(err: TestCommandError) => {
// file differences between Unix and Windows makes it tricky to add the path
assert.match(err.output, /SeedScriptNotFoundError/);
assert.match(err.output, /There is no file that corresponds to/);
assert.match(
err.output,
/Please make a file that corresponds to (.*) and put your seed logic in it/
);
return true;
}
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Argv, CommandModule } from 'yargs';
import path from 'path';
import { existsSync } from 'fs';
import { execa } from 'execa';
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js';
import { AmplifyUserError } from '@aws-amplify/platform-core';
import { SandboxCommandGlobalOptions } from '../option_types.js';

/**
* Command that runs seed in sandbox environment
*/
export class SandboxSeedCommand implements CommandModule<object> {
/**
* @inheritDoc
*/
readonly command: string;

/**
* @inheritDoc
*/
readonly describe: string;

/**
* Seeds sandbox environment.
*/
constructor(
private readonly backendIDResolver: SandboxBackendIdResolver,
private readonly seedSubCommands: CommandModule[]
) {
this.command = 'seed';
this.describe = 'Seeds sandbox environment';
}

/**
* @inheritDoc
*/
handler = async (): Promise<void> => {
const backendID = await this.backendIDResolver.resolve();
const seedPath = path.join('amplify', 'seed', 'seed.ts');
await execa('tsx', [seedPath], {
cwd: process.cwd(),
stdio: 'inherit',
env: {
AMPLIFY_SANDBOX_IDENTIFIER: JSON.stringify(backendID),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call it AMPLIFY_BACKEND_IDENTIFIER . This will future proof it in case we ever implement branch support in some form.

},
});
};

/**
* @inheritDoc
*/
builder = (yargs: Argv): Argv<SandboxCommandGlobalOptions> => {
return yargs.command(this.seedSubCommands).check(() => {
const seedPath = path.join(process.cwd(), 'amplify', 'seed', 'seed.ts');
if (!existsSync(seedPath)) {
// this only gets sent to outputs
throw new AmplifyUserError('SeedScriptNotFoundError', {
message: `There is no file that corresponds to ${seedPath}`,
resolution: `Please make a file that corresponds to ${seedPath} and put your seed logic in it`,
});
}
return true;
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Argv, CommandModule } from 'yargs';
import { printer } from '@aws-amplify/cli-core';
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js';
import { generateSeedPolicyTemplate } from '../../../seed-policy-generation/generate_seed_policy_template.js';

/**
* Command that generates policy template with permissions to be able to run seed in sandbox environment
*/
export class SandboxSeedGeneratePolicyCommand implements CommandModule<object> {
/**
* @inheritDoc
*/
readonly command: string;

/**
* @inheritDoc
*/
readonly describe: string;

/**
* Generates policy to run seed, is a subcommand of seed
*/
constructor(private readonly backendIdResolver: SandboxBackendIdResolver) {
this.command = 'generate-policy';
this.describe = 'Generates policy for seeding based on outputs';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.describe = 'Generates policy for seeding based on outputs';
this.describe = 'Generates policy for seeding';

That is implementation details, we can avoid disclosing it.

}

/**
* @inheritDoc
*/
handler = async (): Promise<void> => {
const backendId = await this.backendIdResolver.resolve();
const policyDocument = await generateSeedPolicyTemplate(backendId);
printer.print(JSON.stringify(policyDocument.toJSON(), null, 1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we use 2 space indentation

Suggested change
printer.print(JSON.stringify(policyDocument.toJSON(), null, 1));
printer.print(JSON.stringify(policyDocument.toJSON(), null, 2));

};

/**
* @inheritDoc
*/
builder = (yargs: Argv) => {
return yargs;
};
}
10 changes: 9 additions & 1 deletion packages/cli/src/commands/sandbox/sandbox_command_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from './sandbox_command.js';
import { SandboxSingletonFactory } from '@aws-amplify/sandbox';
import { SandboxDeleteCommand } from './sandbox-delete/sandbox_delete_command.js';
import { SandboxSeedCommand } from './sandbox-seed/sandbox_seed_command.js';
import { SandboxBackendIdResolver } from './sandbox_id_resolver.js';
import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_generator_adapter.js';
import { LocalNamespaceResolver } from '../../backend-identifier/local_namespace_resolver.js';
Expand All @@ -24,6 +25,7 @@ import {
import { S3Client } from '@aws-sdk/client-s3';
import { AmplifyClient } from '@aws-sdk/client-amplify';
import { CloudFormationClient } from '@aws-sdk/client-cloudformation';
import { SandboxSeedGeneratePolicyCommand } from './sandbox-seed/sandbox_seed_policy_command.js';

/**
* Creates wired sandbox command.
Expand Down Expand Up @@ -75,7 +77,13 @@ export const createSandboxCommand = (): CommandModule<
const commandMiddleWare = new CommandMiddleware(printer);
return new SandboxCommand(
sandboxFactory,
[new SandboxDeleteCommand(sandboxFactory), createSandboxSecretCommand()],
[
new SandboxDeleteCommand(sandboxFactory),
createSandboxSecretCommand(),
new SandboxSeedCommand(sandboxBackendIdPartsResolver, [
new SandboxSeedGeneratePolicyCommand(sandboxBackendIdPartsResolver),
]),
],
clientConfigGeneratorAdapter,
commandMiddleWare,
eventHandlerFactory.getSandboxEventHandlers
Expand Down
Loading
Loading