Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: diagnose #10383

Merged
merged 25 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
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
4 changes: 4 additions & 0 deletions .eslint-dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
"codebase",
"codegen",
"codepipeline",
"columnify",
"cognito",
"cors",
"createCipheriv",
"creds",
"datasource",
"decrypt",
Expand Down Expand Up @@ -83,13 +85,15 @@
"nspawn",
"nullability",
"nullable",
"oaepHash",
"oauth",
"oidc",
"openid",
"opensearch",
"orgs",
"parens",
"pathname",
"pbkdf2Sync",
"pipelined",
"positionally",
"posix",
Expand Down
2 changes: 2 additions & 0 deletions packages/amplify-cli-core/src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export class CustomPoliciesFormatError extends Error {}
export class ExportPathValidationError extends Error {}
export class ExportedStackNotFoundError extends Error {}
export class ExportedStackNotInValidStateError extends Error {}
export class DebugConfigValueNotSetError extends Error {}
export class DiagnoseReportUploadError extends Error {}

export class NotInitializedError extends Error {
public constructor() {
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-cli-logger/src/Redactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function Redactor(arg: string | undefined): string {

return arg;
}
function stringMasker(s: string): string {
export function stringMasker(s: string): string {
sachscode marked this conversation as resolved.
Show resolved Hide resolved
if (!s.includes('-') && !s.includes('/')) return redactPart(s);

// if string only includes '/' char
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-cli-logger/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AmplifyLogger } from './AmplifyLogger';
import { IAmplifyLogger } from './IAmplifyLogger';
import { constants } from './constants';
export { Redactor } from './Redactor';
export { Redactor, stringMasker } from './Redactor';
export const logger: IAmplifyLogger = new AmplifyLogger();
export const LocalLogDirectory = constants.LOG_DIRECTORY;
4 changes: 3 additions & 1 deletion packages/amplify-cli/amplify-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"configure",
"console",
"delete",
"diagnose",
"env",
"export",
"help",
Expand All @@ -21,7 +22,8 @@
"uninstall",
"upgrade",
"version",
"build"
"build",
"report"
],
"commandAliases": {
"h": "help",
Expand Down
5 changes: 5 additions & 0 deletions packages/amplify-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"env-editor": "^0.5.0",
"execa": "^5.1.1",
"folder-hash": "^4.0.2",
"form-data": "^4.0.0",
sachscode marked this conversation as resolved.
Show resolved Hide resolved
"fs-extra": "^8.1.0",
"glob": "^7.2.0",
"global-prefix": "^3.0.0",
Expand All @@ -98,11 +99,14 @@
"promise-sequential": "^1.1.1",
"semver": "^7.3.5",
"tar-fs": "^2.1.1",
"treeify": "^1.1.0",
"update-notifier": "^5.1.0",
"uuid": "^8.3.2",
"which": "^2.0.2"
},
"devDependencies": {
"@types/archiver": "^5.3.1",
"@types/columnify": "^1.5.1",
"@types/ci-info": "^2.0.0",
"@types/folder-hash": "^4.0.1",
"@types/fs-extra": "^8.0.1",
Expand All @@ -114,6 +118,7 @@
"@types/progress": "^2.0.3",
"@types/promise-sequential": "^1.1.0",
"@types/tar-fs": "^2.0.0",
"@types/treeify": "^1.0.0",
"@types/update-notifier": "^5.1.0",
"amplify-function-plugin-interface": "1.9.5",
"cloudform-types": "^4.2.0",
Expand Down
170 changes: 170 additions & 0 deletions packages/amplify-cli/src/__tests__/commands/diagnose.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import * as fs from 'fs-extra';
import archiver from 'archiver';
import { pathManager, stateManager } from 'amplify-cli-core';
import { Redactor } from 'amplify-cli-logger';
import { WriteStream } from 'fs-extra';
import fetch from 'node-fetch';
import * as uuid from 'uuid';
import { collectFiles } from '../../commands/helpers/collect-files';
import { run } from '../../commands/diagnose';
import { Context } from '../../domain/context';

jest.mock('uuid');
jest.mock('amplify-cli-core');
jest.mock('../../commands/helpers/collect-files');
jest.mock('../../commands/helpers/encryption-helpers');
jest.mock('archiver');
jest.mock('fs-extra');
jest.mock('amplify-cli-logger', () => ({
Redactor: jest.fn(),
stringMasker: jest.fn(),
}));

jest.mock('path');
jest.mock('crypto', () => ({
publicEncrypt: jest.fn().mockReturnValue(Buffer.from([])),
createHash: jest.fn().mockReturnValue({
update: jest.fn().mockReturnValue({
digest: jest.fn().mockReturnValue('projectId'),
}),
}),
randomBytes: jest.fn().mockReturnValue(Buffer.from('RandomBytes')),
/* eslint-disable spellcheck/spell-checker*/
pbkdf2Sync: jest.fn(),
createCipheriv: jest.fn(),
/* eslint-enable spellcheck/spell-checker*/
}));
jest.mock('node-fetch', () => jest.fn().mockReturnValue({ status: 200 }));

const mockMeta = {
providers: {
awscloudformation: {
// eslint-disable-next-line spellcheck/spell-checker
AmplifyAppId: 'd2ew5jdgc57sa7',
},
},
hosting: {},
auth: {
testAuth: {
service: 'Cognito',
},
},
storage: {
testBucket: {
service: 'S3',
},
},
api: {
myApi: {
service: 'AppSync',
},
},
};
const collectedFiles : { filePath: string, redact: boolean }[] = [
{
filePath: 'file.ts',
redact: false,
},
{
filePath: 'file.json',
redact: true,
},
];

describe('run report command', () => {
it('runs report command for only a resource', async () => {
const contextMock = {
usageData: {
getUsageDataPayload: jest.fn().mockReturnValue({
sessionUuid: 'sessionId',
installationUuid: '',

}),
},
exeInfo: {
/* eslint-disable spellcheck/spell-checker */
cloudFormationEvents: [
{
StackId: 'arn:aws:cloudformation:us-east-1:1234567891009:stack/amplify-pushfail-dev-230444/d7470930-8ac5-11ec-a30c-0a84db46e9eb',
EventId: 'd006c2e0-c0f4-11ec-841d-0e43d8dbed1f',
StackName: 'amplify-pushfail-dev-230444',
LogicalResourceId: 'amplify-pushfail-dev-230444',
PhysicalResourceId: 'arn:aws:cloudformation:us-east-1:1234567891009:stack/amplify-pushfail-dev-230444/d7470930-8ac5-11ec-a30c-0a84db46e9eb',
ResourceType: 'AWS::CloudFormation::Stack',
Timestamp: '2022-04-20T21:57:03.599Z',
ResourceStatus: 'UPDATE_IN_PROGRESS',
ResourceStatusReason: 'User Initiated',
},
{
StackId: 'arn:aws:cloudformation:us-east-1:1234567891009:stack/amplify-pushfail-dev-230444/d7470930-8ac5-11ec-a30c-0a84db46e9eb',
EventId: 'apipushfail-CREATE_IN_PROGRESS-2022-04-20T21:57:09.528Z',
StackName: 'amplify-pushfail-dev-230444',
LogicalResourceId: 'apipushfail',
PhysicalResourceId: '',
ResourceType: 'AWS::CloudFormation::Stack',
Timestamp: '2022-04-20T21:57:09.528Z',
ResourceStatus: 'CREATE_IN_PROGRESS',
},
{
StackId: 'arn:aws:cloudformation:us-east-1:1234567891009:stack/amplify-pushfail-dev-230444/d7470930-8ac5-11ec-a30c-0a84db46e9eb',
EventId: 'UpdateRolesWithIDPFunctionRole-CREATE_IN_PROGRESS-2022-04-20T21:57:09.540Z',
StackName: 'amplify-pushfail-dev-230444',
LogicalResourceId: 'UpdateRolesWithIDPFunctionRole',
PhysicalResourceId: '',
ResourceType: 'AWS::IAM::Role',
Timestamp: '2022-04-20T21:57:09.540Z',
ResourceStatus: 'CREATE_IN_PROGRESS',
},
],
/* eslint-enable spellcheck/spell-checker */

},
input: {
options: {
'send-report': true,
},
},
};
const mockRootPath = 'user/source/myProject';
const pathManagerMock = pathManager as jest.Mocked<typeof pathManager>;
pathManagerMock.findProjectRoot = jest.fn().mockReturnValue(mockRootPath);

const stateManagerMock = stateManager as jest.Mocked<typeof stateManager>;
stateManagerMock.getBackendConfig = jest.fn().mockReturnValue(mockMeta);
stateManagerMock.getProjectConfig = jest.fn().mockReturnValue({ projectName: 'myProject' });

const collectFilesMock = collectFiles as jest.MockedFunction<typeof collectFiles>;

collectFilesMock.mockReturnValue(collectedFiles);

const mockArchiver = archiver as jest.Mocked<typeof archiver>;
const zipperMock = {
append: jest.fn(),
pipe: jest.fn(),
finalize: jest.fn(),
};
mockArchiver.create = jest.fn().mockReturnValue(zipperMock);

const fsMock = fs as jest.Mocked<typeof fs>;
fsMock.createWriteStream.mockReturnValue({
on: jest.fn().mockImplementation((event, resolveFunction) => {
if (event === 'close') {
resolveFunction();
}
}),
error: jest.fn(),
} as unknown as WriteStream);

const uuidMock = uuid as jest.Mocked<typeof uuid>;
uuidMock.v4.mockReturnValue('randomPassPhrase');

const contextMockTyped = contextMock as unknown as Context;
await run(contextMockTyped, new Error('mock error'));
expect(fsMock.readFileSync).toBeCalled();
expect(Redactor).toBeCalledTimes(1);
expect(zipperMock.pipe).toBeCalled();
expect(zipperMock.finalize).toBeCalled();
expect(fetch).toBeCalled();
expect(zipperMock.append).toBeCalledTimes(3);
});
});
38 changes: 19 additions & 19 deletions packages/amplify-cli/src/__tests__/context-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ import { constructContext, attachUsageData } from '../context-manager';
import { Context } from '../domain/context';
import { PluginInfo } from '../domain/plugin-info';
import { PluginManifest } from '../domain/plugin-manifest';
import { UsageData, NoUsageData} from '../domain/amplify-usageData';
import { UsageData, NoUsageData } from '../domain/amplify-usageData';

jest.mock('../domain/amplify-usageData/', () => ({
UsageData: {
Instance: {
setIsHeadless: jest.fn(),
init : jest.fn(),
init: jest.fn(),
},
},
NoUsageData: {
Instance: {
setIsHeadless: jest.fn(),
init : jest.fn(),
Instance: {
setIsHeadless: jest.fn(),
init: jest.fn(),
},
},
CLINoFlowReport: {
instance: jest.fn(() => ({
setIsHeadless: jest.fn(),
}))
})),
},
}));
jest.mock('../app-config');
Expand All @@ -42,18 +42,19 @@ describe('test attachUsageData', () => {
mockContext.pluginPlatform = new PluginPlatform();
mockContext.pluginPlatform.plugins.core = [new PluginInfo('', version, '', new PluginManifest('', ''))];
mockContext.usageData = {
init : jest.fn(),
setIsHeadless : jest.fn(),
emitError : jest.fn(),
emitAbort : jest.fn(),
emitSuccess : jest.fn(),
startCodePathTimer : jest.fn(),
stopCodePathTimer : jest.fn(),
pushHeadlessFlow : jest.fn(),
pushInteractiveFlow : jest.fn(),
getFlowReport : jest.fn(),
assignProjectIdentifier : jest.fn(),
}
init: jest.fn(),
setIsHeadless: jest.fn(),
emitError: jest.fn(),
emitAbort: jest.fn(),
emitSuccess: jest.fn(),
startCodePathTimer: jest.fn(),
stopCodePathTimer: jest.fn(),
pushHeadlessFlow: jest.fn(),
pushInteractiveFlow: jest.fn(),
getFlowReport: jest.fn(),
assignProjectIdentifier: jest.fn(),
getUsageDataPayload: jest.fn(),
};

const stateManagerMocked = stateManager as jest.Mocked<typeof stateManager>;
stateManagerMocked.metaFileExists.mockReturnValue(true);
Expand Down Expand Up @@ -116,6 +117,5 @@ describe('test attachUsageData', () => {
{},
ts,
);

});
});
3 changes: 2 additions & 1 deletion packages/amplify-cli/src/__tests__/test-aborting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ describe('test SIGINT with execute', () => {
pushInteractiveFlow: jest.fn(),
getFlowReport: jest.fn(),
assignProjectIdentifier: jest.fn(),
getUsageDataPayload: jest.fn(),

};
mockContext.projectHasMobileHubResources = false;

mockContext.amplify = jest.createMockFromModule('../domain/amplify-toolkit');
Object.defineProperty(mockContext.amplify, 'getEnvInfo', { value: jest.fn() });
jest.setMock('../context-manager', {
Expand Down
34 changes: 34 additions & 0 deletions packages/amplify-cli/src/__tests__/utils/encrypt-buffer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { encryptBuffer, encryptKey } from '../../commands/helpers/encryption-helpers'
import { v4 } from 'uuid';
import crypto from 'crypto'
describe("encryption helper", () => {
it("tests encryption helper", async() =>{
const originalKey = v4();
const plainText = crypto.randomBytes(10);
const encryptedText = await encryptBuffer(plainText, originalKey);
const bData = Buffer.from(encryptedText, 'base64');

// convert data to buffers
const salt = bData.slice(0, 64);
const iv = bData.slice(64, 80);
const tag = bData.slice(80, 96);
const text = bData.slice(96);

const masterkey = Buffer.from(originalKey, 'utf-8');

// derive key using; 32 byte key length
const key = crypto.pbkdf2Sync(masterkey, salt , 2145, 32, 'sha512');

// AES 256 GCM Mode
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);

// encrypt the given text
const decrypted = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8');
expect(decrypted).toEqual(plainText.toString('utf8'));
});

it("test encryption key", async () => {
expect(await encryptKey(v4())).toBeTruthy();
})
})
Loading