Merge pull request #31 from Azure/atharvam/cluster-connect-master

v1.1 release merge
This commit is contained in:
Atharva Mulmuley 2021-06-10 12:49:40 +05:30 committed by GitHub
commit 15ce083385
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1531 additions and 1261 deletions

View File

@ -2,14 +2,17 @@
This action can be used to set cluster context before other actions like [`azure/k8s-deploy`](https://github.com/Azure/k8s-deploy/tree/master), [`azure/k8s-create-secret`](https://github.com/Azure/k8s-create-secret/tree/master) or any kubectl commands (in script) can be run subsequently in the workflow. This action can be used to set cluster context before other actions like [`azure/k8s-deploy`](https://github.com/Azure/k8s-deploy/tree/master), [`azure/k8s-create-secret`](https://github.com/Azure/k8s-create-secret/tree/master) or any kubectl commands (in script) can be run subsequently in the workflow.
There are two approaches for specifying the deployment target: It is a requirement to use [`azure/login`](https://github.com/Azure/login/tree/master) in your workflow before using this action.
There are three approaches for specifying the deployment target:
- Kubeconfig file provided as input to the action - Kubeconfig file provided as input to the action
- Service account approach where the secret associated with the service account is provided as input to the action - Service account approach where the secret associated with the service account is provided as input to the action
- Service principal approach(only applicable for arc cluster) where service principal provided with 'creds' is used as input to action
If inputs related to both these approaches are provided, kubeconfig approach related inputs are given precedence. If inputs related to all these approaches are provided, kubeconfig approach related inputs are given precedence.
In both these approaches it is recommended to store these contents (kubeconfig file content or secret content) in a [secret](https://developer.github.com/actions/managing-workflows/storing-secrets/) which could be referenced later in the action. In all these approaches it is recommended to store these contents (kubeconfig file content or secret content) in a [secret](https://developer.github.com/actions/managing-workflows/storing-secrets/) which could be referenced later in the action.
## Action inputs ## Action inputs
@ -22,7 +25,7 @@ In both these approaches it is recommended to store these contents (kubeconfig f
</thead> </thead>
<tr> <tr>
<td><code>method</code><br/>Method</td> <td><code>method</code><br/>Method</td>
<td>(Optional) Acceptable values: kubeconfig/service-account. Default value: kubeconfig</td> <td>(Optional) Acceptable values: kubeconfig/service-account/service-principal. Default value: kubeconfig</td>
</tr> </tr>
<tr> <tr>
<td><code>kubeconfig</code><br/>Kubectl config</td> <td><code>kubeconfig</code><br/>Kubectl config</td>
@ -40,6 +43,22 @@ In both these approaches it is recommended to store these contents (kubeconfig f
<td><code>k8s-secret</code><br/>Secret</td> <td><code>k8s-secret</code><br/>Secret</td>
<td>(Relevant for service account approach) Secret associated with the service account to be used for deployments</td> <td>(Relevant for service account approach) Secret associated with the service account to be used for deployments</td>
</tr> </tr>
<tr>
<td><code>cluster-type</code><br/>Type of cluster</td>
<td>Type of cluster. Acceptable values: generic/arc</td>
</tr>
<tr>
<td><code>cluster-name</code><br/>Name of arc cluster</td>
<td>Name of Azure Arc enabled Kubernetes cluster. Required only if cluster-type is 'arc'.</td>
</tr>
<tr>
<td><code>resource-group</code><br/>resource group</td>
<td>Resource group containing the Azure Arc enabled Kubernetes cluster. Required only if cluster-type is 'arc'.</td>
</tr>
<tr>
<td><code>token</code><br/>Service account token</td>
<td>Applicable for 'service-account' method.</td>
</tr>
</table> </table>
## Example usage ## Example usage
@ -103,6 +122,31 @@ kubectl get serviceAccounts <service-account-name> -n <namespace> -o 'jsonpath={
kubectl get secret <service-account-secret-name> -n <namespace> -o yaml kubectl get secret <service-account-secret-name> -n <namespace> -o yaml
``` ```
### Service account approach for arc cluster
```yaml
- uses: azure/k8s-set-context@v1
with:
method: service-account
cluster-type: 'arc'
cluster-name: <cluster-name>
resource-group: <resource-group>
token: '${{ secrets.SA_TOKEN }}'
id: setcontext
```
### Service principal approach for arc cluster
```yaml
- uses: azure/k8s-set-context@v1
with:
method: service-principal
cluster-type: 'arc'
cluster-name: <cluster-name>
resource-group: <resource-group>
id: setcontext
```
## Contributing ## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a This project welcomes contributions and suggestions. Most contributions require you to agree to a

View File

@ -1,4 +1,5 @@
import * as run from '../src/login' import * as run from '../src/login'
import * as arc from '../src/arc-login'
import * as os from 'os'; import * as os from 'os';
import * as io from '@actions/io'; import * as io from '@actions/io';
import * as toolCache from '@actions/tool-cache'; import * as toolCache from '@actions/tool-cache';
@ -6,7 +7,8 @@ import * as core from '@actions/core';
import * as fs from 'fs'; import * as fs from 'fs';
import * as jsyaml from 'js-yaml'; import * as jsyaml from 'js-yaml';
import * as path from 'path'; import * as path from 'path';
import * as child_process from 'child_process';
import * as exec from '@actions/exec';
var mockStatusCode; var mockStatusCode;
const mockExecFn = jest.fn().mockImplementation(() => mockStatusCode); const mockExecFn = jest.fn().mockImplementation(() => mockStatusCode);
jest.mock('@actions/exec/lib/toolrunner', () => { jest.mock('@actions/exec/lib/toolrunner', () => {
@ -21,6 +23,121 @@ jest.mock('@actions/exec/lib/toolrunner', () => {
describe('Testing all functions.', () => { describe('Testing all functions.', () => {
test('getArcKubeconfig() - checking execution of service-account scenario',async () =>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'service-account';
if (inputName == 'resource-group') return 'testrg';
if (inputName == 'cluster-name') return 'testcluster';
if (inputName == 'token') return 'token';
});
jest.spyOn(io, 'which').mockResolvedValue('az');
process.env['RUNNER_TEMP'] = 'tempDirPath';
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
const dummy_process=await child_process.spawn('ls',[]);
const dummy_sleep_promise = new Promise(resolve => setTimeout(resolve, 0));
jest.spyOn(child_process,'spawn').mockImplementation((commands, arg, option)=>{
return dummy_process;
});
jest.spyOn(arc,'sleep').mockImplementation((ms)=>{
return dummy_sleep_promise;
});
jest.spyOn(fs, 'chmodSync').mockImplementation();
jest.spyOn(core, 'exportVariable').mockImplementation();
jest.spyOn(exec,'exec').mockImplementation();
await arc.getArcKubeconfig();
expect(core.getInput).toBeCalledTimes(4);
expect(io.which).toHaveBeenCalledWith("az",true);
expect(child_process.spawn).toHaveBeenCalledWith('az',['connectedk8s','proxy','-n','testcluster','-g','testrg','-f',path.join('tempDirPath', 'kubeconfig_1234561234567'),'--token','token'], {
detached: true,
stdio: 'ignore'
});
expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600');
expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567'));
},180000);
test('getArcKubeconfig() - checking execution of service-principal scenario',async () =>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'service-principal';
if (inputName == 'resource-group') return 'testrg';
if (inputName == 'cluster-name') return 'testcluster';
});
jest.spyOn(io, 'which').mockResolvedValue('az');
process.env['RUNNER_TEMP'] = 'tempDirPath';
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
const dummy_process=await child_process.spawn('ls',[]);
const dummy_sleep_promise = new Promise(resolve => setTimeout(resolve, 0));
jest.spyOn(child_process,'spawn').mockImplementation((commands, arg, option)=>{
return dummy_process;
});
jest.spyOn(arc,'sleep').mockImplementation((ms)=>{
return dummy_sleep_promise;
});
jest.spyOn(fs, 'chmodSync').mockImplementation();
jest.spyOn(core, 'exportVariable').mockImplementation();
jest.spyOn(exec,'exec').mockImplementation();
await arc.getArcKubeconfig();
expect(core.getInput).toBeCalledTimes(3);
expect(io.which).toHaveBeenCalledWith("az",true);
expect(child_process.spawn).toHaveBeenCalledWith('az',['connectedk8s','proxy','-n','testcluster','-g','testrg','-f',path.join('tempDirPath', 'kubeconfig_1234561234567')], {
detached: true,
stdio: 'ignore'
});
expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600');
expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567'));
},180000);
test('getArcKubeconfig() - wrong method passed',async ()=>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'someMethod';
});
await expect(arc.getArcKubeconfig()).rejects.toThrow("Supported methods for arc cluster are 'service-account' and 'service-principal'.");
expect(core.getInput).toBeCalledTimes(1);
});
test('getArcKubeconfig() - resource group not passed',async ()=>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'service-account';
if (inputName == 'resource-group') return '';
});
await expect(arc.getArcKubeconfig()).rejects.toThrow("'resourceGroupName' is not passed for arc cluster.");
expect(core.getInput).toBeCalledTimes(3);
});
test('getArcKubeconfig() - cluster name not passed',async ()=>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'service-account';
if (inputName == 'resource-group') return 'testrg';
if (inputName == 'cluster-name') return '';
});
await expect(arc.getArcKubeconfig()).rejects.toThrow("'clusterName' is not passed for arc cluster.");
expect(core.getInput).toBeCalledTimes(3);
});
test('getArcKubeconfig() - token not passed',async ()=>{
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'method') return 'service-account';
if (inputName == 'resource-group') return 'testrg';
if (inputName == 'cluster-name') return 'testcluster';
if (inputName == 'token') return '';
});
jest.spyOn(io, 'which').mockResolvedValue('az');
jest.spyOn(exec,'exec').mockImplementation();
process.env['RUNNER_TEMP'] = 'tempDirPath';
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
await expect(arc.getArcKubeconfig()).rejects.toThrow("'saToken' is not passed for 'service-account' method.");
expect(core.getInput).toBeCalledTimes(4);
expect(io.which).toBeCalled();
});
test('executeAzCliCommand() - testing execution of function',()=>{
jest.spyOn(exec,'exec').mockImplementation();
var azPath = "az";
expect(arc.executeAzCliCommand("some command",false));
expect(exec.exec).toBeCalled();
});
test('getExecutableExtension() - return .exe when os is Windows', () => { test('getExecutableExtension() - return .exe when os is Windows', () => {
jest.spyOn(os, 'type').mockReturnValue('Windows_NT'); jest.spyOn(os, 'type').mockReturnValue('Windows_NT');
@ -187,6 +304,7 @@ describe('Testing all functions.', () => {
if (inputName == 'method') return 'kubeconfig'; if (inputName == 'method') return 'kubeconfig';
if (inputName == 'kubeconfig') return '###'; if (inputName == 'kubeconfig') return '###';
if (inputName == 'context') return ''; if (inputName == 'context') return '';
if (inputName == 'cluster-type') return 'generic';
}); });
process.env['RUNNER_TEMP'] = 'tempDirPath' process.env['RUNNER_TEMP'] = 'tempDirPath'
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567); jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
@ -196,4 +314,15 @@ describe('Testing all functions.', () => {
expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600'); expect(fs.chmodSync).toHaveBeenCalledWith(path.join('tempDirPath', 'kubeconfig_1234561234567'), '600');
expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567')); expect(core.exportVariable).toHaveBeenCalledWith('KUBECONFIG', path.join('tempDirPath', 'kubeconfig_1234561234567'));
}); });
test('run() - check if arc scenario is getting triggered', async () =>{
jest.spyOn(arc,'getArcKubeconfig').mockImplementation();
jest.spyOn(core, 'getInput').mockImplementation((inputName, options) => {
if (inputName == 'cluster-type') return 'arc';
});
expect(run.run());
expect(arc.getArcKubeconfig).toBeCalled();
});
}); });

