зеркало из https://github.com/Azure/mysql.git
Make database parameter optional. (#22)
By making the database optional opens the scenario where we just want to issue commands on the server (eg: to create a database), - Fix: If the database name had spaces `mysql` call would fail - Pass the password as an environment variable instead of command line. Eliminates warning that password method passing is insecure.
This commit is contained in:
Родитель
afb936bc2c
Коммит
926671aa6a
|
@ -3,19 +3,9 @@ import AzureMySqlActionHelper from '../src/AzureMySqlActionHelper';
|
||||||
import MySqlConnectionStringBuilder from '../src/MySqlConnectionStringBuilder';
|
import MySqlConnectionStringBuilder from '../src/MySqlConnectionStringBuilder';
|
||||||
import AzureMySqlAction, { IActionInputs } from '../src/AzureMySqlAction';
|
import AzureMySqlAction, { IActionInputs } from '../src/AzureMySqlAction';
|
||||||
|
|
||||||
jest.mock('../src/MySqlConnectionStringBuilder', () => {
|
|
||||||
return jest.fn().mockImplementation(() => {
|
|
||||||
return {
|
|
||||||
server: 'testmysqlserver.mysql.database.azure.com',
|
|
||||||
userId: 'testuser@testmysqlserver',
|
|
||||||
password: 'testpassword',
|
|
||||||
database: 'testdb'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('AzureMySqlAction tests', () => {
|
describe('AzureMySqlAction tests', () => {
|
||||||
it('executes sql file on target database', async () => {
|
it('executes sql file on target database', async () => {
|
||||||
|
|
||||||
let inputs = getInputs();
|
let inputs = getInputs();
|
||||||
let action = new AzureMySqlAction(inputs);
|
let action = new AzureMySqlAction(inputs);
|
||||||
|
|
||||||
|
@ -26,7 +16,41 @@ describe('AzureMySqlAction tests', () => {
|
||||||
|
|
||||||
expect(getMySqlClientPathSpy).toHaveBeenCalledTimes(1);
|
expect(getMySqlClientPathSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(execSpy).toHaveBeenCalledTimes(1);
|
expect(execSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(execSpy).toHaveBeenCalledWith(`"mysql.exe" -h testmysqlserver.mysql.database.azure.com -D testdb -u testuser@testmysqlserver -t 10 -e "source ./testsqlfile.sql"`, ['--password=testpassword']);
|
expect(execSpy).toHaveBeenCalledWith(`"mysql.exe" -t 10`,
|
||||||
|
[
|
||||||
|
"-h", "testmysqlserver.mysql.database.azure.com",
|
||||||
|
"-u", "testuser@testmysqlserver",
|
||||||
|
"-e", "source ./testsqlfile.sql",
|
||||||
|
"-D", "testdb"
|
||||||
|
], {
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": "testpassword"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('executes sql file on target server', async () => {
|
||||||
|
|
||||||
|
let inputsNoDB = getInputsNoDatabase();
|
||||||
|
let action = new AzureMySqlAction(inputsNoDB);
|
||||||
|
|
||||||
|
let getMySqlClientPathSpy = jest.spyOn(AzureMySqlActionHelper, 'getMySqlClientPath').mockResolvedValue('mysql.exe');
|
||||||
|
let execSpy = jest.spyOn(exec, 'exec').mockResolvedValue(0);
|
||||||
|
|
||||||
|
await action.execute();
|
||||||
|
|
||||||
|
expect(getMySqlClientPathSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(execSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(execSpy).toHaveBeenCalledWith(`"mysql.exe" -t 10`,
|
||||||
|
[
|
||||||
|
"-h", "testmysqlserver.mysql.database.azure.com",
|
||||||
|
"-u", "testuser@testmysqlserver",
|
||||||
|
"-e", "source ./testsqlfile.sql"
|
||||||
|
], {
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": "testpassword"
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws if mysql client fails to execute', async () => {
|
it('throws if mysql client fails to execute', async () => {
|
||||||
|
@ -46,7 +70,16 @@ describe('AzureMySqlAction tests', () => {
|
||||||
function getInputs() {
|
function getInputs() {
|
||||||
return {
|
return {
|
||||||
serverName: 'testmysqlserver.mysql.database.azure.com',
|
serverName: 'testmysqlserver.mysql.database.azure.com',
|
||||||
connectionString: new MySqlConnectionStringBuilder('testmysqlserver.mysql.database.azure.com; Port=3306; Database=testdb; Uid=testuser@testmysqlserver; Pwd=testpassword; SslMode=Preferred'),
|
connectionString: new MySqlConnectionStringBuilder('Server=testmysqlserver.mysql.database.azure.com; Port=3306; Database=testdb; Uid=testuser@testmysqlserver; Pwd=testpassword; SslMode=Preferred'),
|
||||||
|
sqlFile: './testsqlfile.sql',
|
||||||
|
additionalArguments: '-t 10'
|
||||||
|
} as IActionInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInputsNoDatabase() {
|
||||||
|
return {
|
||||||
|
serverName: 'testmysqlserver.mysql.database.azure.com',
|
||||||
|
connectionString: new MySqlConnectionStringBuilder('Server=testmysqlserver.mysql.database.azure.com; Port=3306; Uid=testuser@testmysqlserver; Pwd=testpassword; SslMode=Preferred'),
|
||||||
sqlFile: './testsqlfile.sql',
|
sqlFile: './testsqlfile.sql',
|
||||||
additionalArguments: '-t 10'
|
additionalArguments: '-t 10'
|
||||||
} as IActionInputs;
|
} as IActionInputs;
|
||||||
|
|
|
@ -22,6 +22,20 @@ describe('MySqlConnectionStringBuilder tests', () => {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('validate no database specified', () => {
|
||||||
|
let validConnectionStrings = [
|
||||||
|
[`Server=testserver;User Id=user;Password=JustANormal123@#$password;`, 'validates values no db specified', `JustANormal123@#$password`]
|
||||||
|
];
|
||||||
|
|
||||||
|
it.each(validConnectionStrings)('Input `%s` %s', (connectionStringInput, testDescription, passwordOutput) => {
|
||||||
|
let connectionString = new MySqlConnectionStringBuilder(connectionStringInput);
|
||||||
|
|
||||||
|
expect(connectionString.connectionString).toMatch(connectionStringInput);
|
||||||
|
expect(connectionString.password).toMatch(passwordOutput);
|
||||||
|
expect(connectionString.userId).toMatch(`user`);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
describe('throw for invalid connection strings', () => {
|
describe('throw for invalid connection strings', () => {
|
||||||
let invalidConnectionStrings = [
|
let invalidConnectionStrings = [
|
||||||
[`Server=testserver;User Id=user;Password="ab'=abcdf''c;123;Database=testdb`, 'validates values beginning with double quotes but not ending with double quotes'],
|
[`Server=testserver;User Id=user;Password="ab'=abcdf''c;123;Database=testdb`, 'validates values beginning with double quotes but not ending with double quotes'],
|
||||||
|
|
|
@ -29,7 +29,20 @@ class AzureMySqlAction {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
core.debug('Begin executing action...');
|
core.debug('Begin executing action...');
|
||||||
let mySqlClientPath = yield AzureMySqlActionHelper_1.default.getMySqlClientPath();
|
let mySqlClientPath = yield AzureMySqlActionHelper_1.default.getMySqlClientPath();
|
||||||
yield exec.exec(`"${mySqlClientPath}" -h ${this._inputs.serverName} -D ${this._inputs.connectionString.database} -u ${this._inputs.connectionString.userId} ${this._inputs.additionalArguments} -e "source ${this._inputs.sqlFile}"`, [`--password=${this._inputs.connectionString.password}`]);
|
let parameters = [
|
||||||
|
"-h", this._inputs.serverName,
|
||||||
|
"-u", this._inputs.connectionString.userId,
|
||||||
|
"-e", `source ${this._inputs.sqlFile}`,
|
||||||
|
];
|
||||||
|
if (this._inputs.connectionString.database) {
|
||||||
|
parameters.push("-D");
|
||||||
|
parameters.push(this._inputs.connectionString.database);
|
||||||
|
}
|
||||||
|
yield exec.exec(`"${mySqlClientPath}" ${this._inputs.additionalArguments}`, parameters, {
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": this._inputs.connectionString.password
|
||||||
|
}
|
||||||
|
});
|
||||||
console.log('Successfully executed sql file on target database');
|
console.log('Successfully executed sql file on target database');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,8 +101,8 @@ class MySqlConnectionStringBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!parsedConnectionString.server || !parsedConnectionString.userId || !parsedConnectionString.password || !parsedConnectionString.database) {
|
if (!parsedConnectionString.server || !parsedConnectionString.userId || !parsedConnectionString.password) {
|
||||||
throw new Error(`Missing required keys in connection string. Please ensure that the keys 'Server', 'User Id', 'Password', 'Database' are provided in the connection string.`);
|
throw new Error(`Missing required keys in connection string. Please ensure that the keys 'Server', 'User Id', 'Password' are provided in the connection string.`);
|
||||||
}
|
}
|
||||||
return parsedConnectionString;
|
return parsedConnectionString;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,13 @@ class MySqlUtils {
|
||||||
try {
|
try {
|
||||||
core.debug(`Validating if client has access to MySql Server '${serverName}'.`);
|
core.debug(`Validating if client has access to MySql Server '${serverName}'.`);
|
||||||
core.debug(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`);
|
core.debug(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`);
|
||||||
yield exec.exec(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`, [`--password=${connectionString.password}`], {
|
yield exec.exec(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`, [], {
|
||||||
silent: true,
|
silent: true,
|
||||||
listeners: {
|
listeners: {
|
||||||
stderr: (data) => mySqlError += data.toString()
|
stderr: (data) => mySqlError += data.toString()
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": connectionString.password
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,26 @@ export default class AzureMySqlAction {
|
||||||
|
|
||||||
public async execute() {
|
public async execute() {
|
||||||
core.debug('Begin executing action...');
|
core.debug('Begin executing action...');
|
||||||
|
|
||||||
let mySqlClientPath = await AzureMySqlActionHelper.getMySqlClientPath();
|
let mySqlClientPath = await AzureMySqlActionHelper.getMySqlClientPath();
|
||||||
await exec.exec(`"${mySqlClientPath}" -h ${this._inputs.serverName} -D ${this._inputs.connectionString.database} -u ${this._inputs.connectionString.userId} ${this._inputs.additionalArguments} -e "source ${this._inputs.sqlFile}"`, [`--password=${this._inputs.connectionString.password}`]);
|
|
||||||
|
let parameters = [
|
||||||
|
"-h", this._inputs.serverName,
|
||||||
|
"-u", this._inputs.connectionString.userId,
|
||||||
|
"-e", `source ${this._inputs.sqlFile}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
if (this._inputs.connectionString.database) {
|
||||||
|
parameters.push("-D")
|
||||||
|
parameters.push(this._inputs.connectionString.database)
|
||||||
|
}
|
||||||
|
|
||||||
|
await exec.exec(`"${mySqlClientPath}" ${this._inputs.additionalArguments}`,
|
||||||
|
parameters, {
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": this._inputs.connectionString.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
console.log('Successfully executed sql file on target database');
|
console.log('Successfully executed sql file on target database');
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,8 +113,8 @@ export default class MySqlConnectionStringBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parsedConnectionString.server || !parsedConnectionString.userId || !parsedConnectionString.password || !parsedConnectionString.database) {
|
if (!parsedConnectionString.server || !parsedConnectionString.userId || !parsedConnectionString.password) {
|
||||||
throw new Error(`Missing required keys in connection string. Please ensure that the keys 'Server', 'User Id', 'Password', 'Database' are provided in the connection string.`);
|
throw new Error(`Missing required keys in connection string. Please ensure that the keys 'Server', 'User Id', 'Password' are provided in the connection string.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedConnectionString;
|
return parsedConnectionString;
|
||||||
|
|
|
@ -11,10 +11,13 @@ export default class MySqlUtils {
|
||||||
try {
|
try {
|
||||||
core.debug(`Validating if client has access to MySql Server '${serverName}'.`);
|
core.debug(`Validating if client has access to MySql Server '${serverName}'.`);
|
||||||
core.debug(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`);
|
core.debug(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`);
|
||||||
await exec.exec(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`, [`--password=${connectionString.password}`], {
|
await exec.exec(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`, [], {
|
||||||
silent: true,
|
silent: true,
|
||||||
listeners: {
|
listeners: {
|
||||||
stderr: (data: Buffer) => mySqlError += data.toString()
|
stderr: (data: Buffer) => mySqlError += data.toString()
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
"MYSQL_PWD": connectionString.password
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче