From 6e2f9f1d81ae9ddbc7d7a1d4247f08cf980b16f5 Mon Sep 17 00:00:00 2001 From: Atharva Mulmuley Date: Tue, 18 May 2021 17:57:55 +0530 Subject: [PATCH] cherry-picked v1.1 commit --- README.md | 52 ++++++++++++++++++++++-- action.yml | 20 ++++++++- lib/arc-login.js | 92 ++++++++++++++++++++++++++++++++++++++++++ lib/client.js | 99 +++++++++++++++++++++++++++++++++++++++++++++ lib/login.js | 41 +++++++++++-------- src/arc-login.ts | 83 ++++++++++++++++++++++++++++++++++++++ src/client.ts | 103 +++++++++++++++++++++++++++++++++++++++++++++++ src/login.ts | 42 ++++++++++++------- 8 files changed, 496 insertions(+), 36 deletions(-) create mode 100644 lib/arc-login.js create mode 100644 lib/client.js create mode 100644 src/arc-login.ts create mode 100644 src/client.ts diff --git a/README.md b/README.md index f212f014..0cfc1046 100644 --- a/README.md +++ b/README.md @@ -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. -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 - 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 @@ -22,7 +25,7 @@ In both these approaches it is recommended to store these contents (kubeconfig f method
Method - (Optional) Acceptable values: kubeconfig/service-account. Default value: kubeconfig + (Optional) Acceptable values: kubeconfig/service-account/service-principal. Default value: kubeconfig kubeconfig
Kubectl config @@ -40,6 +43,22 @@ In both these approaches it is recommended to store these contents (kubeconfig f k8s-secret
Secret (Relevant for service account approach) Secret associated with the service account to be used for deployments + + cluster-type
Type of cluster + Type of cluster. Acceptable values: generic/arc + + + cluster-name
Name of arc cluster + Name of Azure Arc enabled Kubernetes cluster. Required only if cluster-type is 'arc'. + + + resource-group
resource group + Resource group containing the Azure Arc enabled Kubernetes cluster. Required only if cluster-type is 'arc'. + + + token
Service account token + Applicable for 'service-account' method. + ## Example usage @@ -103,6 +122,31 @@ kubectl get serviceAccounts -n -o 'jsonpath={ kubectl get secret -n -o yaml ``` +### Service account approach for arc cluster + +```yaml +- uses: azure/k8s-set-context@v1 + with: + method: service-account + cluster-type: 'arc' + cluster-name: + 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: + resource-group: + id: setcontext +``` + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a diff --git a/action.yml b/action.yml index e35166f7..703a176f 100644 --- a/action.yml +++ b/action.yml @@ -2,8 +2,12 @@ name: 'Kubernetes set context' description: 'Kubernetes set context' 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 + cluster-type: + description: 'Acceptable values: generic or arc' + required: true + default: 'generic' method: - description: 'Acceptable values: kubeconfig or service-account' + description: 'Acceptable values: kubeconfig or service-account or service-principal' required: true default: 'kubeconfig' kubeconfig: @@ -23,9 +27,21 @@ inputs: description: 'Service account secret. Run kubectl get serviceaccounts -o yaml and copy the service-account-secret-name. Copy the ouptut of kubectl get secret -o yaml' required: false 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: color: 'green' # optional, decorates the entry in the GitHub Marketplace runs: using: 'node12' - main: 'lib/login.js' + main: 'lib/login.js' \ No newline at end of file diff --git a/lib/arc-login.js b/lib/arc-login.js new file mode 100644 index 00000000..ae57a203 --- /dev/null +++ b/lib/arc-login.js @@ -0,0 +1,92 @@ +"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)); +} +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); + } + }); +} diff --git a/lib/client.js b/lib/client.js new file mode 100644 index 00000000..d19f77ec --- /dev/null +++ b/lib/client.js @@ -0,0 +1,99 @@ +"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 util = require("util"); +const fs = require("fs"); +const httpClient = require("typed-rest-client/HttpClient"); +const core = require("@actions/core"); +var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {}); +class WebRequest { +} +exports.WebRequest = WebRequest; +class WebResponse { +} +exports.WebResponse = WebResponse; +class WebRequestOptions { +} +exports.WebRequestOptions = WebRequestOptions; +function sendRequest(request, options) { + return __awaiter(this, void 0, void 0, function* () { + let i = 0; + let retryCount = options && options.retryCount ? options.retryCount : 5; + let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2; + let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"]; + let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504]; + let timeToWait = retryIntervalInSeconds; + while (true) { + try { + if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) { + request.body = fs.createReadStream(request.body["path"]); + } + let response = yield sendRequestInternal(request); + if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) { + core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage)); + yield sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + continue; + } + return response; + } + catch (error) { + if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) { + core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message)); + yield sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + } + else { + if (error.code) { + core.debug("error code =" + error.code); + } + throw error; + } + } + } + }); +} +exports.sendRequest = sendRequest; +function sleepFor(sleepDurationInSeconds) { + return new Promise((resolve, reject) => { + setTimeout(resolve, sleepDurationInSeconds * 1000); + }); +} +exports.sleepFor = sleepFor; +function sendRequestInternal(request) { + return __awaiter(this, void 0, void 0, function* () { + core.debug(util.format("[%s]%s", request.method, request.uri)); + var response = yield httpCallbackClient.request(request.method, request.uri, request.body, request.headers); + return yield toWebResponse(response); + }); +} +function toWebResponse(response) { + return __awaiter(this, void 0, void 0, function* () { + var res = new WebResponse(); + if (response) { + res.statusCode = response.message.statusCode; + res.statusMessage = response.message.statusMessage; + res.headers = response.message.headers; + var body = yield response.readBody(); + if (body) { + try { + res.body = JSON.parse(body); + } + catch (error) { + core.debug("Could not parse response: " + JSON.stringify(error)); + core.debug("Response: " + JSON.stringify(res.body)); + res.body = body; + } + } + } + return res; + }); +} diff --git a/lib/login.js b/lib/login.js index 18d24e80..0b69e0bd 100644 --- a/lib/login.js +++ b/lib/login.js @@ -9,7 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.run = exports.setContext = exports.getKubectlPath = exports.getExecutableExtension = exports.getKubeconfig = void 0; const core = require("@actions/core"); const path = require("path"); const fs = require("fs"); @@ -19,6 +18,7 @@ const os = require("os"); const toolrunner_1 = require("@actions/exec/lib/toolrunner"); const jsyaml = require("js-yaml"); const util = require("util"); +const arc_login_1 = require("./arc-login"); function getKubeconfig() { const method = core.getInput('method', { required: true }); if (method == 'kubeconfig') { @@ -31,7 +31,7 @@ function getKubeconfig() { core.debug("Found clusterUrl, creating kubeconfig using certificate and token"); let k8sSecret = core.getInput('k8s-secret', { required: true }); 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 acount secret yaml does not contain %s; field. Make sure that its present and try again.'; if (!parsedk8sSecret) { throw Error("The service account secret yaml specified is invalid. Make sure that its a valid yaml and try again."); } @@ -71,14 +71,12 @@ function getKubeconfig() { throw Error("Invalid method specified. Acceptable values are kubeconfig and service-account."); } } -exports.getKubeconfig = getKubeconfig; function getExecutableExtension() { if (os.type().match(/^Win/)) { return '.exe'; } return ''; } -exports.getExecutableExtension = getExecutableExtension; function getKubectlPath() { return __awaiter(this, void 0, void 0, function* () { let kubectlPath = yield io.which('kubectl', false); @@ -93,7 +91,6 @@ function getKubectlPath() { return kubectlPath; }); } -exports.getKubectlPath = getKubectlPath; function setContext(kubeconfigPath) { return __awaiter(this, void 0, void 0, function* () { let context = core.getInput('context'); @@ -108,19 +105,31 @@ function setContext(kubeconfigPath) { } }); } -exports.setContext = setContext; function run() { return __awaiter(this, void 0, void 0, function* () { - let kubeconfig = 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); - console.log('KUBECONFIG environment variable is set'); - yield setContext(kubeconfigPath); + 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 kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); + kubeconfig = getKubeconfig(); + core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); + fs.writeFileSync(kubeconfigPath, kubeconfig); + fs.chmodSync(kubeconfigPath, '600'); + core.exportVariable('KUBECONFIG', kubeconfigPath); + console.log('KUBECONFIG environment variable is set'); + yield setContext(kubeconfigPath); + } + } + catch (ex) { + return Promise.reject(ex); + } }); } -exports.run = run; run().catch(core.setFailed); diff --git a/src/arc-login.ts b/src/arc-login.ts new file mode 100644 index 00000000..88e94303 --- /dev/null +++ b/src/arc-login.ts @@ -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 { + 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); + } +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +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); + } +} \ No newline at end of file diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 00000000..a314d2df --- /dev/null +++ b/src/client.ts @@ -0,0 +1,103 @@ +import util = require("util"); +import fs = require('fs'); +import httpClient = require("typed-rest-client/HttpClient"); +import * as core from '@actions/core'; + +var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {}); + +export class WebRequest { + public method: string; + public uri: string; + // body can be string or ReadableStream + public body: string | NodeJS.ReadableStream; + public headers: any; +} + +export class WebResponse { + public statusCode: number; + public statusMessage: string; + public headers: any; + public body: any; +} + +export class WebRequestOptions { + public retriableErrorCodes?: string[]; + public retryCount?: number; + public retryIntervalInSeconds?: number; + public retriableStatusCodes?: number[]; + public retryRequestTimedout?: boolean; +} + +export async function sendRequest(request: WebRequest, options?: WebRequestOptions): Promise { + let i = 0; + let retryCount = options && options.retryCount ? options.retryCount : 5; + let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2; + let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"]; + let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504]; + let timeToWait: number = retryIntervalInSeconds; + while (true) { + try { + if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) { + request.body = fs.createReadStream(request.body["path"]); + } + + let response: WebResponse = await sendRequestInternal(request); + if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) { + core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage)); + await sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + continue; + } + + return response; + } + catch (error) { + if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) { + core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message)); + await sleepFor(timeToWait); + timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds; + } + else { + if (error.code) { + core.debug("error code =" + error.code); + } + + throw error; + } + } + } +} + +export function sleepFor(sleepDurationInSeconds: number): Promise { + return new Promise((resolve, reject) => { + setTimeout(resolve, sleepDurationInSeconds * 1000); + }); +} + +async function sendRequestInternal(request: WebRequest): Promise { + core.debug(util.format("[%s]%s", request.method, request.uri)); + var response: httpClient.HttpClientResponse = await httpCallbackClient.request(request.method, request.uri, request.body, request.headers); + return await toWebResponse(response); +} + +async function toWebResponse(response: httpClient.HttpClientResponse): Promise { + var res = new WebResponse(); + if (response) { + res.statusCode = response.message.statusCode; + res.statusMessage = response.message.statusMessage; + res.headers = response.message.headers; + var body = await response.readBody(); + if (body) { + try { + res.body = JSON.parse(body); + } + catch (error) { + core.debug("Could not parse response: " + JSON.stringify(error)); + core.debug("Response: " + JSON.stringify(res.body)); + res.body = body; + } + } + } + + return res; +} \ No newline at end of file diff --git a/src/login.ts b/src/login.ts index 886cb6be..72086239 100644 --- a/src/login.ts +++ b/src/login.ts @@ -7,18 +7,19 @@ import * as os from 'os'; import { ToolRunner } from "@actions/exec/lib/toolrunner"; import * as jsyaml from 'js-yaml'; import * as util from 'util'; +import { getArcKubeconfig } from './arc-login'; -export function getKubeconfig(): string { - const method = core.getInput('method', {required: true}); +function getKubeconfig(): string { + const method = core.getInput('method', { required: true }); if (method == 'kubeconfig') { - const kubeconfig = core.getInput('kubeconfig', {required : true}); + const kubeconfig = core.getInput('kubeconfig', { required: true }); core.debug("Setting context using kubeconfig"); return kubeconfig; } else if (method == 'service-account') { const clusterUrl = core.getInput('k8s-url', { required: true }); 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); let kubernetesServiceAccountSecretFieldNotPresent = 'The service account secret yaml does not contain %s; field. Make sure that its present and try again.'; if (!parsedk8sSecret) { @@ -101,16 +102,29 @@ export async function setContext(kubeconfigPath: string) { } } -export async function run() { - let kubeconfig = 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); - console.log('KUBECONFIG environment variable is set'); - await setContext(kubeconfigPath); +async function run() { + try { + let kubeconfig = ''; + const cluster_type = core.getInput('cluster-type', { required: true }); + if (cluster_type == 'arc') { + 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 kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); + kubeconfig = getKubeconfig(); + core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); + fs.writeFileSync(kubeconfigPath, kubeconfig); + fs.chmodSync(kubeconfigPath, '600'); + core.exportVariable('KUBECONFIG', kubeconfigPath); + console.log('KUBECONFIG environment variable is set'); + await setContext(kubeconfigPath); + } + } catch (ex) { + return Promise.reject(ex); + } } run().catch(core.setFailed);