Skip to content

Commit 8800cba

Browse files
authored
Merge pull request #34 from Bit-Quill/achild/fixCdk
Fix CDK deployment
2 parents 909ed51 + 5f41c81 commit 8800cba

File tree

9 files changed

+178
-138
lines changed

9 files changed

+178
-138
lines changed

src/CDKPipelineApp.js

+4
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ async function createAWSpipelineCDK({
167167
CDKFile = CDKFile.replace( "const NEPTUNE_DB_NAME = '';", `const NEPTUNE_DB_NAME = '${NEPTUNE_DB_NAME}';` );
168168
CDKFile = CDKFile.replace( "const NEPTUNE_TYPE = '';", `const NEPTUNE_TYPE = '${NEPTUNE_TYPE}';` );
169169
CDKFile = CDKFile.replace( "const NEPTUNE_DBSubnetGroup = null;", `const NEPTUNE_DBSubnetGroup = '${NEPTUNE_DBSubnetGroup}';` );
170+
if (neptuneClusterInfo) {
171+
CDKFile = CDKFile.replace("const NEPTUNE_DBSubnetIds = null;", `const NEPTUNE_DBSubnetIds = '${neptuneClusterInfo.dbSubnetIds}';`);
172+
CDKFile = CDKFile.replace("const NEPTUNE_VpcSecurityGroupId = null;",`const NEPTUNE_VpcSecurityGroupId = '${neptuneClusterInfo.vpcSecurityGroupId}';`);
173+
}
170174
CDKFile = CDKFile.replace( "const NEPTUNE_IAM_AUTH = false;", `const NEPTUNE_IAM_AUTH = ${isNeptuneIAMAuth};` );
171175
CDKFile = CDKFile.replace( "const NEPTUNE_IAM_POLICY_RESOURCE = '*';", `const NEPTUNE_IAM_POLICY_RESOURCE = '${NEPTUNE_IAM_POLICY_RESOURCE}';` );
172176

src/NeptuneSchema.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { aws4Interceptor } from "aws4-axios";
1515
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
1616
import { NeptunedataClient, ExecuteOpenCypherQueryCommand } from "@aws-sdk/client-neptunedata";
1717
import { loggerDebug, loggerError, loggerInfo, yellow } from "./logger.js";
18-
import { parseNeptuneDomainFromHost, parseNeptuneGraphName } from "./util.js";
1918
import { ExecuteQueryCommand, GetGraphSummaryCommand, NeptuneGraphClient } from "@aws-sdk/client-neptune-graph";
2019

2120
const NEPTUNE_DB = 'neptune-db';
@@ -25,7 +24,8 @@ const HTTP_LANGUAGE = 'openCypher';
2524
const NEPTUNE_GRAPH_LANGUAGE = 'OPEN_CYPHER';
2625
let HOST = '';
2726
let PORT = 8182;
28-
let REGION = ''
27+
let REGION = '';
28+
let DOMAIN = '';
2929
let SAMPLE = 5000;
3030
let NEPTUNE_TYPE = NEPTUNE_DB;
3131
let NAME = '';
@@ -331,12 +331,13 @@ async function getEdgesDirectionsCardinality() {
331331
}
332332

333333

334-
function setGetNeptuneSchemaParameters(host, port, region, neptuneType) {
335-
HOST = host;
336-
PORT = port;
337-
REGION = region;
338-
NEPTUNE_TYPE = neptuneType;
339-
NAME = parseNeptuneGraphName(host);
334+
function setGetNeptuneSchemaParameters(neptuneInfo) {
335+
HOST = neptuneInfo.host;
336+
PORT = neptuneInfo.port;
337+
REGION = neptuneInfo.region;
338+
NEPTUNE_TYPE = neptuneInfo.neptuneType;
339+
NAME = neptuneInfo.graphName;
340+
DOMAIN = neptuneInfo.domain;
340341
}
341342

342343
function getNeptunedataClient() {
@@ -354,7 +355,7 @@ function getNeptuneGraphClient() {
354355
loggerInfo('Instantiating NeptuneGraphClient')
355356
neptuneGraphClient = new NeptuneGraphClient({
356357
port: PORT,
357-
host: parseNeptuneDomainFromHost(HOST),
358+
host: DOMAIN,
358359
region: REGION,
359360
protocol: NEPTUNE_GRAPH_PROTOCOL,
360361
});

src/main.js

+53-54
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ let spinner = null;
2929
// find global installation dir
3030
import path from 'path';
3131
import { fileURLToPath } from 'url';
32-
import { parseNeptuneDomainFromEndpoint } from "./util.js";
32+
import { parseNeptuneEndpoint } from "./util.js";
3333
const __filename = fileURLToPath(import.meta.url);
3434
const __dirname = path.dirname(__filename);
3535

@@ -63,7 +63,7 @@ let createUpdatePipelineNeptuneDatabaseName = '';
6363
let removePipelineName = '';
6464
let inputCDKpipeline = false;
6565
let inputCDKpipelineName = '';
66-
let inputCDKpipelineEnpoint = '';
66+
let inputCDKpipelineEndpoint = '';
6767
let inputCDKpipelineFile = '';
6868
let inputCDKpipelineRegion = '';
6969
let inputCDKpipelineDatabaseName = '';
@@ -212,10 +212,14 @@ function processArgs() {
212212
inputCDKpipeline = true;
213213
break;
214214
case '-ce':
215+
// support miss-spelled option for backwards compatibility - could be removed for next major release
216+
case '--output-aws-pipeline-cdk-neptume-endpoint':
215217
case '--output-aws-pipeline-cdk-neptune-endpoint':
216-
inputCDKpipelineEnpoint = array[index + 1];
218+
inputCDKpipelineEndpoint = array[index + 1];
217219
break;
218220
case '-cd':
221+
// support miss-spelled option for backwards compatibility - could be removed for next major release
222+
case '--output-aws-pipeline-cdk-neptume-database-name':
219223
case '--output-aws-pipeline-cdk-neptune-database-name':
220224
inputCDKpipelineDatabaseName = array[index + 1];
221225
break;
@@ -285,37 +289,31 @@ async function main() {
285289
}
286290
}
287291

292+
293+
let neptuneInfo;
288294
// Check if any of the Neptune endpoints are a neptune analytic endpoint and if so, set the neptuneType and IAM to required
289-
const nonEmptyEndpoints = [inputGraphDBSchemaNeptuneEndpoint, createUpdatePipelineEndpoint, inputCDKpipelineEnpoint].filter(endpoint => endpoint !== '');
290-
const isNeptuneAnalyticsGraph = nonEmptyEndpoints.length > 0 && parseNeptuneDomainFromEndpoint(nonEmptyEndpoints[0]).includes(NEPTUNE_GRAPH);
291-
if (isNeptuneAnalyticsGraph) {
292-
neptuneType = NEPTUNE_GRAPH;
293-
// neptune analytics requires IAM
294-
loggerInfo("Detected neptune-graph from input endpoint - setting IAM auth to true as it is required for neptune analytics")
295-
isNeptuneIAMAuth = true;
295+
// only one of these endpoints are expected to be non-empty at the same time
296+
const nonEmptyEndpoints = [inputGraphDBSchemaNeptuneEndpoint, createUpdatePipelineEndpoint, inputCDKpipelineEndpoint].filter(endpoint => endpoint !== '');
297+
if (nonEmptyEndpoints.length > 0) {
298+
neptuneInfo = parseNeptuneEndpoint(nonEmptyEndpoints[0]);
299+
neptuneType = neptuneInfo.neptuneType;
300+
if (neptuneType === NEPTUNE_GRAPH) {
301+
// neptune analytics requires IAM
302+
loggerInfo("Detected neptune-graph from input endpoint - setting IAM auth to true as it is required for neptune analytics")
303+
isNeptuneIAMAuth = true;
304+
}
296305
}
297306

298307
// Get Neptune schema from endpoint
299308
if (inputGraphDBSchemaNeptuneEndpoint != '' && inputGraphDBSchema == '' && inputGraphDBSchemaFile == '') {
300-
let endpointParts = inputGraphDBSchemaNeptuneEndpoint.split(':');
301-
if (endpointParts.length != 2) {
302-
loggerError('Neptune endpoint must be in the form of host:port');
303-
process.exit(1);
309+
if (!neptuneInfo) {
310+
neptuneInfo = parseNeptuneEndpoint(inputGraphDBSchemaNeptuneEndpoint);
304311
}
305-
let neptuneHost = endpointParts[0];
306-
let neptunePort = endpointParts[1];
307-
308-
let neptuneRegionParts = inputGraphDBSchemaNeptuneEndpoint.split('.');
309-
let neptuneRegion = '';
310-
if (neptuneType === NEPTUNE_DB)
311-
neptuneRegion = neptuneRegionParts[2];
312-
else
313-
neptuneRegion = neptuneRegionParts[1];
314312

315313
loggerInfo('Retrieving Neptune schema');
316-
loggerDebug('Getting Neptune schema from endpoint: ' + yellow(neptuneHost + ':' + neptunePort), {toConsole: true});
314+
loggerDebug('Getting Neptune schema from endpoint: ' + yellow(inputGraphDBSchemaNeptuneEndpoint), {toConsole: true});
317315

318-
setGetNeptuneSchemaParameters(neptuneHost, neptunePort, neptuneRegion, neptuneType);
316+
setGetNeptuneSchemaParameters(neptuneInfo);
319317
let startTime = performance.now();
320318
inputGraphDBSchema = await getNeptuneSchema();
321319
let endTime = performance.now();
@@ -372,15 +370,11 @@ async function main() {
372370
process.exit(1);
373371
}
374372
if (createUpdatePipelineEndpoint != '') {
375-
let parts = createUpdatePipelineEndpoint.split('.');
376-
createUpdatePipelineNeptuneDatabaseName = parts[0];
377-
378-
let parsedRegion;
379-
if (neptuneType === NEPTUNE_DB) {
380-
parsedRegion = parts[2];
381-
} else {
382-
parsedRegion = parts[1];
373+
if (!neptuneInfo) {
374+
neptuneInfo = parseNeptuneEndpoint(createUpdatePipelineEndpoint);
383375
}
376+
createUpdatePipelineNeptuneDatabaseName = neptuneInfo.graphName;
377+
const parsedRegion = neptuneInfo.region;
384378

385379
if (createUpdatePipelineRegion !== parsedRegion) {
386380
if (createUpdatePipelineRegion !== '') {
@@ -399,27 +393,37 @@ async function main() {
399393
// CDK
400394
if (inputCDKpipeline) {
401395
if (!inputGraphDBSchemaNeptuneEndpoint == '') {
402-
inputCDKpipelineEnpoint = inputGraphDBSchemaNeptuneEndpoint;
396+
inputCDKpipelineEndpoint = inputGraphDBSchemaNeptuneEndpoint;
403397
}
404-
if (inputCDKpipelineEnpoint == '' &&
398+
if (inputCDKpipelineEndpoint == '' &&
405399
inputCDKpipelineRegion == '' && inputCDKpipelineDatabaseName == '') {
406400
loggerError('AWS CDK: is required a Neptune endpoint, or a Neptune database name and region.');
407401
process.exit(1);
408402
}
409-
if (inputCDKpipelineEnpoint == '' &&
403+
if (inputCDKpipelineEndpoint == '' &&
410404
!inputCDKpipelineRegion == '' && inputCDKpipelineDatabaseName == '') {
411405
loggerError('AWS CDK: a Neptune database name is required.');
412406
process.exit(1);
413407
}
414-
if (inputCDKpipelineEnpoint == '' &&
408+
if (inputCDKpipelineEndpoint == '' &&
415409
inputCDKpipelineRegion == '' && !inputCDKpipelineDatabaseName == '') {
416410
loggerError('AWS CDK: a Neptune database region is required.');
417411
process.exit(1);
418412
}
419-
if (inputCDKpipelineEnpoint != '') {
420-
let parts = inputCDKpipelineEnpoint.split('.');
421-
inputCDKpipelineDatabaseName = parts[0];
422-
inputCDKpipelineRegion = parts[2];
413+
if (inputCDKpipelineEndpoint != '') {
414+
if (!neptuneInfo) {
415+
neptuneInfo = parseNeptuneEndpoint(inputCDKpipelineEndpoint);
416+
}
417+
inputCDKpipelineDatabaseName = neptuneInfo.graphName;
418+
const parsedRegion = neptuneInfo.region;
419+
if (inputCDKpipelineRegion !== parsedRegion) {
420+
if (inputCDKpipelineRegion !== '') {
421+
loggerInfo('Switching CDK region from ' + inputCDKpipelineRegion + ' to region parsed from endpoint: ' + parsedRegion);
422+
} else {
423+
loggerInfo('Region parsed from CDK endpoint: ' + parsedRegion);
424+
}
425+
inputCDKpipelineRegion = parsedRegion;
426+
}
423427
}
424428
if (inputCDKpipelineName == '') {
425429
inputCDKpipelineName = inputCDKpipelineDatabaseName;
@@ -576,13 +580,11 @@ async function main() {
576580
// Create Update AWS Pipeline
577581
if (createUpdatePipeline) {
578582
try {
579-
let endpointParts = createUpdatePipelineEndpoint.split(':');
580-
if (endpointParts.length < 2) {
581-
loggerError('Neptune endpoint must be in the form of host:port');
582-
process.exit(1);
583+
if (!neptuneInfo) {
584+
neptuneInfo = parseNeptuneEndpoint(createUpdatePipelineEndpoint);
583585
}
584-
let neptuneHost = endpointParts[0];
585-
let neptunePort = endpointParts[1];
586+
let neptuneHost = neptuneInfo.host;
587+
let neptunePort = neptuneInfo.port;
586588

587589
await createUpdateAWSpipeline( createUpdatePipelineName,
588590
createUpdatePipelineNeptuneDatabaseName,
@@ -608,14 +610,11 @@ async function main() {
608610
try {
609611
loggerInfo('Creating CDK File', {toConsole: true});
610612

611-
let endpointParts = inputCDKpipelineEnpoint.split(':');
612-
if (endpointParts.length < 2) {
613-
loggerError('Neptune endpoint must be in the form of host:port');
614-
process.exit(1);
613+
if (!neptuneInfo) {
614+
neptuneInfo = parseNeptuneEndpoint(inputCDKpipelineEndpoint);
615615
}
616-
let neptuneHost = endpointParts[0];
617-
let neptunePort = endpointParts[1];
618-
616+
let neptuneHost = neptuneInfo.host;
617+
let neptunePort = neptuneInfo.port;
619618

620619
if (inputCDKpipelineFile == '') {
621620
inputCDKpipelineFile = `${outputFolderPath}/${inputCDKpipelineName}-cdk.js`;

src/pipelineResources.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -330,19 +330,6 @@ async function createLambdaRole() {
330330

331331

332332
if (NEPTUNE_IAM_AUTH) {
333-
334-
let action = [];
335-
if (NEPTUNE_TYPE === NEPTUNE_DB) {
336-
action = [
337-
"neptune-db:DeleteDataViaQuery",
338-
"neptune-db:connect",
339-
"neptune-db:ReadDataViaQuery",
340-
"neptune-db:WriteDataViaQuery"
341-
];
342-
} else {
343-
action = ["neptune-graph:*"]
344-
}
345-
346333
// Create Neptune query policy
347334
startSpinner('Creating policy for Neptune queries', true);
348335
let policyName = NAME+"NeptuneQueryPolicy";
@@ -352,7 +339,12 @@ async function createLambdaRole() {
352339
Statement: [
353340
{
354341
Effect: "Allow",
355-
Action: action,
342+
Action: [
343+
NEPTUNE_TYPE + ':connect',
344+
NEPTUNE_TYPE + ':DeleteDataViaQuery',
345+
NEPTUNE_TYPE + ':ReadDataViaQuery',
346+
NEPTUNE_TYPE + ':WriteDataViaQuery'
347+
],
356348
Resource: NEPTUNE_IAM_POLICY_RESOURCE
357349
},
358350
],

src/test/util.test.js

+29-20
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,46 @@
1-
import {parseNeptuneDomainFromEndpoint, parseNeptuneDomainFromHost, parseNeptuneGraphName} from '../util.js';
1+
import {parseNeptuneDomainFromHost, parseNeptuneEndpoint} from '../util.js';
22

33
test('parse domain from neptune cluster host', () => {
4-
expect(parseNeptuneDomainFromHost('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')).toBe('neptune.amazonaws.com');
4+
expect(parseNeptuneDomainFromHost('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com'))
5+
.toBe('neptune.amazonaws.com');
56
});
67

78
test('parse domain from neptune analytics host', () => {
8-
expect(parseNeptuneDomainFromHost('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toBe('neptune-graph.amazonaws.com');
9+
expect(parseNeptuneDomainFromHost('g-abcdef.us-west-2.neptune-graph.amazonaws.com'))
10+
.toBe('neptune-graph.amazonaws.com');
911
});
1012

1113
test('parse domain from host without enough parts throws error', () => {
12-
expect(() => parseNeptuneDomainFromHost('invalid.com')).toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5');
14+
expect(() => parseNeptuneDomainFromHost('invalid.com'))
15+
.toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5');
1316
});
1417

15-
test('parse domain from neptune cluster endpoint', () => {
16-
expect(parseNeptuneDomainFromEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com:8182')).toBe('neptune.amazonaws.com');
18+
test('parse neptune db endpoint', () => {
19+
let neptuneInfo = parseNeptuneEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com:8182');
20+
expect(neptuneInfo).toHaveProperty('port', '8182');
21+
expect(neptuneInfo).toHaveProperty('host', 'db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com');
22+
expect(neptuneInfo).toHaveProperty('domain', 'neptune.amazonaws.com');
23+
expect(neptuneInfo).toHaveProperty('region', 'us-west-2');
24+
expect(neptuneInfo).toHaveProperty('graphName', 'db-neptune-abc-def');
25+
expect(neptuneInfo).toHaveProperty('neptuneType', 'neptune-db');
1726
});
1827

19-
test('parse domain from neptune analytics endpoint', () => {
20-
expect(parseNeptuneDomainFromEndpoint('g-abcdef.us-west-2.neptune-graph.amazonaws.com:8182')).toBe('neptune-graph.amazonaws.com');
28+
test('parse neptune analytics endpoint', () => {
29+
let neptuneInfo = parseNeptuneEndpoint('g-abcdef.us-east-1.neptune-graph.amazonaws.com:8183');
30+
expect(neptuneInfo).toHaveProperty('port', '8183');
31+
expect(neptuneInfo).toHaveProperty('host', 'g-abcdef.us-east-1.neptune-graph.amazonaws.com');
32+
expect(neptuneInfo).toHaveProperty('domain', 'neptune-graph.amazonaws.com');
33+
expect(neptuneInfo).toHaveProperty('region', 'us-east-1');
34+
expect(neptuneInfo).toHaveProperty('graphName', 'g-abcdef');
35+
expect(neptuneInfo).toHaveProperty('neptuneType', 'neptune-graph');
2136
});
2237

23-
test('parse domain from endpoint without enough parts throws error', () => {
24-
expect(() => parseNeptuneDomainFromEndpoint('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toThrow('Cannot parse domain from neptune endpoint g-abcdef.us-west-2.neptune-graph.amazonaws.com because it has 1 part(s) delimited by : but expected 2');
38+
test('parse neptune endpoint without port throws error', () => {
39+
expect(() => parseNeptuneEndpoint('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com'))
40+
.toThrow('Cannot parse neptune endpoint db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com because it is not in expected format of host:port');
2541
});
2642

27-
test('parse name from neptune cluster host', () => {
28-
expect(parseNeptuneGraphName('db-neptune-abc-def.cluster-xyz.us-west-2.neptune.amazonaws.com')).toBe('db-neptune-abc-def');
29-
});
30-
31-
test('parse name from neptune analytics host', () => {
32-
expect(parseNeptuneGraphName('g-abcdef.us-west-2.neptune-graph.amazonaws.com')).toBe('g-abcdef');
33-
});
34-
35-
test('parse name from host without enough parts throws error', () => {
36-
expect(() => parseNeptuneGraphName('invalid.com')).toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5');
43+
test('parse neptune endpoint not enough parts in domain throws error', () => {
44+
expect(() => parseNeptuneEndpoint('invalid.com:1234'))
45+
.toThrow('Cannot parse neptune host invalid.com because it has 2 part(s) delimited by . but expected at least 5');
3746
});

0 commit comments

Comments
 (0)