View File

@ -2,8 +2,12 @@ name: 'Kubernetes set context'
description: 'Kubernetes set context' description: 'Kubernetes set context'
inputs: inputs:
# Used for setting the target K8s cluster context which will be used by other actions like azure/k8s-actions/k8s-deploy or azure/k8s-actions/k8s-create-secret # Used for setting the target K8s cluster context which will be used by other actions like azure/k8s-actions/k8s-deploy or azure/k8s-actions/k8s-create-secret
cluster-type:
description: 'Acceptable values: generic or arc'
required: true
default: 'generic'
method: method:
description: 'Acceptable values: kubeconfig or service-account' description: 'Acceptable values: kubeconfig or service-account or service-principal'
required: true required: true
default: 'kubeconfig' default: 'kubeconfig'
kubeconfig: kubeconfig:
@ -23,6 +27,18 @@ inputs:
description: 'Service account secret. Run kubectl get serviceaccounts <service-account-name> -o yaml and copy the service-account-secret-name. Copy the ouptut of kubectl get secret <service-account-secret-name> -o yaml' description: 'Service account secret. Run kubectl get serviceaccounts <service-account-name> -o yaml and copy the service-account-secret-name. Copy the ouptut of kubectl get secret <service-account-secret-name> -o yaml'
required: false required: false
default: '' default: ''
token:
description: 'Token extracted from the secret of service account (should be base 64 decoded)'
required: false
default: ''
resource-group:
description: 'Azure resource group name'
required: false
default: ''
cluster-name:
description: 'Azure connected cluster name'
required: false
default: ''
branding: branding:
color: 'green' # optional, decorates the entry in the GitHub Marketplace color: 'green' # optional, decorates the entry in the GitHub Marketplace

