From 7c3df5926a6a8e3fea799a9455a4d83bd6d05838 Mon Sep 17 00:00:00 2001 From: Troy Kelly Date: Fri, 20 Nov 2020 14:48:07 +1100 Subject: [PATCH 1/3] Adding support for egress and ingress settings Resolves #242 --- package/lib/compileFunctions.js | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/package/lib/compileFunctions.js b/package/lib/compileFunctions.js index d24afbf..cc025cd 100644 --- a/package/lib/compileFunctions.js +++ b/package/lib/compileFunctions.js @@ -62,6 +62,18 @@ module.exports = { }); } + if (funcObject.egress) { + _.assign(funcTemplate.properties, { + vpcConnectorEgressSettings: _.get(funcObject, 'egress') || _.get(this, 'serverless.service.provider.egress'), + }); + } + + if (funcObject.ingress) { + _.assign(funcTemplate.properties, { + ingressSettings: _.get(funcObject, 'ingress') || _.get(this, 'serverless.service.provider.ingress'), + }); + } + if (funcObject.maxInstances) { funcTemplate.properties.maxInstances = funcObject.maxInstances; } @@ -157,6 +169,46 @@ const validateVpcConnectorProperty = (funcObject, functionName) => { } }; +/** + * Validate the function egress settings per + * https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#vpcconnectoregresssettings + * @param {*} funcObject + * @param {*} functionName + */ +const validateVpcEgressProperty = (funcObject, functionName) => { + if (funcObject.egress && typeof funcObject.egress === 'string') { + const validTypes = ['VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', 'PRIVATE_RANGES_ONLY', 'ALL_TRAFFIC']; + if (!validTypes.includes(funcObject.egress)) { + const errorMessage = [ + `The function "${functionName}" has an invalid egress setting`, + ' Egress setting should be ALL_TRAFFIC, PRIVATE_RANGES_ONLY or VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', + ' Please check the docs for more info.', + ].join(''); + throw new Error(errorMessage); + } + } +}; + +/** + * Validate the function ingress settings per + * https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#ingresssettings + * @param {*} funcObject + * @param {*} functionName + */ +const validateVpcIngressProperty = (funcObject, functionName) => { + if (funcObject.ingress && typeof funcObject.ingress === 'string') { + const validTypes = ['INGRESS_SETTINGS_UNSPECIFIED', 'ALLOW_ALL', 'ALLOW_INTERNAL_ONLY', 'ALLOW_INTERNAL_AND_GCLB']; + if (!validTypes.includes(funcObject.ingress)) { + const errorMessage = [ + `The function "${functionName}" has an invalid ingress setting`, + ' Ingress setting should be ALLOW_ALL, ALLOW_INTERNAL_ONLY, ALLOW_INTERNAL_AND_GCLB or INGRESS_SETTINGS_UNSPECIFIED', + ' Please check the docs for more info.', + ].join(''); + throw new Error(errorMessage); + } + } +}; + const getFunctionTemplate = (funcObject, projectName, region, sourceArchiveUrl) => { //eslint-disable-line return { From 44bf0050a57ed59b0a29aba4b53819f4c3a00cc4 Mon Sep 17 00:00:00 2001 From: dxsn Date: Mon, 25 Jan 2021 21:49:33 +0700 Subject: [PATCH 2/3] use validate ingress and egress and change coding style as requested --- package/lib/compileFunctions.js | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/package/lib/compileFunctions.js b/package/lib/compileFunctions.js index cc025cd..7d69511 100644 --- a/package/lib/compileFunctions.js +++ b/package/lib/compileFunctions.js @@ -24,6 +24,8 @@ module.exports = { validateHandlerProperty(funcObject, functionName); validateEventsProperty(funcObject, functionName); validateVpcConnectorProperty(funcObject, functionName); + validateVpcEgressProperty(funcObject, functionName); + validateVpcIngressProperty(funcObject, functionName); const funcTemplate = getFunctionTemplate( funcObject, @@ -57,20 +59,22 @@ module.exports = { } if (funcObject.vpc) { - _.assign(funcTemplate.properties, { + Object.assign(funcTemplate.properties, { vpcConnector: _.get(funcObject, 'vpc') || _.get(this, 'serverless.service.provider.vpc'), }); } if (funcObject.egress) { - _.assign(funcTemplate.properties, { - vpcConnectorEgressSettings: _.get(funcObject, 'egress') || _.get(this, 'serverless.service.provider.egress'), + Object.assign(funcTemplate.properties, { + vpcConnectorEgressSettings: + _.get(funcObject, 'egress') || _.get(this, 'serverless.service.provider.egress'), }); } if (funcObject.ingress) { _.assign(funcTemplate.properties, { - ingressSettings: _.get(funcObject, 'ingress') || _.get(this, 'serverless.service.provider.ingress'), + ingressSettings: + _.get(funcObject, 'ingress') || _.get(this, 'serverless.service.provider.ingress'), }); } @@ -169,6 +173,11 @@ const validateVpcConnectorProperty = (funcObject, functionName) => { } }; +const validEgressTypes = new Set([ + 'VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', + 'PRIVATE_RANGES_ONLY', + 'ALL_TRAFFIC', +]); /** * Validate the function egress settings per * https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#vpcconnectoregresssettings @@ -176,9 +185,8 @@ const validateVpcConnectorProperty = (funcObject, functionName) => { * @param {*} functionName */ const validateVpcEgressProperty = (funcObject, functionName) => { - if (funcObject.egress && typeof funcObject.egress === 'string') { - const validTypes = ['VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', 'PRIVATE_RANGES_ONLY', 'ALL_TRAFFIC']; - if (!validTypes.includes(funcObject.egress)) { + if (typeof funcObject.egress === 'string') { + if (!validEgressTypes.includes(funcObject.egress)) { const errorMessage = [ `The function "${functionName}" has an invalid egress setting`, ' Egress setting should be ALL_TRAFFIC, PRIVATE_RANGES_ONLY or VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', @@ -189,6 +197,12 @@ const validateVpcEgressProperty = (funcObject, functionName) => { } }; +const validIngressTypes = new Set([ + 'INGRESS_SETTINGS_UNSPECIFIED', + 'ALLOW_ALL', + 'ALLOW_INTERNAL_ONLY', + 'ALLOW_INTERNAL_AND_GCLB', +]); /** * Validate the function ingress settings per * https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#ingresssettings @@ -196,9 +210,8 @@ const validateVpcEgressProperty = (funcObject, functionName) => { * @param {*} functionName */ const validateVpcIngressProperty = (funcObject, functionName) => { - if (funcObject.ingress && typeof funcObject.ingress === 'string') { - const validTypes = ['INGRESS_SETTINGS_UNSPECIFIED', 'ALLOW_ALL', 'ALLOW_INTERNAL_ONLY', 'ALLOW_INTERNAL_AND_GCLB']; - if (!validTypes.includes(funcObject.ingress)) { + if (typeof funcObject.ingress === 'string') { + if (!validIngressTypes.includes(funcObject.ingress)) { const errorMessage = [ `The function "${functionName}" has an invalid ingress setting`, ' Ingress setting should be ALLOW_ALL, ALLOW_INTERNAL_ONLY, ALLOW_INTERNAL_AND_GCLB or INGRESS_SETTINGS_UNSPECIFIED', From c56395f11838a65d0770a329853cffbe0ff0b3a4 Mon Sep 17 00:00:00 2001 From: dxsn Date: Mon, 25 Jan 2021 22:07:46 +0700 Subject: [PATCH 3/3] add test coverage and fix error with set --- package/lib/compileFunctions.js | 6 +- package/lib/compileFunctions.test.js | 128 +++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) diff --git a/package/lib/compileFunctions.js b/package/lib/compileFunctions.js index 7d69511..4dfeba3 100644 --- a/package/lib/compileFunctions.js +++ b/package/lib/compileFunctions.js @@ -72,7 +72,7 @@ module.exports = { } if (funcObject.ingress) { - _.assign(funcTemplate.properties, { + Object.assign(funcTemplate.properties, { ingressSettings: _.get(funcObject, 'ingress') || _.get(this, 'serverless.service.provider.ingress'), }); @@ -186,7 +186,7 @@ const validEgressTypes = new Set([ */ const validateVpcEgressProperty = (funcObject, functionName) => { if (typeof funcObject.egress === 'string') { - if (!validEgressTypes.includes(funcObject.egress)) { + if (!validEgressTypes.has(funcObject.egress)) { const errorMessage = [ `The function "${functionName}" has an invalid egress setting`, ' Egress setting should be ALL_TRAFFIC, PRIVATE_RANGES_ONLY or VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED', @@ -211,7 +211,7 @@ const validIngressTypes = new Set([ */ const validateVpcIngressProperty = (funcObject, functionName) => { if (typeof funcObject.ingress === 'string') { - if (!validIngressTypes.includes(funcObject.ingress)) { + if (!validIngressTypes.has(funcObject.ingress)) { const errorMessage = [ `The function "${functionName}" has an invalid ingress setting`, ' Ingress setting should be ALLOW_ALL, ALLOW_INTERNAL_ONLY, ALLOW_INTERNAL_AND_GCLB or INGRESS_SETTINGS_UNSPECIFIED', diff --git a/package/lib/compileFunctions.test.js b/package/lib/compileFunctions.test.js index cfdc6f4..cd65806 100644 --- a/package/lib/compileFunctions.test.js +++ b/package/lib/compileFunctions.test.js @@ -93,6 +93,50 @@ describe('CompileFunctions', () => { expect(() => googlePackage.compileFunctions()).toThrow(Error); }); + it('should throw an error if the vpc connector property is invalid', () => { + googlePackage.serverless.service.functions = { + func1: { + handler: 'func1', + memorySize: 128, + runtime: 'nodejs8', + vpc: 'project/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + events: [{ http: 'foo' }], + }, + }; + + expect(() => googlePackage.compileFunctions()).toThrow(Error); + }); + + it('should throw an error if the vpc connector egress property is invalid', () => { + googlePackage.serverless.service.functions = { + func1: { + handler: 'func1', + memorySize: 128, + runtime: 'nodejs8', + vpc: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + egress: 'foo', + events: [{ http: 'foo' }], + }, + }; + + expect(() => googlePackage.compileFunctions()).toThrow(Error); + }); + + it('should throw an error if the vpc connector ingress property is invalid', () => { + googlePackage.serverless.service.functions = { + func1: { + handler: 'func1', + memorySize: 128, + runtime: 'nodejs8', + vpc: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + ingress: 'foo', + events: [{ http: 'foo' }], + }, + }; + + expect(() => googlePackage.compileFunctions()).toThrow(Error); + }); + it('should set the memory size based on the functions configuration', () => { googlePackage.serverless.service.functions = { func1: { @@ -658,6 +702,90 @@ describe('CompileFunctions', () => { }); }); + it('should set vpc egress on the function configuration', () => { + googlePackage.serverless.service.functions = { + func1: { + handler: 'func1', + memorySize: 128, + runtime: 'nodejs8', + vpc: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + egress: 'ALL_TRAFFIC', + events: [{ http: 'foo' }], + }, + }; + + const compiledResources = [ + { + type: 'gcp-types/cloudfunctions-v1:projects.locations.functions', + name: 'my-service-dev-func1', + properties: { + parent: 'projects/myProject/locations/us-central1', + runtime: 'nodejs8', + function: 'my-service-dev-func1', + entryPoint: 'func1', + availableMemoryMb: 128, + timeout: '60s', + sourceArchiveUrl: 'gs://sls-my-service-dev-12345678/some-path/artifact.zip', + httpsTrigger: { + url: 'foo', + }, + labels: {}, + vpcConnector: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + vpcConnectorEgressSettings: 'ALL_TRAFFIC', + }, + }, + ]; + + return googlePackage.compileFunctions().then(() => { + expect(consoleLogStub.called).toEqual(true); + expect( + googlePackage.serverless.service.provider.compiledConfigurationTemplate.resources + ).toEqual(compiledResources); + }); + }); + + it('should set vpc ingress on the function configuration', () => { + googlePackage.serverless.service.functions = { + func1: { + handler: 'func1', + memorySize: 128, + runtime: 'nodejs8', + vpc: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + ingress: 'ALLOW_ALL', + events: [{ http: 'foo' }], + }, + }; + + const compiledResources = [ + { + type: 'gcp-types/cloudfunctions-v1:projects.locations.functions', + name: 'my-service-dev-func1', + properties: { + parent: 'projects/myProject/locations/us-central1', + runtime: 'nodejs8', + function: 'my-service-dev-func1', + entryPoint: 'func1', + availableMemoryMb: 128, + timeout: '60s', + sourceArchiveUrl: 'gs://sls-my-service-dev-12345678/some-path/artifact.zip', + httpsTrigger: { + url: 'foo', + }, + labels: {}, + vpcConnector: 'projects/pg-us-n-app-123456/locations/us-central1/connectors/my-vpc', + ingressSettings: 'ALLOW_ALL', + }, + }, + ]; + + return googlePackage.compileFunctions().then(() => { + expect(consoleLogStub.called).toEqual(true); + expect( + googlePackage.serverless.service.provider.compiledConfigurationTemplate.resources + ).toEqual(compiledResources); + }); + }); + it('should set max instances on the function configuration', () => { googlePackage.serverless.service.functions = { func1: {