cherry-picked v1.1 commit

This commit is contained in:
Atharva Mulmuley 2021-05-18 17:57:55 +05:30
parent e8a7e17aad
commit 6e2f9f1d81
8 changed files with 496 additions and 36 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

@ -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,9 +27,21 @@ 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
runs: runs:
using: 'node12' using: 'node12'
main: 'lib/login.js' main: 'lib/login.js'

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

@ -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);
}
});
}

99
lib/client.js Normal file
View File

@ -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;
});
}

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') {
@ -31,7 +31,7 @@ function getKubeconfig() {
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 acount secret yaml does not contain %s; field. Make sure that its present and try again.';
if (!parsedk8sSecret) { if (!parsedk8sSecret) {
throw Error("The service account secret yaml specified is invalid. Make sure that its a valid yaml and try again."); 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."); throw Error("Invalid method specified. Acceptable values are kubeconfig and service-account.");
} }
} }
exports.getKubeconfig = getKubeconfig;
function getExecutableExtension() { function getExecutableExtension() {
if (os.type().match(/^Win/)) { if (os.type().match(/^Win/)) {
return '.exe'; return '.exe';
} }
return ''; return '';
} }
exports.getExecutableExtension = getExecutableExtension;
function getKubectlPath() { function getKubectlPath() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let kubectlPath = yield io.which('kubectl', false); let kubectlPath = yield io.which('kubectl', false);
@ -93,7 +91,6 @@ function getKubectlPath() {
return kubectlPath; return kubectlPath;
}); });
} }
exports.getKubectlPath = getKubectlPath;
function setContext(kubeconfigPath) { function setContext(kubeconfigPath) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let context = core.getInput('context'); let context = core.getInput('context');
@ -108,19 +105,31 @@ function setContext(kubeconfigPath) {
} }
}); });
} }
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 {
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated let kubeconfig = '';
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); const cluster_type = core.getInput('cluster-type', { required: true });
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); if (cluster_type == 'arc') {
fs.writeFileSync(kubeconfigPath, kubeconfig); yield arc_login_1.getArcKubeconfig().catch(ex => {
fs.chmodSync(kubeconfigPath, '600'); throw new Error('Error: Could not get the KUBECONFIG for arc cluster: ' + ex);
core.exportVariable('KUBECONFIG', kubeconfigPath); });
console.log('KUBECONFIG environment variable is set'); }
yield setContext(kubeconfigPath); 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); run().catch(core.setFailed);

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);
}
}
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);
}
}

103
src/client.ts Normal file
View File

@ -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<WebResponse> {
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<any> {
return new Promise((resolve, reject) => {
setTimeout(resolve, sleepDurationInSeconds * 1000);
});
}
async function sendRequestInternal(request: WebRequest): Promise<WebResponse> {
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<WebResponse> {
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;
}

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 { 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) {
@ -101,16 +102,29 @@ export async function setContext(kubeconfigPath: string) {
} }
} }
export async function run() { async function run() {
let kubeconfig = getKubeconfig(); try {
const runnerTempDirectory = process.env['RUNNER_TEMP']; // Using process.env until the core libs are updated let kubeconfig = '';
const kubeconfigPath = path.join(runnerTempDirectory, `kubeconfig_${Date.now()}`); const cluster_type = core.getInput('cluster-type', { required: true });
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); if (cluster_type == 'arc') {
fs.writeFileSync(kubeconfigPath, kubeconfig); await getArcKubeconfig().catch(ex => {
fs.chmodSync(kubeconfigPath, '600'); throw new Error('Error: Could not get the KUBECONFIG for arc cluster: ' + ex);
core.exportVariable('KUBECONFIG', kubeconfigPath); });
console.log('KUBECONFIG environment variable is set'); }
await setContext(kubeconfigPath); 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); run().catch(core.setFailed);