Родитель
dccd92cdb1
Коммит
0d17dff9ee
|
@ -15,7 +15,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Run L0 tests.
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
npm install
|
||||
npm test
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# dependencies
|
||||
/node_modules
|
||||
coverage
|
||||
|
||||
# Transpiled JS
|
||||
lib/
|
||||
/lib
|
||||
|
|
28
action.yml
28
action.yml
|
@ -1,24 +1,24 @@
|
|||
name: 'Azure Kubernetes set context'
|
||||
description: 'Sets the kubeconfig on the machine to communicate with the Azure Kubernetes cluster. Github.com/Azure/Actions'
|
||||
inputs:
|
||||
name: "Azure Kubernetes set context"
|
||||
description: "Sets the kubeconfig on the machine to communicate with the Azure Kubernetes cluster. Github.com/Azure/Actions"
|
||||
inputs:
|
||||
creds:
|
||||
description: 'Azure credentials i.e. output of `az ad sp create-for-rbac --sdk-auth`'
|
||||
description: "Azure credentials i.e. output of `az ad sp create-for-rbac --sdk-auth`"
|
||||
required: true
|
||||
default: ''
|
||||
default: ""
|
||||
resource-group:
|
||||
description: 'Resource Group Name'
|
||||
description: "Resource Group Name"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
cluster-name:
|
||||
description: 'AKS Cluster Name'
|
||||
description: "AKS Cluster Name"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
subscription-id:
|
||||
description: 'Subscription ID'
|
||||
description: "Subscription ID"
|
||||
required: false
|
||||
default: ''
|
||||
default: ""
|
||||
branding:
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
color: "green" # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'lib/login.js'
|
||||
using: "node12"
|
||||
main: "lib/run.js"
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
import * as login from './login'
|
||||
import * as core from '@actions/core';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as azureActionsUtil from "@azure-actions/utilities/lib/http";
|
||||
|
||||
describe('Testing all functions in login file.', () => {
|
||||
test('getAKSKubeconfig() - get kubeconfig from aks API and return it', async () => {
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
});
|
||||
const response = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'properties': {
|
||||
'kubeConfig': Buffer.from('###').toString('base64')
|
||||
}
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockResolvedValue(response);
|
||||
|
||||
expect(await login.getAKSKubeconfig('<access_token>', '<subscription id>', 'https://management.azure.com/')).toBe('###');
|
||||
const request = {
|
||||
'body': '',
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': 'Bearer <access_token>',
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
'uri': 'https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31'
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test('getAKSKubeconfig() - reject if response not in expected format', async () => {
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
});
|
||||
const response = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'error': 'ErrorMessage'
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockResolvedValue(response);
|
||||
|
||||
await login.getAKSKubeconfig('<access_token>', '<subscription id>', 'https://management.azure.com/')
|
||||
.then(response => expect(response).toBeUndefined())
|
||||
.catch(error => expect(error).toBe(JSON.stringify(response.body)));
|
||||
const request = {
|
||||
'body': '',
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': 'Bearer <access_token>',
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
'uri': 'https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31'
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test('getAKSKubeconfig() - reject if error recieved', async () => {
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
});
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockRejectedValue('ErrorMessage');
|
||||
|
||||
await login.getAKSKubeconfig('<access_token>', '<subscription id>', 'https://management.azure.com/')
|
||||
.then(response => expect(response).toBeUndefined())
|
||||
.catch(error => expect(error).toBe('ErrorMessage'));
|
||||
const request = {
|
||||
'body': '',
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': 'Bearer <access_token>',
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
'uri': 'https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31'
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test('getKubeconfig() - reject if incorrect credentials', async () => {
|
||||
jest.spyOn(core, 'getInput').mockReturnValue('Wrong cred.')
|
||||
|
||||
await expect(login.getKubeconfig()).rejects.toThrow('Credentials object is not a valid JSON');
|
||||
});
|
||||
|
||||
test('getKubeconfig() - get access token, use it to get kubeconfig and return it ', async () => {
|
||||
const creds = {
|
||||
"clientId": "<client id>",
|
||||
"clientSecret": "<client secret>",
|
||||
"subscriptionId": "<subscription id>",
|
||||
"tenantId": "<tenant id>",
|
||||
"activeDirectoryEndpointUrl": "https://login.k8s.microsoftonline.com",
|
||||
"resourceManagerEndpointUrl": "https://management.k8s.azure.com/",
|
||||
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
|
||||
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
|
||||
"galleryEndpointUrl": "https://gallery.azure.com/",
|
||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||
}
|
||||
const responseKube = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'properties': {
|
||||
'kubeConfig': Buffer.from('###').toString('base64')
|
||||
}
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'access_token': '<access_token>'
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
if (inputName == 'creds') return JSON.stringify(creds);
|
||||
});
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockResolvedValueOnce(responseLogin).mockResolvedValueOnce(responseKube);
|
||||
|
||||
await login.getKubeconfig()
|
||||
.then(response => expect(response).toBe('###'))
|
||||
.catch(error => expect(error).toBeUndefined());
|
||||
const requestForToken = {
|
||||
method: 'POST',
|
||||
uri: 'https://login.k8s.microsoftonline.com/<tenant id>/oauth2/token/',
|
||||
body: 'resource=https%3A%2F%2Fmanagement.k8s.azure.com%2F&client_id=%3Cclient%20id%3E&grant_type=client_credentials&client_secret=%3Cclient%20secret%3E',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
|
||||
}
|
||||
} as azureActionsUtil.WebRequest;
|
||||
let optionsForToken = {
|
||||
retriableStatusCodes: [400, 408, 409, 500, 502, 503, 504],
|
||||
} as azureActionsUtil.WebRequestOptions;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForToken, optionsForToken);
|
||||
const requestForKubecongig = {
|
||||
'body': '',
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': 'Bearer <access_token>',
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
'uri': 'https://management.k8s.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31'
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForKubecongig);
|
||||
});
|
||||
|
||||
test('getKubeconfig() - use default endpoints if not provided in creds', async () => {
|
||||
const creds = {
|
||||
"clientId": "<client id>",
|
||||
"clientSecret": "<client secret>",
|
||||
"subscriptionId": "<subscription id>",
|
||||
"tenantId": "<tenant id>",
|
||||
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
|
||||
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
|
||||
"galleryEndpointUrl": "https://gallery.azure.com/",
|
||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||
}
|
||||
const responseKube = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'properties': {
|
||||
'kubeConfig': Buffer.from('###').toString('base64')
|
||||
}
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'access_token': '<access_token>'
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
if (inputName == 'creds') return JSON.stringify(creds);
|
||||
});
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockResolvedValueOnce(responseLogin).mockResolvedValueOnce(responseKube);
|
||||
|
||||
await login.getKubeconfig()
|
||||
.then(response => expect(response).toBe('###'))
|
||||
.catch(error => expect(error).toBeUndefined());
|
||||
const requestForToken = {
|
||||
method: 'POST',
|
||||
uri: 'https://login.microsoftonline.com/<tenant id>/oauth2/token/',
|
||||
body: 'resource=https%3A%2F%2Fmanagement.azure.com%2F&client_id=%3Cclient%20id%3E&grant_type=client_credentials&client_secret=%3Cclient%20secret%3E',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
|
||||
}
|
||||
} as azureActionsUtil.WebRequest;
|
||||
let optionsForToken = {
|
||||
retriableStatusCodes: [400, 408, 409, 500, 502, 503, 504],
|
||||
} as azureActionsUtil.WebRequestOptions;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForToken, optionsForToken);
|
||||
const requestForKubecongig = {
|
||||
'body': '',
|
||||
'method': 'GET',
|
||||
'headers': {
|
||||
'Authorization': 'Bearer <access_token>',
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
},
|
||||
'uri': 'https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31'
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForKubecongig);
|
||||
});
|
||||
|
||||
test('run() - create kubeconfig, export variable and give appropriate access', async () => {
|
||||
jest.spyOn(fs, 'writeFileSync').mockImplementation();
|
||||
jest.spyOn(fs, 'chmodSync').mockImplementation();
|
||||
jest.spyOn(core, 'exportVariable').mockImplementation();
|
||||
jest.spyOn(console, 'log').mockImplementation();
|
||||
jest.spyOn(core, 'debug').mockImplementation();
|
||||
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
|
||||
if (inputName == 'resource-group') return 'sample-rg';
|
||||
if (inputName == 'cluster-name') return 'testing';
|
||||
if (inputName == 'creds') return JSON.stringify(creds);
|
||||
});
|
||||
const creds = {
|
||||
"clientId": "<client id>",
|
||||
"clientSecret": "<client secret>",
|
||||
"subscriptionId": "<subscription id>",
|
||||
"tenantId": "<tenant id>",
|
||||
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
|
||||
"resourceManagerEndpointUrl": "https://management.azure.com/",
|
||||
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
|
||||
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
|
||||
"galleryEndpointUrl": "https://gallery.azure.com/",
|
||||
"managementEndpointUrl": "https://management.core.windows.net/"
|
||||
}
|
||||
const responseKube = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'properties': {
|
||||
'kubeConfig': Buffer.from('###').toString('base64')
|
||||
}
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
'statusCode': 200,
|
||||
'body': {
|
||||
'access_token': '<access_token>'
|
||||
}
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(azureActionsUtil, 'sendRequest').mockResolvedValueOnce(responseLogin).mockResolvedValueOnce(responseKube);
|
||||
process.env['RUNNER_TEMP'] = 'tempDirPath'
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
|
||||
|
||||
expect(await login.run());
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '###');
|
||||
expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600');
|
||||
expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567'));
|
||||
});
|
||||
});
|
57
src/login.ts
57
src/login.ts
|
@ -1,57 +0,0 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { WebRequest, WebResponse, sendRequest } from "@azure-actions/utilities/lib/http";
|
||||
import { getAzureAccessToken } from '@azure-actions/auth';
|
||||
|
||||
export function getAKSKubeconfig(azureSessionToken: string, subscriptionId: string, managementEndpointUrl: string): Promise<string> {
|
||||
const resourceGroupName = core.getInput('resource-group', { required: true });
|
||||
const clusterName = core.getInput('cluster-name', { required: true });
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
var webRequest = new WebRequest();
|
||||
webRequest.method = 'GET';
|
||||
webRequest.uri = `${managementEndpointUrl}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ContainerService/managedClusters/${clusterName}/accessProfiles/clusterAdmin?api-version=2017-08-31`;
|
||||
webRequest.headers = {
|
||||
'Authorization': 'Bearer ' + azureSessionToken,
|
||||
'Content-Type': 'application/json; charset=utf-8'
|
||||
}
|
||||
sendRequest(webRequest).then((response: WebResponse) => {
|
||||
const accessProfile = response.body;
|
||||
if (accessProfile.properties && accessProfile.properties.kubeConfig) {
|
||||
var kubeconfig = Buffer.from(accessProfile.properties.kubeConfig, 'base64');
|
||||
resolve(kubeconfig.toString());
|
||||
} else {
|
||||
reject(JSON.stringify(response.body));
|
||||
}
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getKubeconfig(): Promise<string> {
|
||||
const creds = core.getInput('creds', { required: true });
|
||||
let credsObject: { [key: string]: string; };
|
||||
try {
|
||||
credsObject = JSON.parse(creds);
|
||||
} catch (ex) {
|
||||
throw new Error('Credentials object is not a valid JSON');
|
||||
}
|
||||
|
||||
const managementEndpointUrl = credsObject["resourceManagerEndpointUrl"] || "https://management.azure.com/";
|
||||
const subscriptionId = core.getInput('subscription-id') || credsObject["subscriptionId"];
|
||||
const azureSessionToken = await getAzureAccessToken(creds);
|
||||
const kubeconfig = await getAKSKubeconfig(azureSessionToken, subscriptionId, managementEndpointUrl);
|
||||
return kubeconfig;
|
||||
}
|
||||
|
||||
export async function run() {
|
||||
const kubeconfig = await getKubeconfig();
|
||||
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated
|
||||
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`);
|
||||
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`);
|
||||
fs.writeFileSync(kubeconfigPath, kubeconfig);
|
||||
fs.chmodSync(kubeconfigPath, '600');
|
||||
core.exportVariable('KUBECONFIG', kubeconfigPath);
|
||||
core.debug('KUBECONFIG environment variable is set');
|
||||
}
|
||||
|
||||
run().catch(core.setFailed);
|
|
@ -0,0 +1,292 @@
|
|||
import * as login from "./run";
|
||||
import * as core from "@actions/core";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import * as azureActionsUtil from "@azure-actions/utilities/lib/http";
|
||||
|
||||
describe("Testing all functions in login file.", () => {
|
||||
test("getAKSKubeconfig() - get kubeconfig from aks API and return it", async () => {
|
||||
jest.spyOn(core, "getInput").mockImplementation((inputName, options) => {
|
||||
if (inputName == "resource-group") return "sample-rg";
|
||||
if (inputName == "cluster-name") return "testing";
|
||||
});
|
||||
const response = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
properties: {
|
||||
kubeConfig: Buffer.from("###").toString("base64"),
|
||||
},
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(azureActionsUtil, "sendRequest").mockResolvedValue(response);
|
||||
|
||||
expect(
|
||||
await login.getAKSKubeconfig(
|
||||
"<access_token>",
|
||||
"<subscription id>",
|
||||
"https://management.azure.com/",
|
||||
"sample-rg",
|
||||
"testing"
|
||||
)
|
||||
).toBe("###");
|
||||
const request = {
|
||||
body: "",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
uri: "https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31",
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test("getAKSKubeconfig() - reject if response not in expected format", async () => {
|
||||
const response = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
error: "ErrorMessage",
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest.spyOn(azureActionsUtil, "sendRequest").mockResolvedValue(response);
|
||||
|
||||
await login
|
||||
.getAKSKubeconfig(
|
||||
"<access_token>",
|
||||
"<subscription id>",
|
||||
"https://management.azure.com/",
|
||||
"sample-rg",
|
||||
"testing"
|
||||
)
|
||||
.then((response) => expect(response).toBeUndefined())
|
||||
.catch((error) => expect(error).toBe(JSON.stringify(response.body)));
|
||||
const request = {
|
||||
body: "",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
uri: "https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31",
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test("getAKSKubeconfig() - reject if error recieved", async () => {
|
||||
jest
|
||||
.spyOn(azureActionsUtil, "sendRequest")
|
||||
.mockRejectedValue("ErrorMessage");
|
||||
|
||||
await login
|
||||
.getAKSKubeconfig(
|
||||
"<access_token>",
|
||||
"<subscription id>",
|
||||
"https://management.azure.com/",
|
||||
"sample-rg",
|
||||
"testing"
|
||||
)
|
||||
.then((response) => expect(response).toBeUndefined())
|
||||
.catch((error) => expect(error).toBe("ErrorMessage"));
|
||||
const request = {
|
||||
body: "",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
uri: "https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31",
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(request);
|
||||
});
|
||||
|
||||
test("getKubeconfig() - reject if incorrect credentials", async () => {
|
||||
jest.spyOn(core, "getInput").mockReturnValue("Wrong cred.");
|
||||
|
||||
await expect(
|
||||
login.getKubeconfig({}, "subscriptionId", "sample-rg", "testing")
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("getKubeconfig() - get access token, use it to get kubeconfig and return it ", async () => {
|
||||
const creds = {
|
||||
clientId: "<client id>",
|
||||
clientSecret: "<client secret>",
|
||||
subscriptionId: "<subscription id>",
|
||||
tenantId: "<tenant id>",
|
||||
activeDirectoryEndpointUrl: "https://login.k8s.microsoftonline.com",
|
||||
resourceManagerEndpointUrl: "https://management.k8s.azure.com/",
|
||||
activeDirectoryGraphResourceId: "https://graph.windows.net/",
|
||||
sqlManagementEndpointUrl: "https://management.core.windows.net:8443/",
|
||||
galleryEndpointUrl: "https://gallery.azure.com/",
|
||||
managementEndpointUrl: "https://management.core.windows.net/",
|
||||
};
|
||||
const responseKube = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
properties: {
|
||||
kubeConfig: Buffer.from("###").toString("base64"),
|
||||
},
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
access_token: "<access_token>",
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest
|
||||
.spyOn(azureActionsUtil, "sendRequest")
|
||||
.mockResolvedValueOnce(responseLogin)
|
||||
.mockResolvedValueOnce(responseKube);
|
||||
|
||||
await login
|
||||
.getKubeconfig(creds, creds["subscriptionId"], "sample-rg", "testing")
|
||||
.then((response) => expect(response).toBe("###"))
|
||||
.catch((error) => expect(error).toBeUndefined());
|
||||
const requestForToken = {
|
||||
method: "POST",
|
||||
uri: "https://login.k8s.microsoftonline.com/<tenant id>/oauth2/token/",
|
||||
body: "resource=https%3A%2F%2Fmanagement.k8s.azure.com%2F&client_id=%3Cclient%20id%3E&grant_type=client_credentials&client_secret=%3Cclient%20secret%3E",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
||||
},
|
||||
} as azureActionsUtil.WebRequest;
|
||||
let optionsForToken = {
|
||||
retriableStatusCodes: [400, 408, 409, 500, 502, 503, 504],
|
||||
} as azureActionsUtil.WebRequestOptions;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(
|
||||
requestForToken,
|
||||
optionsForToken
|
||||
);
|
||||
const requestForKubecongig = {
|
||||
body: "",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
uri: "https://management.k8s.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31",
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForKubecongig);
|
||||
});
|
||||
|
||||
test("getKubeconfig() - use default endpoints if not provided in creds", async () => {
|
||||
const creds = {
|
||||
clientId: "<client id>",
|
||||
clientSecret: "<client secret>",
|
||||
subscriptionId: "<subscription id>",
|
||||
tenantId: "<tenant id>",
|
||||
activeDirectoryGraphResourceId: "https://graph.windows.net/",
|
||||
sqlManagementEndpointUrl: "https://management.core.windows.net:8443/",
|
||||
galleryEndpointUrl: "https://gallery.azure.com/",
|
||||
managementEndpointUrl: "https://management.core.windows.net/",
|
||||
};
|
||||
const responseKube = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
properties: {
|
||||
kubeConfig: Buffer.from("###").toString("base64"),
|
||||
},
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
access_token: "<access_token>",
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest
|
||||
.spyOn(azureActionsUtil, "sendRequest")
|
||||
.mockResolvedValueOnce(responseLogin)
|
||||
.mockResolvedValueOnce(responseKube);
|
||||
|
||||
await login
|
||||
.getKubeconfig(creds, creds["subscriptionId"], "sample-rg", "testing")
|
||||
.then((response) => expect(response).toBe("###"))
|
||||
.catch((error) => expect(error).toBeUndefined());
|
||||
const requestForToken = {
|
||||
method: "POST",
|
||||
uri: "https://login.microsoftonline.com/<tenant id>/oauth2/token/",
|
||||
body: "resource=https%3A%2F%2Fmanagement.azure.com%2F&client_id=%3Cclient%20id%3E&grant_type=client_credentials&client_secret=%3Cclient%20secret%3E",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
||||
},
|
||||
} as azureActionsUtil.WebRequest;
|
||||
let optionsForToken = {
|
||||
retriableStatusCodes: [400, 408, 409, 500, 502, 503, 504],
|
||||
} as azureActionsUtil.WebRequestOptions;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(
|
||||
requestForToken,
|
||||
optionsForToken
|
||||
);
|
||||
const requestForKubecongig = {
|
||||
body: "",
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
},
|
||||
uri: "https://management.azure.com//subscriptions/<subscription id>/resourceGroups/sample-rg/providers/Microsoft.ContainerService/managedClusters/testing/accessProfiles/clusterAdmin?api-version=2017-08-31",
|
||||
} as azureActionsUtil.WebRequest;
|
||||
expect(azureActionsUtil.sendRequest).toBeCalledWith(requestForKubecongig);
|
||||
});
|
||||
|
||||
test("run() - create kubeconfig, export variable and give appropriate access", async () => {
|
||||
jest.spyOn(fs, "writeFileSync").mockImplementation();
|
||||
jest.spyOn(fs, "chmodSync").mockImplementation();
|
||||
jest.spyOn(core, "exportVariable").mockImplementation();
|
||||
jest.spyOn(console, "log").mockImplementation();
|
||||
jest.spyOn(core, "debug").mockImplementation();
|
||||
jest.spyOn(core, "getInput").mockImplementation((inputName, options) => {
|
||||
if (inputName == "resource-group") return "sample-rg";
|
||||
if (inputName == "cluster-name") return "testing";
|
||||
if (inputName == "creds") return JSON.stringify(creds);
|
||||
});
|
||||
const creds = {
|
||||
clientId: "<client id>",
|
||||
clientSecret: "<client secret>",
|
||||
subscriptionId: "<subscription id>",
|
||||
tenantId: "<tenant id>",
|
||||
activeDirectoryEndpointUrl: "https://login.microsoftonline.com",
|
||||
resourceManagerEndpointUrl: "https://management.azure.com/",
|
||||
activeDirectoryGraphResourceId: "https://graph.windows.net/",
|
||||
sqlManagementEndpointUrl: "https://management.core.windows.net:8443/",
|
||||
galleryEndpointUrl: "https://gallery.azure.com/",
|
||||
managementEndpointUrl: "https://management.core.windows.net/",
|
||||
};
|
||||
const responseKube = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
properties: {
|
||||
kubeConfig: Buffer.from("###").toString("base64"),
|
||||
},
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
const responseLogin = {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
access_token: "<access_token>",
|
||||
},
|
||||
} as azureActionsUtil.WebResponse;
|
||||
jest
|
||||
.spyOn(azureActionsUtil, "sendRequest")
|
||||
.mockResolvedValueOnce(responseLogin)
|
||||
.mockResolvedValueOnce(responseKube);
|
||||
process.env["RUNNER_TEMP"] = "tempDirPath";
|
||||
jest.spyOn(Date, "now").mockImplementation(() => 1234561234567);
|
||||
|
||||
expect(await login.run());
|
||||
expect(fs.writeFileSync).toHaveBeenCalledWith(
|
||||
path.join("tempDirPath", "kubeconfig_1234561234567"),
|
||||
"###"
|
||||
);
|
||||
expect(fs.chmodSync).toHaveBeenCalledWith(
|
||||
path.join("tempDirPath", "kubeconfig_1234561234567"),
|
||||
"600"
|
||||
);
|
||||
expect(core.exportVariable).toHaveBeenCalledWith(
|
||||
"KUBECONFIG",
|
||||
path.join("tempDirPath", "kubeconfig_1234561234567")
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
import * as core from "@actions/core";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import {
|
||||
WebRequest,
|
||||
WebResponse,
|
||||
sendRequest,
|
||||
} from "@azure-actions/utilities/lib/http";
|
||||
import { getAzureAccessToken } from "@azure-actions/auth";
|
||||
|
||||
export async function run() {
|
||||
// get inputs
|
||||
const creds = core.getInput("creds", { required: true });
|
||||
let credsObject: { [key: string]: string };
|
||||
try {
|
||||
credsObject = JSON.parse(creds);
|
||||
} catch (ex) {
|
||||
throw new Error("Credentials object is not a valid JSON: " + ex);
|
||||
}
|
||||
const subscriptionId =
|
||||
core.getInput("subscription-id") || credsObject["subscriptionId"];
|
||||
const resourceGroupName = core.getInput("resource-group", { required: true });
|
||||
const clusterName = core.getInput("cluster-name", { required: true });
|
||||
|
||||
// get kubeconfig
|
||||
core.debug("Getting kubeconfig");
|
||||
const kubeconfig = await getKubeconfig(
|
||||
credsObject,
|
||||
subscriptionId,
|
||||
resourceGroupName,
|
||||
clusterName
|
||||
);
|
||||
|
||||
// create file
|
||||
const runnerTempDirectory = process.env["RUNNER_TEMP"]; // use process.env until the core libs are updated
|
||||
const kubeconfigPath = path.join(
|
||||
runnerTempDirectory,
|
||||
`kubeconfig_${Date.now()}`
|
||||
);
|
||||
core.debug(`Writing kubeconfig to ${kubeconfigPath}`);
|
||||
fs.writeFileSync(kubeconfigPath, kubeconfig);
|
||||
fs.chmodSync(kubeconfigPath, "600");
|
||||
|
||||
// export
|
||||
core.exportVariable("KUBECONFIG", kubeconfigPath);
|
||||
core.debug("KUBECONFIG environment variable is set");
|
||||
}
|
||||
|
||||
export async function getKubeconfig(
|
||||
creds: {
|
||||
[key: string]: string;
|
||||
},
|
||||
subscriptionId: string,
|
||||
resourceGroupName: string,
|
||||
clusterName: string
|
||||
): Promise<string> {
|
||||
const managementEndpointUrl =
|
||||
creds["resourceManagerEndpointUrl"] || "https://management.azure.com/";
|
||||
const azureSessionToken = await getAzureAccessToken(JSON.stringify(creds));
|
||||
|
||||
return await getAKSKubeconfig(
|
||||
azureSessionToken,
|
||||
subscriptionId,
|
||||
managementEndpointUrl,
|
||||
resourceGroupName,
|
||||
clusterName
|
||||
);
|
||||
}
|
||||
|
||||
export function getAKSKubeconfig(
|
||||
azureSessionToken: string,
|
||||
subscriptionId: string,
|
||||
managementEndpointUrl: string,
|
||||
resourceGroupName: string,
|
||||
clusterName: string
|
||||
): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const webRequest = new WebRequest();
|
||||
webRequest.method = "GET";
|
||||
webRequest.uri = `${managementEndpointUrl}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.ContainerService/managedClusters/${clusterName}/accessProfiles/clusterAdmin?api-version=2017-08-31`;
|
||||
webRequest.headers = {
|
||||
Authorization: "Bearer " + azureSessionToken,
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
};
|
||||
|
||||
sendRequest(webRequest)
|
||||
.then((response: WebResponse) => {
|
||||
const accessProfile = response.body;
|
||||
if (accessProfile?.properties?.kubeConfig) {
|
||||
const kubeconfig = Buffer.from(
|
||||
accessProfile.properties.kubeConfig,
|
||||
"base64"
|
||||
);
|
||||
resolve(kubeconfig.toString());
|
||||
} else {
|
||||
reject(JSON.stringify(response.body));
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
run().catch(core.setFailed);
|
|
@ -1,12 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.test.ts"
|
||||
]
|
||||
}
|
||||
"exclude": ["node_modules", "tests", "**/*.test.ts"]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче