Skip to content

Commit

Permalink
Add support for MySQL flexible server
Browse files Browse the repository at this point in the history
  • Loading branch information
alanenriqueo committed Aug 4, 2022
1 parent d3780aa commit 650d5a7
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 46 deletions.
218 changes: 218 additions & 0 deletions __tests__/AzureMySqlResourceManager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import * as core from '@actions/core';
import { IAuthorizer } from 'azure-actions-webclient/Authorizer/IAuthorizer';
import { WebRequest, WebResponse } from 'azure-actions-webclient/WebClient';
import { ServiceClient as AzureRestClient, ToError, AzureError } from 'azure-actions-webclient/AzureRestClient';

import AzureMySqlResourceManager, { FirewallRule } from '../src/AzureMySqlResourceManager';
import FirewallManager from '../src/FirewallManager';

jest.mock('azure-actions-webclient/AzureRestClient');

describe('AzureMySqlResourceManager tests', () => {
it('initializes resource manager correctly for single server', async () => {
let getRequestUrlSpy = jest.spyOn(AzureRestClient.prototype, 'getRequestUri').mockReturnValue('https://randomUrl/');
let beginRequestSpy = jest.spyOn(AzureRestClient.prototype, 'beginRequest').mockResolvedValue({
statusCode: 200,
body: {
value: [
{
name: 'testServer',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer'
},
{
name: 'testServer2',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer2'
}
]
},
statusMessage: 'OK',
headers: []
});

let resourceManager = await AzureMySqlResourceManager.getResourceManager('testServer', {} as IAuthorizer);
let server = resourceManager.getMySqlServer();

expect(server!.name).toEqual('testServer');
expect(server!.id).toEqual('/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer');
expect(getRequestUrlSpy).toHaveBeenCalledTimes(1);
expect(beginRequestSpy).toHaveBeenCalledTimes(1);
});

it('initializes resource manager correctly for flexible server', async () => {
let getRequestUrlSpy = jest.spyOn(AzureRestClient.prototype, 'getRequestUri').mockReturnValue('https://randomUrl/');
let beginRequestSpy = jest.spyOn(AzureRestClient.prototype, 'beginRequest').mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer'
},
{
name: 'testServer2',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer2'
}
]
},
statusMessage: 'OK',
headers: []
}).mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer3',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer3'
},
{
name: 'testServer4',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer4'
}
]
},
statusMessage: 'OK',
headers: []
});

let resourceManager = await AzureMySqlResourceManager.getResourceManager('testServer3', {} as IAuthorizer);
let server = resourceManager.getMySqlServer();

expect(server!.name).toEqual('testServer3');
expect(server!.id).toEqual('/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer3');
expect(getRequestUrlSpy).toHaveBeenCalledTimes(2);
expect(beginRequestSpy).toHaveBeenCalledTimes(2);
})

it('throws if resource manager fails to initialize', async () => {
let getRequestUrlSpy = jest.spyOn(AzureRestClient.prototype, 'getRequestUri').mockReturnValue('https://randomUrl/');
let beginRequestSpy = jest.spyOn(AzureRestClient.prototype, 'beginRequest').mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer1',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer1'
},
{
name: 'testServer2',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer2'
}
]
},
statusMessage: 'OK',
headers: []
}).mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer3',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/flexibleServers/testServer3'
},
{
name: 'testServer4',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/flexibleServers/testServer4'
}
]
},
statusMessage: 'OK',
headers: []
});

let expectedError = `Unable to get details of MySQL server testServer. MySQL server 'testServer' was not found in the subscription.`;
try {
await AzureMySqlResourceManager.getResourceManager('testServer', {} as IAuthorizer);
} catch(error) {
expect(error.message).toEqual(expectedError);
}
expect(getRequestUrlSpy).toHaveBeenCalledTimes(2);
expect(beginRequestSpy).toHaveBeenCalledTimes(2);
});

it('adds firewall rule successfully', async () => {
let getRequestUrlSpy = jest.spyOn(AzureRestClient.prototype, 'getRequestUri').mockReturnValue('https://randomUrl/');
let beginRequestSpy = jest.spyOn(AzureRestClient.prototype, 'beginRequest').mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer'
},
{
name: 'testServer2',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer2'
}
]
},
statusMessage: 'OK',
headers: []
}).mockResolvedValueOnce({
statusCode: 202,
body: {},
statusMessage: 'OK',
headers: {
'azure-asyncoperation': 'http://asyncRedirectionURI'
}
}).mockResolvedValueOnce({
statusCode: 200,
body: {
'status': 'Succeeded'
},
statusMessage: 'OK',
headers: {}
});;

let getFirewallRuleSpy = jest.spyOn(AzureMySqlResourceManager.prototype, 'getFirewallRule').mockResolvedValue({
name: 'FirewallRule'
} as FirewallRule);

let resourceManager = await AzureMySqlResourceManager.getResourceManager('testServer', {} as IAuthorizer);
await resourceManager.addFirewallRule('0.0.0.0', '1.1.1.1');

expect(getRequestUrlSpy).toHaveBeenCalledTimes(2);
expect(beginRequestSpy).toHaveBeenCalledTimes(3);
expect(getFirewallRuleSpy).toHaveBeenCalledTimes(1);
});

