Skip to content

Commit 5fb1a0f

Browse files
authored
Read credentials from scw config (#83)
1 parent 82896a3 commit 5fb1a0f

File tree

3 files changed

+186
-5
lines changed

3 files changed

+186
-5
lines changed

provider/scalewayProvider.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
'use strict';
22

3+
const fs = require('fs');
4+
const os = require('os');
5+
const path = require('path');
6+
const yaml = require('js-yaml');
7+
38
const BbPromise = require('bluebird');
49
const { FUNCTIONS_API_URL } = require('../shared/constants');
510
const { CONTAINERS_API_URL } = require('../shared/constants');
@@ -9,6 +14,8 @@ const { DEFAULT_REGION } = require('../shared/constants');
914
const providerName = 'scaleway';
1015

1116
class ScalewayProvider {
17+
static scwConfigFile = path.join(os.homedir(), ".config", "scw", "config.yaml");
18+
1219
static getProviderName() {
1320
return providerName;
1421
}
@@ -39,37 +46,67 @@ class ScalewayProvider {
3946

4047
setCredentials(options) {
4148
// On serverless info command we do not want log pollution from authentication.
42-
// This is necessary to use it in automated environment.
49+
// This is necessary to use it in an automated environment.
4350
let hideLog = false;
44-
if (this.serverless.configurationInput.service && this.serverless.configurationInput.service === 'serverlessInfo') {
51+
if (this.serverless.configurationInput.service &&
52+
this.serverless.configurationInput.service === 'serverlessInfo') {
4553
hideLog = true;
4654
}
55+
4756
if (options['scw-token'] && options['scw-project']) {
4857
if (!hideLog) {
4958
this.serverless.cli.log('Using credentials from command line parameters');
5059
}
60+
5161
this.scwToken = options['scw-token'];
5262
this.scwProject = options['scw-project'];
63+
5364
} else if (process.env.SCW_SECRET_KEY && process.env.SCW_DEFAULT_PROJECT_ID) {
5465
if (!hideLog) {
5566
this.serverless.cli.log('Using credentials from system environment');
5667
}
68+
5769
this.scwToken = process.env.SCW_SECRET_KEY;
5870
this.scwProject = process.env.SCW_DEFAULT_PROJECT_ID;
71+
5972
} else if (process.env.SCW_TOKEN && process.env.SCW_PROJECT) {
6073
if (!hideLog) {
6174
this.serverless.cli.log('Using credentials from system environment');
6275
this.serverless.cli.log('NOTICE: you are using deprecated environment variable notation,');
6376
this.serverless.cli.log('please update to SCW_SECRET_KEY and SCW_DEFAULT_PROJECT_ID');
6477
}
78+
6579
this.scwToken = process.env.SCW_TOKEN;
6680
this.scwProject = process.env.SCW_PROJECT;
81+
82+
} else if (this.serverless.service.provider.scwToken ||
83+
this.serverless.service.provider.scwProject) {
84+
if (!hideLog) {
85+
this.serverless.cli.log('Using credentials from serverless.yml');
86+
}
87+
88+
this.scwToken = this.serverless.service.provider.scwToken;
89+
this.scwProject = this.serverless.service.provider.scwProject;
90+
91+
} else if (fs.existsSync(ScalewayProvider.scwConfigFile)) {
92+
if (!hideLog) {
93+
this.serverless.cli.log(`Using credentials from ${ScalewayProvider.scwConfigFile}`);
94+
}
95+
96+
let fileData = fs.readFileSync(ScalewayProvider.scwConfigFile, 'utf8');
97+
let scwConfig = yaml.load(fileData);
98+
99+
this.scwToken = scwConfig.secret_key;
100+
this.scwProject = scwConfig.default_project_id;
101+
this.scwRegion = scwConfig.default_region;
102+
67103
} else {
68104
if (!hideLog) {
69-
this.serverless.cli.log('Using credentials from yml');
105+
this.serverless.cli.log('Unable to locate Scaleway provider credentials');
70106
}
71-
this.scwToken = this.serverless.service.provider.scwToken || '';
72-
this.scwProject = this.serverless.service.provider.scwProject || '';
107+
108+
this.scwToken = '';
109+
this.scwProject = '';
73110
}
74111
}
75112

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
const { expect } = require('chai');
2+
const { expect: jestExpect } = require('@jest/globals');
3+
4+
const fs = require('fs');
5+
const os = require('os');
6+
const path = require('path');
7+
8+
const ScalewayProvider = require('../../provider/scalewayProvider');
9+
const { createTmpDir } = require('../utils/fs');
10+
11+
class MockServerless {
12+
constructor() {
13+
this.service = {};
14+
this.service.provider = {};
15+
16+
this.cli = {};
17+
this.cli.log = (logMsg) => {
18+
console.log(logMsg);
19+
}
20+
};
21+
22+
setProvider(provName, prov) {
23+
this.service.provider = prov;
24+
};
25+
};
26+
27+
describe('Scaleway credentials test', () => {
28+
this.expectedToken = null;
29+
this.expectedProject = null;
30+
31+
this.serverless = new MockServerless();
32+
this.prov = new ScalewayProvider(this.serverless);
33+
34+
beforeAll(() => {
35+
// Override scw config file location
36+
this.dummyScwConfigDir = createTmpDir();
37+
this.dummyScwConfigPath = path.join(this.dummyScwConfigDir, 'config.yml');
38+
39+
ScalewayProvider.scwConfigFile = this.dummyScwConfigPath;
40+
});
41+
42+
afterAll(() => {
43+
// Delete the dummy config file and directory
44+
if(fs.existsSync(this.dummyScwConfigPath)) {
45+
fs.unlinkSync(this.dummyScwConfigPath);
46+
}
47+
48+
if(fs.existsSync(this.dummyScwConfigDir)) {
49+
fs.rmdirSync(this.dummyScwConfigDir);
50+
}
51+
});
52+
53+
this.checkCreds = (options) => {
54+
// Set the credentials
55+
this.prov.setCredentials(options);
56+
57+
// Check they're as expected
58+
expect(this.prov.scwToken).to.equal(this.expectedToken);
59+
expect(this.prov.scwProject).to.equal(this.expectedProject);
60+
};
61+
62+
// -------------------------------------
63+
// These tests must be written in order of increasing precedence, each one getting superceded by the next.
64+
// -------------------------------------
65+
66+
it('should return nothing when no credentials found', () => {
67+
this.expectedToken = '';
68+
this.expectedProject = '';
69+
70+
this.checkCreds({});
71+
});
72+
73+
it('should read from scw config file if present', () => {
74+
// Write the dummy file
75+
const dummyScwConfigContents = 'secret_key: scw-key\ndefault_project_id: scw-proj\n';
76+
fs.writeFileSync(this.dummyScwConfigPath, dummyScwConfigContents);
77+
78+
this.expectedToken = 'scw-key';
79+
this.expectedProject = 'scw-proj';
80+
81+
this.checkCreds({});
82+
});
83+
84+
it('should take values from serverless.yml if present', () => {
85+
this.expectedToken = 'conf-token';
86+
this.expectedProject = 'conf-proj';
87+
88+
this.serverless.service.provider.scwToken = this.expectedToken;
89+
this.serverless.service.provider.scwProject = this.expectedProject;
90+
91+
this.checkCreds({});
92+
});
93+
94+
it('should read from legacy environment variables if present', () => {
95+
let originalToken = process.env.SCW_TOKEN;
96+
let originalProject = process.env.SCW_PROJECT;
97+
98+
this.expectedToken = 'legacy-token';
99+
this.expectedProject = 'legacy-proj';
100+
101+
process.env.SCW_TOKEN = this.expectedToken;
102+
process.env.SCW_PROJECT = this.expectedProject;
103+
104+
this.checkCreds({});
105+
106+
process.env.SCW_TOKEN = originalToken;
107+
process.env.SCW_PROJECT = originalProject;
108+
});
109+
110+
it('should read from environment variables if present', () => {
111+
let originalToken = process.env.SCW_SECRET_KEY;
112+
let originalProject = process.env.SCW_DEFAULT_PROJECT_ID;
113+
114+
this.expectedToken = 'env-token';
115+
this.expectedProject = 'env-proj';
116+
117+
process.env.SCW_SECRET_KEY = this.expectedToken;
118+
process.env.SCW_DEFAULT_PROJECT_ID = this.expectedProject;
119+
120+
this.checkCreds({});
121+
122+
process.env.SCW_SECRET_KEY = originalToken;
123+
process.env.SCW_DEFAULT_PROJECT_ID = originalProject;
124+
});
125+
126+
it('should read credentials from options if present', () => {
127+
let options = {};
128+
options['scw-token'] = 'opt-token';
129+
options['scw-project'] = 'opt-proj';
130+
131+
this.expectedToken = 'opt-token';
132+
this.expectedProject = 'opt-proj';
133+
134+
this.checkCreds(options);
135+
});
136+
});
137+

tests/utils/fs/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ function getTmpDirPath() {
1616
return path.join(tmpDirCommonPath, crypto.randomBytes(8).toString('hex'));
1717
}
1818

19+
function createTmpDir() {
20+
const tmpDir = getTmpDirPath();
21+
fs.mkdirSync(tmpDir, { recursive: true });
22+
return tmpDir;
23+
}
24+
1925
function replaceTextInFile(filePath, subString, newSubString) {
2026
const fileContent = fs.readFileSync(filePath).toString();
2127
fs.writeFileSync(filePath, fileContent.replace(subString, newSubString));
@@ -35,6 +41,7 @@ function writeYamlFile(filePath, content) {
3541
module.exports = {
3642
tmpDirCommonPath,
3743
getTmpDirPath,
44+
createTmpDir,
3845

3946
replaceTextInFile,
4047
readYamlFile,

0 commit comments

Comments
 (0)