From 10bfbe2a9d3313fca27d40aba996e4346c169fe3 Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Tue, 21 Jun 2022 18:45:32 +0200 Subject: [PATCH 1/6] Read scw config --- examples/python3/serverless.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/python3/serverless.yml b/examples/python3/serverless.yml index c22c596f..f10d89d4 100644 --- a/examples/python3/serverless.yml +++ b/examples/python3/serverless.yml @@ -2,15 +2,16 @@ service: scaleway-python3 configValidationMode: off provider: name: scaleway - runtime: python310 # Available python runtimes are listed in documentation - # Global Environment variables - used in every functions + runtime: python310 + + # Global Environment variables - used in every function env: test: test - # the path to the credentials file needs to be absolute - scwToken: - scwProject: - # region in which the deployment will happen - scwRegion: fr-par + + # Read credentials from scw CLI config + scwToken: "${file(${env:HOME}/.config/scw/config.yaml):secret_key}" + scwProject: "${file(${env:HOME}/.config/scw/config.yaml):default_project_id}" + scwRegion: "${file(${env:HOME}/.config/scw/config.yaml):default_region}" plugins: - serverless-scaleway-functions @@ -24,6 +25,7 @@ package: functions: first: handler: handler.handle + # Local environment variables - used only in given function env: local: local From 7c614b72e2930f5bb44273aed29d62309231838e Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Mon, 27 Jun 2022 09:58:30 +0200 Subject: [PATCH 2/6] Read config in provider --- examples/python3/serverless.yml | 5 ----- provider/scalewayProvider.js | 30 +++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/examples/python3/serverless.yml b/examples/python3/serverless.yml index f10d89d4..8e246c79 100644 --- a/examples/python3/serverless.yml +++ b/examples/python3/serverless.yml @@ -8,11 +8,6 @@ provider: env: test: test - # Read credentials from scw CLI config - scwToken: "${file(${env:HOME}/.config/scw/config.yaml):secret_key}" - scwProject: "${file(${env:HOME}/.config/scw/config.yaml):default_project_id}" - scwRegion: "${file(${env:HOME}/.config/scw/config.yaml):default_region}" - plugins: - serverless-scaleway-functions diff --git a/provider/scalewayProvider.js b/provider/scalewayProvider.js index 6978eb36..ffe3aa39 100644 --- a/provider/scalewayProvider.js +++ b/provider/scalewayProvider.js @@ -1,11 +1,18 @@ 'use strict'; +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const yaml = require('js-yaml'); + const BbPromise = require('bluebird'); const { FUNCTIONS_API_URL } = require('../shared/constants'); const { CONTAINERS_API_URL } = require('../shared/constants'); const { REGISTRY_API_URL } = require('../shared/constants'); const { DEFAULT_REGION } = require('../shared/constants'); +const scwConfigFile = path.join(os.homedir(), ".config", "scw", "config.yaml"); + const providerName = 'scaleway'; class ScalewayProvider { @@ -52,10 +59,19 @@ class ScalewayProvider { this.serverless.cli.log('please update to SCW_SECRET_KEY and SCW_DEFAULT_PROJECT_ID'); this.scwToken = process.env.SCW_TOKEN; this.scwProject = process.env.SCW_PROJECT; - } else { + } else if (this.serverless.service.provider.scwToken || + this.serverless.service.provider.scwProject) { this.serverless.cli.log('Using credentials from yml'); - this.scwToken = this.serverless.service.provider.scwToken || ''; - this.scwProject = this.serverless.service.provider.scwProject || ''; + this.scwToken = this.serverless.service.provider.scwToken; + this.scwProject = this.serverless.service.provider.scwProject; + } else if (this.scwConfig) { + this.scwToken = this.scwConfig.secret_key; + this.scwProject = this.scwConfig.default_project_id; + this.scwRegion = this.scwConfig.default_region; + } else { + this.serverless.cli.log('Unable to locate Scaleway provider credentials'); + this.scwToken = ''; + this.scwProject = ''; } } @@ -76,6 +92,14 @@ class ScalewayProvider { this.serverless = serverless; this.options = options; + this.scwConfig = null; + if (fs.existsSync(scwConfigFile)) { + this.serverless.cli.log(`Using credentials from ${scwConfigFile}`); + + let fileData = fs.readFileSync(scwConfigFile, 'utf8'); + this.scwConfig = yaml.safeLoad(fileData); + } + return new BbPromise((resolve) => { this.setCredentials(options); this.setApiURL(options); From 83b14eeb82f3bd806c75b1a7b3bb7dbd8538fabe Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Tue, 28 Jun 2022 18:21:44 +0200 Subject: [PATCH 3/6] Add failing test for provider creds --- provider/scalewayProvider.js | 6 +- tests/provider/scalewayProvider.test.js | 121 ++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 tests/provider/scalewayProvider.test.js diff --git a/provider/scalewayProvider.js b/provider/scalewayProvider.js index ffe3aa39..11e32d3a 100644 --- a/provider/scalewayProvider.js +++ b/provider/scalewayProvider.js @@ -11,11 +11,11 @@ const { CONTAINERS_API_URL } = require('../shared/constants'); const { REGISTRY_API_URL } = require('../shared/constants'); const { DEFAULT_REGION } = require('../shared/constants'); -const scwConfigFile = path.join(os.homedir(), ".config", "scw", "config.yaml"); - const providerName = 'scaleway'; class ScalewayProvider { + static scwConfigFile = path.join(os.homedir(), ".config", "scw", "config.yaml"); + static getProviderName() { return providerName; } @@ -94,8 +94,6 @@ class ScalewayProvider { this.scwConfig = null; if (fs.existsSync(scwConfigFile)) { - this.serverless.cli.log(`Using credentials from ${scwConfigFile}`); - let fileData = fs.readFileSync(scwConfigFile, 'utf8'); this.scwConfig = yaml.safeLoad(fileData); } diff --git a/tests/provider/scalewayProvider.test.js b/tests/provider/scalewayProvider.test.js new file mode 100644 index 00000000..781354fa --- /dev/null +++ b/tests/provider/scalewayProvider.test.js @@ -0,0 +1,121 @@ +const { expect } = require('chai'); +const { expect: jestExpect } = require('@jest/globals'); + +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const yaml = require('js-yaml'); + +const ScalewayProvider = require('../../provider/scalewayProvider'); + +describe('Scaleway credentials test', () => { + this.serverless = { + setProvider: (prov) => { + this.provider = prov; + }, + cli: { + log: (logMsg) => {} + } + }; + + this.expectedToken = null; + this.expectedProject = null; + + this.dummyYamlFile = false; + + beforeAll(() => { + if(!fs.existsSync(ScalewayProvider.scwConfigFile)) { + this.dummyYamlFile = true; + + const fileContents = 'secret_key: scw-key\ndefault_project_id: scw-proj'; + + // TODO - write dummy file + } + }); + + afterAll(() => { + if(this.dummyYamlFile) { + // TODO - delete file + } + }); + + this.checkCreds = (options) => { + // Create the provider + this.prov = new ScalewayProvider(this.serverless); + + // Set the credentials + this.prov.setCredentials(options); + + // Check they're as expected + expect(this.prov.scwToken).to.equal(this.expectedToken); + expect(this.prov.scwProject).to.equal(this.expectedProject); + }; + + it('should read from scw config file', () => { + let fileData = fs.readFileSync(ScalewayProvider.scwConfigFile, 'utf8'); + let scwConfig = yaml.safeLoad(fileData); + + this.expectedToken = scwConfig.secret_key; + this.expectedProject = scwConfig.default_project_id; + + this.checkCreds({}); + }); + + it('should read from legacy environment variables if present', () => { + let originalToken = process.env.SCW_TOKEN; + let originalProject = process.env.SCW_PROJECT; + + this.expectedToken = "legacy-token"; + this.expectedProject = "legacy-proj"; + + process.env.SCW_TOKEN = this.expectedToken; + process.env.SCW_PROJECT = this.expectedProject; + + this.checkCreds({}); + + process.env.SCW_TOKEN = originalToken; + process.env.SCW_PROJECT = originalProject; + }); + + it('should read from environment variables if present', () => { + let originalToken = process.env.SCW_SECRET_KEY; + let originalProject = process.env.SCW_DEFAULT_PROJECT_ID; + + this.expectedToken = "env-token"; + this.expectedProject = "env-proj"; + + process.env.SCW_SECRET_KEY = this.expectedToken; + process.env.SCW_DEFAULT_PROJECT_ID = this.expectedProject; + + this.checkCreds({}); + + process.env.SCW_SECRET_KEY = originalToken; + process.env.SCW_DEFAULT_PROJECT_ID = originalProject; + }); + + it('should take values from serverless config if present', () => { + this.expectedToken = "conf-token"; + this.expectedProject = "conf-proj"; + + this.serverless.service = { + provider: { + scwToken: this.expectedToken, + scwProject: this.expectedProject, + }, + }; + + this.checkCreds({}); + }); + + it('should read credentials from options if present', () => { + let options = {}; + options['scw-token'] = "opt-token"; + options['scw-project'] = "opt-proj"; + + this.expectedToken = "opt-token"; + this.expectedProject = "opt-proj"; + + this.checkCreds(options); + }); +}); + From 9ce9c56dbad7b0c10e755b48c8c6e11577d0b14e Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Wed, 29 Jun 2022 10:07:15 +0200 Subject: [PATCH 4/6] Add test for credentials reading --- provider/scalewayProvider.js | 19 ++-- tests/provider/scalewayProvider.test.js | 111 ++++++++++++++---------- tests/utils/fs/index.js | 7 ++ tests/utils/misc/index.js | 2 +- 4 files changed, 82 insertions(+), 57 deletions(-) diff --git a/provider/scalewayProvider.js b/provider/scalewayProvider.js index 11e32d3a..32fe3741 100644 --- a/provider/scalewayProvider.js +++ b/provider/scalewayProvider.js @@ -64,10 +64,15 @@ class ScalewayProvider { this.serverless.cli.log('Using credentials from yml'); this.scwToken = this.serverless.service.provider.scwToken; this.scwProject = this.serverless.service.provider.scwProject; - } else if (this.scwConfig) { - this.scwToken = this.scwConfig.secret_key; - this.scwProject = this.scwConfig.default_project_id; - this.scwRegion = this.scwConfig.default_region; + } else if (fs.existsSync(ScalewayProvider.scwConfigFile)) { + this.serverless.cli.log(`Using credentials from ${ScalewayProvider.scwConfigFile}`); + + let fileData = fs.readFileSync(ScalewayProvider.scwConfigFile, 'utf8'); + let scwConfig = yaml.load(fileData); + + this.scwToken = scwConfig.secret_key; + this.scwProject = scwConfig.default_project_id; + this.scwRegion = scwConfig.default_region; } else { this.serverless.cli.log('Unable to locate Scaleway provider credentials'); this.scwToken = ''; @@ -92,12 +97,6 @@ class ScalewayProvider { this.serverless = serverless; this.options = options; - this.scwConfig = null; - if (fs.existsSync(scwConfigFile)) { - let fileData = fs.readFileSync(scwConfigFile, 'utf8'); - this.scwConfig = yaml.safeLoad(fileData); - } - return new BbPromise((resolve) => { this.setCredentials(options); this.setApiURL(options); diff --git a/tests/provider/scalewayProvider.test.js b/tests/provider/scalewayProvider.test.js index 781354fa..eb9f677e 100644 --- a/tests/provider/scalewayProvider.test.js +++ b/tests/provider/scalewayProvider.test.js @@ -7,42 +7,51 @@ const path = require('path'); const yaml = require('js-yaml'); const ScalewayProvider = require('../../provider/scalewayProvider'); +const { createTmpDir } = require('../utils/fs'); -describe('Scaleway credentials test', () => { - this.serverless = { - setProvider: (prov) => { - this.provider = prov; - }, - cli: { - log: (logMsg) => {} +class MockServerless { + constructor() { + this.service = {}; + this.service.provider = {}; + + this.cli = {}; + this.cli.log = (logMsg) => { + console.log(logMsg); } }; + setProvider(provName, prov) { + this.service.provider = prov; + }; +}; + +describe('Scaleway credentials test', () => { this.expectedToken = null; this.expectedProject = null; - this.dummyYamlFile = false; + this.serverless = new MockServerless(); + this.prov = new ScalewayProvider(this.serverless); beforeAll(() => { - if(!fs.existsSync(ScalewayProvider.scwConfigFile)) { - this.dummyYamlFile = true; - - const fileContents = 'secret_key: scw-key\ndefault_project_id: scw-proj'; + // Override scw config file location + this.dummyScwConfigDir = createTmpDir(); + this.dummyScwConfigPath = path.join(this.dummyScwConfigDir, 'config.yml'); - // TODO - write dummy file - } + ScalewayProvider.scwConfigFile = this.dummyScwConfigPath; }); afterAll(() => { - if(this.dummyYamlFile) { - // TODO - delete file + // Delete the dummy config file and directory + if(fs.existsSync(this.dummyScwConfigPath)) { + fs.unlinkSync(this.dummyScwConfigPath); + } + + if(fs.existsSync(this.dummyScwConfigDir)) { + fs.rmdirSync(this.dummyScwConfigDir); } }); this.checkCreds = (options) => { - // Create the provider - this.prov = new ScalewayProvider(this.serverless); - // Set the credentials this.prov.setCredentials(options); @@ -51,12 +60,36 @@ describe('Scaleway credentials test', () => { expect(this.prov.scwProject).to.equal(this.expectedProject); }; - it('should read from scw config file', () => { - let fileData = fs.readFileSync(ScalewayProvider.scwConfigFile, 'utf8'); - let scwConfig = yaml.safeLoad(fileData); + /* + * The tests here get run in order, hence we start with the lowest priority + * form of configuration first. This will then get superceded by the next one, + * and so on. + */ - this.expectedToken = scwConfig.secret_key; - this.expectedProject = scwConfig.default_project_id; + it('should return nothing when no credentials found', () => { + this.expectedToken = ''; + this.expectedProject = ''; + + this.checkCreds({}); + }); + + it('should read from scw config file if present', () => { + // Write the dummy file + const dummyScwConfigContents = 'secret_key: scw-key\ndefault_project_id: scw-proj\n'; + fs.writeFileSync(this.dummyScwConfigPath, dummyScwConfigContents); + + this.expectedToken = 'scw-key'; + this.expectedProject = 'scw-proj'; + + this.checkCreds({}); + }); + + it('should take values from serverless.yml if present', () => { + this.expectedToken = 'conf-token'; + this.expectedProject = 'conf-proj'; + + this.serverless.service.provider.scwToken = this.expectedToken; + this.serverless.service.provider.scwProject = this.expectedProject; this.checkCreds({}); }); @@ -65,8 +98,8 @@ describe('Scaleway credentials test', () => { let originalToken = process.env.SCW_TOKEN; let originalProject = process.env.SCW_PROJECT; - this.expectedToken = "legacy-token"; - this.expectedProject = "legacy-proj"; + this.expectedToken = 'legacy-token'; + this.expectedProject = 'legacy-proj'; process.env.SCW_TOKEN = this.expectedToken; process.env.SCW_PROJECT = this.expectedProject; @@ -81,8 +114,8 @@ describe('Scaleway credentials test', () => { let originalToken = process.env.SCW_SECRET_KEY; let originalProject = process.env.SCW_DEFAULT_PROJECT_ID; - this.expectedToken = "env-token"; - this.expectedProject = "env-proj"; + this.expectedToken = 'env-token'; + this.expectedProject = 'env-proj'; process.env.SCW_SECRET_KEY = this.expectedToken; process.env.SCW_DEFAULT_PROJECT_ID = this.expectedProject; @@ -93,27 +126,13 @@ describe('Scaleway credentials test', () => { process.env.SCW_DEFAULT_PROJECT_ID = originalProject; }); - it('should take values from serverless config if present', () => { - this.expectedToken = "conf-token"; - this.expectedProject = "conf-proj"; - - this.serverless.service = { - provider: { - scwToken: this.expectedToken, - scwProject: this.expectedProject, - }, - }; - - this.checkCreds({}); - }); - it('should read credentials from options if present', () => { let options = {}; - options['scw-token'] = "opt-token"; - options['scw-project'] = "opt-proj"; + options['scw-token'] = 'opt-token'; + options['scw-project'] = 'opt-proj'; - this.expectedToken = "opt-token"; - this.expectedProject = "opt-proj"; + this.expectedToken = 'opt-token'; + this.expectedProject = 'opt-proj'; this.checkCreds(options); }); diff --git a/tests/utils/fs/index.js b/tests/utils/fs/index.js index 676a96a8..16a778e8 100644 --- a/tests/utils/fs/index.js +++ b/tests/utils/fs/index.js @@ -16,6 +16,12 @@ function getTmpDirPath() { return path.join(tmpDirCommonPath, crypto.randomBytes(8).toString('hex')); } +function createTmpDir() { + const tmpDir = getTmpDirPath(); + fs.mkdirSync(tmpDir, { recursive: true }); + return tmpDir; +} + function replaceTextInFile(filePath, subString, newSubString) { const fileContent = fs.readFileSync(filePath).toString(); fs.writeFileSync(filePath, fileContent.replace(subString, newSubString)); @@ -35,6 +41,7 @@ function writeYamlFile(filePath, content) { module.exports = { tmpDirCommonPath, getTmpDirPath, + createTmpDir, replaceTextInFile, readYamlFile, diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index deb7b5b9..6255ca10 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -62,7 +62,7 @@ function createTestService( execSync(`${serverlessExec} create --template-path ${options.templateName} --path ${tmpDir}`); process.chdir(tmpDir); // Install dependencies - execSync(`npm link ${repoDir}`); + execSync(`npm link --force ${repoDir}`); const serverlessFilePath = path.join(tmpDir, 'serverless.yml'); let serverlessConfig = readYamlFile(serverlessFilePath); From 047b44a59a7baaf7e7ecdd4274b4a474f8189705 Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Wed, 29 Jun 2022 10:07:50 +0200 Subject: [PATCH 5/6] Revert change to py310 example --- examples/python3/serverless.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/python3/serverless.yml b/examples/python3/serverless.yml index 8e246c79..c22c596f 100644 --- a/examples/python3/serverless.yml +++ b/examples/python3/serverless.yml @@ -2,11 +2,15 @@ service: scaleway-python3 configValidationMode: off provider: name: scaleway - runtime: python310 - - # Global Environment variables - used in every function + runtime: python310 # Available python runtimes are listed in documentation + # Global Environment variables - used in every functions env: test: test + # the path to the credentials file needs to be absolute + scwToken: + scwProject: + # region in which the deployment will happen + scwRegion: fr-par plugins: - serverless-scaleway-functions @@ -20,7 +24,6 @@ package: functions: first: handler: handler.handle - # Local environment variables - used only in given function env: local: local From 8ab363ac3171638cffe8a4e376d5a95cf3d544ef Mon Sep 17 00:00:00 2001 From: Simon Shillaker Date: Wed, 29 Jun 2022 10:14:25 +0200 Subject: [PATCH 6/6] Tidy-up --- tests/provider/scalewayProvider.test.js | 9 +++------ tests/utils/misc/index.js | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/provider/scalewayProvider.test.js b/tests/provider/scalewayProvider.test.js index eb9f677e..af87349f 100644 --- a/tests/provider/scalewayProvider.test.js +++ b/tests/provider/scalewayProvider.test.js @@ -4,7 +4,6 @@ const { expect: jestExpect } = require('@jest/globals'); const fs = require('fs'); const os = require('os'); const path = require('path'); -const yaml = require('js-yaml'); const ScalewayProvider = require('../../provider/scalewayProvider'); const { createTmpDir } = require('../utils/fs'); @@ -60,11 +59,9 @@ describe('Scaleway credentials test', () => { expect(this.prov.scwProject).to.equal(this.expectedProject); }; - /* - * The tests here get run in order, hence we start with the lowest priority - * form of configuration first. This will then get superceded by the next one, - * and so on. - */ + // ------------------------------------- + // These tests must be written in order of increasing precedence, each one getting superceded by the next. + // ------------------------------------- it('should return nothing when no credentials found', () => { this.expectedToken = ''; diff --git a/tests/utils/misc/index.js b/tests/utils/misc/index.js index 6255ca10..deb7b5b9 100644 --- a/tests/utils/misc/index.js +++ b/tests/utils/misc/index.js @@ -62,7 +62,7 @@ function createTestService( execSync(`${serverlessExec} create --template-path ${options.templateName} --path ${tmpDir}`); process.chdir(tmpDir); // Install dependencies - execSync(`npm link --force ${repoDir}`); + execSync(`npm link ${repoDir}`); const serverlessFilePath = path.join(tmpDir, 'serverless.yml'); let serverlessConfig = readYamlFile(serverlessFilePath);