94
lib/arc-login.js Normal file
View File

@ -0,0 +1,94 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const core = require("@actions/core");
const path = require("path");
const child_process_1 = require("child_process");
const fs = require("fs");
const io = require("@actions/io");
const exec = require("@actions/exec");
var azPath;
const kubeconfig_timeout = 120; //timeout in seconds
function getArcKubeconfig() {
return __awaiter(this, void 0, void 0, function* () {
try {
let method = core.getInput('method');
if (method != 'service-account' && method != 'service-principal') {
throw Error("Supported methods for arc cluster are 'service-account' and 'service-principal'.");
}
let resourceGroupName = core.getInput('resource-group');
let clusterName = core.getInput('cluster-name');
if (!resourceGroupName) {
throw Error("'resourceGroupName' is not passed for arc cluster.");
}
if (!clusterName) {
throw Error("'clusterName' is not passed for arc cluster.");
}
azPath = yield io.which("az", true);
yield executeAzCliCommand(`account show`, false);
try {
yield executeAzCliCommand(`extension remove -n connectedk8s`, false);
}
catch (_a) {
//ignore if this causes an error
}
yield executeAzCliCommand(`extension add -n connectedk8s`, false);
yield executeAzCliCommand(`extension list`, false);
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`);
if (method == 'service-account') {
let saToken = core.getInput('token');
if (!saToken) {
throw Error("'saToken' is not passed for 'service-account' method.");
}
console.log("using 'service-account' method for authenticating to arc cluster.");
const proc = child_process_1.spawn(azPath, ['connectedk8s', 'proxy', '-n', clusterName, '-g', resourceGroupName, '-f', kubeconfigPath, '--token', saToken], {
detached: true,
stdio: 'ignore'
});
proc.unref();
}
else {
console.log("using 'service-principal' method for authenticating to arc cluster.");
const proc = child_process_1.spawn(azPath, ['connectedk8s', 'proxy', '-n', clusterName, '-g', resourceGroupName, '-f', kubeconfigPath], {
detached: true,
stdio: 'ignore'
});
proc.unref();
}
console.log(`Waiting for ${kubeconfig_timeout} seconds for kubeconfig to be merged....`);
yield sleep(kubeconfig_timeout * 1000); //sleeping for 2 minutes to allow kubeconfig to be merged
fs.chmodSync(kubeconfigPath, '600');
core.exportVariable('KUBECONFIG', kubeconfigPath);
console.log('KUBECONFIG environment variable is set');
}
catch (ex) {
return Promise.reject(ex);
}
});
}
exports.getArcKubeconfig = getArcKubeconfig;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
exports.sleep = sleep;
function executeAzCliCommand(command, silent, execOptions = {}, args = []) {
return __awaiter(this, void 0, void 0, function* () {
execOptions.silent = !!silent;
try {
yield exec.exec(`"${azPath}" ${command}`, args, execOptions);
}
catch (error) {
throw new Error(error);
}
});
}
exports.executeAzCliCommand = executeAzCliCommand;

View File

@ -9,7 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.run = exports.setContext = exports.getKubectlPath = exports.getExecutableExtension = exports.getKubeconfig = void 0;
const core = require("@actions/core"); const core = require("@actions/core");
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
@ -19,6 +18,7 @@ const os = require("os");
const toolrunner_1 = require("@actions/exec/lib/toolrunner"); const toolrunner_1 = require("@actions/exec/lib/toolrunner");
const jsyaml = require("js-yaml"); const jsyaml = require("js-yaml");
const util = require("util"); const util = require("util");
const arc_login_1 = require("./arc-login");
function getKubeconfig() { function getKubeconfig() {
const method = core.getInput('method', { required: true }); const method = core.getInput('method', { required: true });
if (method == 'kubeconfig') { if (method == 'kubeconfig') {
@ -111,15 +111,29 @@ function setContext(kubeconfigPath) {
exports.setContext = setContext; exports.setContext = setContext;
function run() { function run() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let kubeconfig = getKubeconfig(); try {
let kubeconfig = '';
const cluster_type = core.getInput('cluster-type', { required: true });
if (cluster_type == 'arc') {
yield arc_login_1.getArcKubeconfig().catch(ex => {
throw new Error('Error: Could not get the KUBECONFIG for arc cluster: ' + ex);
});
}
else {
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`);
kubeconfig = getKubeconfig();
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`);
fs.writeFileSync(kubeconfigPath, kubeconfig); fs.writeFileSync(kubeconfigPath, kubeconfig);
fs.chmodSync(kubeconfigPath, '600'); fs.chmodSync(kubeconfigPath, '600');
core.exportVariable('KUBECONFIG', kubeconfigPath); core.exportVariable('KUBECONFIG', kubeconfigPath);
console.log('KUBECONFIG environment variable is set'); console.log('KUBECONFIG environment variable is set');
yield setContext(kubeconfigPath); yield setContext(kubeconfigPath);
}
}
catch (ex) {
return Promise.reject(ex);
}
}); });
} }
exports.run = run; exports.run = run;

2327
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,10 +22,10 @@
"js-yaml": "^3.13.1" "js-yaml": "^3.13.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^12.0.4",
"typescript": "3.9.2",
"jest": "^26.0.1",
"@types/jest": "^25.2.2", "@types/jest": "^25.2.2",
"ts-jest": "^25.5.1" "@types/node": "^12.0.4",
"jest": "^26.6.3",
"ts-jest": "^25.5.1",
"typescript": "3.9.2"
} }
} }

83
src/arc-login.ts Normal file
View File

@ -0,0 +1,83 @@
import * as core from '@actions/core';
import * as path from 'path';
import {spawn} from 'child_process';
import * as fs from 'fs';
import * as io from '@actions/io';
import * as exec from '@actions/exec';
var azPath: string;
const kubeconfig_timeout = 120;//timeout in seconds
export async function getArcKubeconfig(): Promise<string> {
try {
let method = core.getInput('method');
if (method != 'service-account' && method != 'service-principal'){
throw Error("Supported methods for arc cluster are 'service-account' and 'service-principal'.");
}
let resourceGroupName = core.getInput('resource-group');
let clusterName = core.getInput('cluster-name');
if(!resourceGroupName){
throw Error("'resourceGroupName' is not passed for arc cluster.")
}
if(!clusterName){
throw Error("'clusterName' is not passed for arc cluster.")
}
azPath = await io.which("az", true);
await executeAzCliCommand(`account show`, false);
try{
await executeAzCliCommand(`extension remove -n connectedk8s`, false);
}
catch{
//ignore if this causes an error
}
await executeAzCliCommand(`extension add -n connectedk8s`, false);
await executeAzCliCommand(`extension list`, false);
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`);
if (method == 'service-account'){
let saToken = core.getInput('token');
if(!saToken){
throw Error("'saToken' is not passed for 'service-account' method.")
}
console.log("using 'service-account' method for authenticating to arc cluster.")
const proc=spawn(azPath,['connectedk8s','proxy','-n',clusterName,'-g',resourceGroupName,'-f',kubeconfigPath,'--token',saToken], {
detached: true,
stdio: 'ignore'
});
proc.unref();
} else{
console.log("using 'service-principal' method for authenticating to arc cluster.")
const proc=spawn(azPath,['connectedk8s','proxy','-n',clusterName,'-g',resourceGroupName,'-f',kubeconfigPath], {
detached: true,
stdio: 'ignore'
});
proc.unref();
}
console.log(`Waiting for ${kubeconfig_timeout} seconds for kubeconfig to be merged....`)
await sleep(kubeconfig_timeout*1000) //sleeping for 2 minutes to allow kubeconfig to be merged
fs.chmodSync(kubeconfigPath, '600');
core.exportVariable('KUBECONFIG', kubeconfigPath);
console.log('KUBECONFIG environment variable is set');
} catch (ex) {
return Promise.reject(ex);
}
}
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export async function executeAzCliCommand(
command: string,
silent?: boolean,
execOptions: any = {},
args: any = []) {
execOptions.silent = !!silent;
try {
await exec.exec(`"${azPath}" ${command}`, args, execOptions);
}
catch (error) {
throw new Error(error);
}
}