it('removes firewall rule successfully', async () => {
let getRequestUrlSpy = jest.spyOn(AzureRestClient.prototype, 'getRequestUri').mockReturnValue('https://randomUrl/');
let beginRequestSpy = jest.spyOn(AzureRestClient.prototype, 'beginRequest').mockResolvedValueOnce({
statusCode: 200,
body: {
value: [
{
name: 'testServer',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer'
},
{
name: 'testServer2',
id: '/subscriptions/SubscriptionId/resourceGroups/testrg/providers/Microsoft.DBforMySQL/servers/testServer2'
}
]
},
statusMessage: 'OK',
headers: []
}).mockResolvedValueOnce({
statusCode: 202,
body: {},
statusMessage: 'OK',
headers: {
'azure-asyncoperation': 'http://asyncRedirectionURI'
}
}).mockResolvedValueOnce({
statusCode: 200,
body: {
'status': 'Succeeded'
},
statusMessage: 'OK',
headers: {}
});

let resourceManager = await AzureMySqlResourceManager.getResourceManager('testServer', {} as IAuthorizer);
await resourceManager.removeFirewallRule({ name: 'FirewallRule' } as FirewallRule);

expect(getRequestUrlSpy).toHaveBeenCalledTimes(2);
expect(beginRequestSpy).toHaveBeenCalledTimes(3);
});
})
46 changes: 26 additions & 20 deletions lib/AzureMySqlResourceManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class AzureMySqlResourceManager {
let firewallRuleName = `ClientIPAddress_${Date.now()}`;
let httpRequest = {
method: 'PUT',
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${firewallRuleName}`, {}, [], '2017-12-01'),
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${firewallRuleName}`, {}, [], this._apiVersion),
body: JSON.stringify({
'properties': {
'startIpAddress': startIpAddress,
Expand Down Expand Up @@ -88,7 +88,7 @@ class AzureMySqlResourceManager {
return __awaiter(this, void 0, void 0, function* () {
let httpRequest = {
method: 'DELETE',
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${firewallRule.name}`, {}, [], '2017-12-01')
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${firewallRule.name}`, {}, [], this._apiVersion)
};
try {
let httpResponse = yield this._restClient.beginRequest(httpRequest);
Expand Down Expand Up @@ -117,7 +117,7 @@ class AzureMySqlResourceManager {
return __awaiter(this, void 0, void 0, function* () {
let httpRequest = {
method: 'GET',
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${ruleName}`, {}, [], '2017-12-01')
uri: this._restClient.getRequestUri(`/${this._resource.id}/firewallRules/${ruleName}`, {}, [], this._apiVersion)
};
try {
let httpResponse = yield this._restClient.beginRequest(httpRequest);
Expand Down Expand Up @@ -161,31 +161,27 @@ class AzureMySqlResourceManager {
return response;
});
}
_populateMySqlServerData(serverName) {
_getMySqlServer(serverType, apiVersion, serverName) {
return __awaiter(this, void 0, void 0, function* () {
// trim the cloud hostname suffix from servername
serverName = serverName.split('.')[0];
let httpRequest = {
const httpRequest = {
method: 'GET',
uri: this._restClient.getRequestUri('//subscriptions/{subscriptionId}/providers/Microsoft.DBforMySQL/servers', {}, [], '2017-12-01')
uri: this._restClient.getRequestUri(`//subscriptions/{subscriptionId}/providers/Microsoft.DBforMySQL/${serverType}`, {}, [], apiVersion)
};
core.debug(`Get MySQL server '${serverName}' details`);
core.debug(`Get '${serverName}' for MySQL ${serverType} details`);
try {
let httpResponse = yield this._restClient.beginRequest(httpRequest);
const httpResponse = yield this._restClient.beginRequest(httpRequest);
if (httpResponse.statusCode !== 200) {
throw AzureRestClient_1.ToError(httpResponse);
}
let sqlServers = httpResponse.body && httpResponse.body.value;
if (sqlServers && sqlServers.length > 0) {
this._resource = sqlServers.filter((sqlResource) => sqlResource.name.toLowerCase() === serverName.toLowerCase())[0];
if (!this._resource) {
throw new Error(`Unable to get details of MySQL server ${serverName}. MySql server '${serverName}' was not found in the subscription.`);
}
core.debug(JSON.stringify(this._resource));
}
else {
throw new Error(`Unable to get details of MySQL server ${serverName}. No MySQL servers were found in the subscription.`);
const sqlServers = ((httpResponse.body && httpResponse.body.value) || []);
const sqlServer = sqlServers.find((sqlResource) => sqlResource.name.toLowerCase() === serverName.toLowerCase());
if (sqlServer) {
this._serverType = serverType;
this._apiVersion = apiVersion;
this._resource = sqlServer;
return true;
}
return false;
}
catch (error) {
if (error instanceof AzureRestClient_1.AzureError) {
Expand All @@ -195,6 +191,16 @@ class AzureMySqlResourceManager {
}
});
}
_populateMySqlServerData(serverName) {
return __awaiter(this, void 0, void 0, function* () {
// trim the cloud hostname suffix from servername
serverName = serverName.split('.')[0];
(yield this._getMySqlServer('servers', '2017-12-01', serverName)) || (yield this._getMySqlServer('flexibleServers', '2021-05-01', serverName));
if (!this._resource) {
throw new Error(`Unable to get details of MySQL server ${serverName}. MySQL server '${serverName}' was not found in the subscription.`);
}
});
}
_sleep(sleepDurationInSeconds) {
return new Promise((resolve) => {
setTimeout(resolve, sleepDurationInSeconds * 1000);
Expand Down
5 changes: 3 additions & 2 deletions lib/MySqlUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ class MySqlUtils {
let ipAddress = '';
let mySqlError = '';
try {
const command = `"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases" --connect-timeout 10`;
core.debug(`Validating if client has access to MySql Server '${serverName}'.`);
core.debug(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`);
yield exec.exec(`"${mySqlClientPath}" -h ${serverName} -u "${connectionString.userId}" -e "show databases"`, [], {
core.debug(command);
yield exec.exec(command, [], {
silent: true,
listeners: {
stderr: (data) => mySqlError += data.toString()
Expand Down
Loading

0 comments on commit 650d5a7

Please sign in to comment.