Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions provider/scalewayProvider.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
'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');
Expand All @@ -9,6 +14,8 @@ const { DEFAULT_REGION } = require('../shared/constants');
const providerName = 'scaleway';

class ScalewayProvider {
static scwConfigFile = path.join(os.homedir(), ".config", "scw", "config.yaml");

static getProviderName() {
return providerName;
}
Expand Down Expand Up @@ -39,37 +46,67 @@ class ScalewayProvider {

setCredentials(options) {
// On serverless info command we do not want log pollution from authentication.
// This is necessary to use it in automated environment.
// This is necessary to use it in an automated environment.
let hideLog = false;
if (this.serverless.configurationInput.service && this.serverless.configurationInput.service === 'serverlessInfo') {
if (this.serverless.configurationInput.service &&
this.serverless.configurationInput.service === 'serverlessInfo') {
hideLog = true;
}

if (options['scw-token'] && options['scw-project']) {
if (!hideLog) {
this.serverless.cli.log('Using credentials from command line parameters');
}

this.scwToken = options['scw-token'];
this.scwProject = options['scw-project'];

} else if (process.env.SCW_SECRET_KEY && process.env.SCW_DEFAULT_PROJECT_ID) {
if (!hideLog) {
this.serverless.cli.log('Using credentials from system environment');
}

this.scwToken = process.env.SCW_SECRET_KEY;
this.scwProject = process.env.SCW_DEFAULT_PROJECT_ID;

} else if (process.env.SCW_TOKEN && process.env.SCW_PROJECT) {
if (!hideLog) {
this.serverless.cli.log('Using credentials from system environment');
this.serverless.cli.log('NOTICE: you are using deprecated environment variable notation,');
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 if (this.serverless.service.provider.scwToken ||
this.serverless.service.provider.scwProject) {
if (!hideLog) {
this.serverless.cli.log('Using credentials from serverless.yml');
}

this.scwToken = this.serverless.service.provider.scwToken;
this.scwProject = this.serverless.service.provider.scwProject;

} else if (fs.existsSync(ScalewayProvider.scwConfigFile)) {
if (!hideLog) {
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 {
if (!hideLog) {
this.serverless.cli.log('Using credentials from yml');
this.serverless.cli.log('Unable to locate Scaleway provider credentials');
}
this.scwToken = this.serverless.service.provider.scwToken || '';
this.scwProject = this.serverless.service.provider.scwProject || '';

this.scwToken = '';
this.scwProject = '';
}
}

Expand Down
137 changes: 137 additions & 0 deletions tests/provider/scalewayProvider.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const { expect } = require('chai');
const { expect: jestExpect } = require('@jest/globals');

const fs = require('fs');
const os = require('os');
const path = require('path');

const ScalewayProvider = require('../../provider/scalewayProvider');
const { createTmpDir } = require('../utils/fs');

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.serverless = new MockServerless();
this.prov = new ScalewayProvider(this.serverless);

beforeAll(() => {
// Override scw config file location
this.dummyScwConfigDir = createTmpDir();
this.dummyScwConfigPath = path.join(this.dummyScwConfigDir, 'config.yml');

ScalewayProvider.scwConfigFile = this.dummyScwConfigPath;
});

afterAll(() => {
// 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) => {
// 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);
};

// -------------------------------------
// 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 = '';
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({});
});

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 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);
});
});

7 changes: 7 additions & 0 deletions tests/utils/fs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -35,6 +41,7 @@ function writeYamlFile(filePath, content) {
module.exports = {
tmpDirCommonPath,
getTmpDirPath,
createTmpDir,

replaceTextInFile,
readYamlFile,
Expand Down