View File

@ -7,18 +7,19 @@ import * as os from 'os';
import { ToolRunner } from "@actions/exec/lib/toolrunner"; import { ToolRunner } from "@actions/exec/lib/toolrunner";
import * as jsyaml from 'js-yaml'; import * as jsyaml from 'js-yaml';
import * as util from 'util'; import * as util from 'util';
import { getArcKubeconfig } from './arc-login';
export function getKubeconfig(): string { export function getKubeconfig(): string {
const method = core.getInput('method', {required: true}); const method = core.getInput('method', { required: true });
if (method == 'kubeconfig') { if (method == 'kubeconfig') {
const kubeconfig = core.getInput('kubeconfig', {required : true}); const kubeconfig = core.getInput('kubeconfig', { required: true });
core.debug("Setting context using kubeconfig"); core.debug("Setting context using kubeconfig");
return kubeconfig; return kubeconfig;
} }
else if (method == 'service-account') { else if (method == 'service-account') {
const clusterUrl = core.getInput('k8s-url', { required: true }); const clusterUrl = core.getInput('k8s-url', { required: true });
core.debug("Found clusterUrl, creating kubeconfig using certificate and token"); core.debug("Found clusterUrl, creating kubeconfig using certificate and token");
let k8sSecret = core.getInput('k8s-secret', {required : true}); let k8sSecret = core.getInput('k8s-secret', { required: true });
var parsedk8sSecret = jsyaml.safeLoad(k8sSecret); var parsedk8sSecret = jsyaml.safeLoad(k8sSecret);
let kubernetesServiceAccountSecretFieldNotPresent = 'The service account secret yaml does not contain %s; field. Make sure that its present and try again.'; let kubernetesServiceAccountSecretFieldNotPresent = 'The service account secret yaml does not contain %s; field. Make sure that its present and try again.';
if (!parsedk8sSecret) { if (!parsedk8sSecret) {
@ -102,15 +103,31 @@ export async function setContext(kubeconfigPath: string) {
} }
export async function run() { export async function run() {
let kubeconfig = getKubeconfig(); try {
let kubeconfig = '';
const cluster_type = core.getInput('cluster-type', { required: true });
if (cluster_type == 'arc') {
try{
await getArcKubeconfig();
}
catch (ex){
throw new Error('Error: Could not get the KUBECONFIG for arc cluster: ' + ex);
}
}
else {
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`);
kubeconfig = getKubeconfig();
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`);
fs.writeFileSync(kubeconfigPath, kubeconfig); fs.writeFileSync(kubeconfigPath, kubeconfig);
fs.chmodSync(kubeconfigPath, '600'); fs.chmodSync(kubeconfigPath, '600');
core.exportVariable('KUBECONFIG', kubeconfigPath); core.exportVariable('KUBECONFIG', kubeconfigPath);
console.log('KUBECONFIG environment variable is set'); console.log('KUBECONFIG environment variable is set');
await setContext(kubeconfigPath); await setContext(kubeconfigPath);
}
} catch (ex) {
return Promise.reject(ex);
}
} }
run().catch(core.setFailed); run().catch(core.setFailed);