Skip to content

Commit

Permalink
feat: Add support for secrets manager environment variables (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
christophersjchow committed May 8, 2022
1 parent 24e62d8 commit 4e59429
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package/lib/compileFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ module.exports = {
_.get(funcObject, 'timeout') || _.get(this, 'serverless.service.provider.timeout') || '60s';
funcTemplate.properties.environmentVariables =
this.provider.getConfiguredEnvironment(funcObject);
funcTemplate.properties.secretEnvironmentVariables =
this.provider.getConfiguredSecrets(funcObject);

if (!funcTemplate.properties.serviceAccountEmail) {
delete funcTemplate.properties.serviceAccountEmail;
Expand Down Expand Up @@ -80,6 +82,9 @@ module.exports = {
if (!_.size(funcTemplate.properties.environmentVariables)) {
delete funcTemplate.properties.environmentVariables;
}
if (!_.size(funcTemplate.properties.secretEnvironmentVariables)) {
delete funcTemplate.properties.secretEnvironmentVariables;
}

funcTemplate.properties.labels = _.assign(
{},
Expand Down
102 changes: 102 additions & 0 deletions package/lib/compileFunctions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,108 @@ describe('CompileFunctions', () => {
});
});

it('should set the secret environment variables based on the function configuration', () => {
googlePackage.serverless.service.functions = {
func1: {
handler: 'func1',
secrets: {
TEST_SECRET: {
secret: 'secret',
version: 'latest',
},
},
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: 'nodejs10',
function: 'my-service-dev-func1',
entryPoint: 'func1',
availableMemoryMb: 256,
secretEnvironmentVariables: [
{
key: 'TEST_SECRET',
secret: 'secret',
version: 'latest',
},
],
timeout: '60s',
sourceArchiveUrl: 'gs://sls-my-service-dev-12345678/some-path/artifact.zip',
httpsTrigger: {
url: 'foo',
},
labels: {},
},
},
];

return googlePackage.compileFunctions().then(() => {
expect(consoleLogStub.calledOnce).toEqual(true);
expect(
googlePackage.serverless.service.provider.compiledConfigurationTemplate.resources
).toEqual(compiledResources);
});
});

it('should merge the secret environment variables on the provider configuration and function definition', () => {
googlePackage.serverless.service.functions = {
func1: {
handler: 'func1',
secrets: {
TEST_SECRET: { secret: 'secret1', version: 'latest' },
TEST_SECRET2: { secret: 'secret2', version: 'latest' },
},
events: [{ http: 'foo' }],
},
};
googlePackage.serverless.service.provider.secrets = {
TEST_SECRET: { secret: 'secretbase', version: 'latest' },
TEST_SECRET_PROVIDER: { secret: 'secretprovider', version: 'latest' },
};

const compiledResources = [
{
type: 'gcp-types/cloudfunctions-v1:projects.locations.functions',
name: 'my-service-dev-func1',
properties: {
parent: 'projects/myProject/locations/us-central1',
runtime: 'nodejs10',
function: 'my-service-dev-func1',
entryPoint: 'func1',
availableMemoryMb: 256,
secretEnvironmentVariables: [
{ key: 'TEST_SECRET', secret: 'secret1', version: 'latest' },
{ key: 'TEST_SECRET2', secret: 'secret2', version: 'latest' },
{ key: 'TEST_SECRET_PROVIDER', secret: 'secretprovider', version: 'latest' },
],
timeout: '60s',
sourceArchiveUrl: 'gs://sls-my-service-dev-12345678/some-path/artifact.zip',
httpsTrigger: {
url: 'foo',
},
labels: {},
},
},
];

return googlePackage.compileFunctions().then(() => {
expect(consoleLogStub.calledOnce).toEqual(true);
expect(
googlePackage.serverless.service.provider.compiledConfigurationTemplate.resources
).toEqual(compiledResources);
expect(googlePackage.serverless.service.provider.secrets).toEqual({
TEST_SECRET: { secret: 'secretbase', version: 'latest' },
TEST_SECRET_PROVIDER: { secret: 'secretprovider', version: 'latest' },
});
});
});

it('should compile "http" events properly', () => {
googlePackage.serverless.service.functions = {
func1: {
Expand Down
38 changes: 38 additions & 0 deletions provider/googleProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,29 @@ class GoogleProvider {
},
additionalProperties: false,
},
cloudFunctionSecretEnvironmentVariables: {
type: 'object',
patternProperties: {
'^[a-zA-Z0-9_]+$': {
type: 'object',
properties: {
projectId: {
type: 'string',
minLength: 1,
},
secret: {
type: 'string',
pattern: '^[a-zA-Z0-9_-]+$',
},
version: {
type: 'string',
pattern: '^(latest|[0-9.]+)$',
},
},
required: ['secret', 'version'],
},
},
},
cloudFunctionVpcEgress: {
enum: ['ALL', 'ALL_TRAFFIC', 'PRIVATE', 'PRIVATE_RANGES_ONLY'],
},
Expand Down Expand Up @@ -119,6 +142,7 @@ class GoogleProvider {
memorySize: { $ref: '#/definitions/cloudFunctionMemory' }, // Can be overridden by function configuration
timeout: { type: 'string' }, // Can be overridden by function configuration
environment: { $ref: '#/definitions/cloudFunctionEnvironmentVariables' }, // Can be overridden by function configuration
secrets: { $ref: '#/definitions/cloudFunctionSecretEnvironmentVariables' }, // Can be overridden by function configuration
vpc: { type: 'string' }, // Can be overridden by function configuration
vpcEgress: { $ref: '#/definitions/cloudFunctionVpcEgress' }, // Can be overridden by function configuration
labels: { $ref: '#/definitions/resourceManagerLabels' }, // Can be overridden by function configuration
Expand All @@ -133,6 +157,7 @@ class GoogleProvider {
timeout: { type: 'string' }, // Override provider configuration
minInstances: { type: 'number' },
environment: { $ref: '#/definitions/cloudFunctionEnvironmentVariables' }, // Override provider configuration
secrets: { $ref: '#/definitions/cloudFunctionSecretEnvironmentVariables' }, // Can be overridden by function configuration
vpc: { type: 'string' }, // Override provider configuration
vpcEgress: { $ref: '#/definitions/cloudFunctionVpcEgress' }, // Can be overridden by function configuration
labels: { $ref: '#/definitions/resourceManagerLabels' }, // Override provider configuration
Expand Down Expand Up @@ -279,6 +304,19 @@ class GoogleProvider {
);
}

getConfiguredSecrets(funcObject) {
const providerSecrets = _.get(this, 'serverless.service.provider.secrets', {});
const secrets = _.merge({}, providerSecrets, funcObject.secrets);

const keys = Object.keys(secrets).sort();
return keys.map((key) => {
return {
key,
...secrets[key],
};
});
}

getConfiguredEnvironment(funcObject) {
return _.merge(
{},
Expand Down

0 comments on commit 4e59429

Please sign in to comment.