Vidya reddy pretty code (#53)

* updated action file with node16

* Code consistency using prettier and its workflow

* Enforce Prettier

* code fix

* code fix

* code fix

Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
This commit is contained in:
Vidya Reddy
2022-06-24 16:08:48 -07:00
committed by GitHub
parent e972a5b196
commit b6c5bf067a
37 changed files with 9089 additions and 9033 deletions

View File

@ -1,103 +1,103 @@
import * as actions from "@actions/exec";
import * as io from "@actions/io";
import { getRequiredInputError } from "../../tests/util";
import { getArcKubeconfig, KUBECONFIG_LOCATION } from "./arc";
import * as az from "./azCommands";
import * as actions from '@actions/exec'
import * as io from '@actions/io'
import {getRequiredInputError} from '../../tests/util'
import {getArcKubeconfig, KUBECONFIG_LOCATION} from './arc'
import * as az from './azCommands'
describe("Arc kubeconfig", () => {
test("it throws error without resource group", async () => {
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError("resource-group")
);
});
test("it throws error without cluster name", async () => {
process.env["INPUT_RESOURCE-GROUP"] = "group";
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError("cluster-name")
);
});
describe("runs az cli commands", () => {
const group = "group";
const name = "name";
const path = "path";
const kubeconfig = "kubeconfig";
beforeEach(() => {
process.env["INPUT_RESOURCE-GROUP"] = group;
process.env["INPUT_CLUSTER-NAME"] = name;
jest.spyOn(io, "which").mockImplementation(async () => path);
jest.spyOn(az, "runAzCliCommand").mockImplementation(async () => {});
jest
.spyOn(az, "runAzKubeconfigCommandBlocking")
.mockImplementation(async () => kubeconfig);
});
it("throws an error without method", async () => {
describe('Arc kubeconfig', () => {
test('it throws error without resource group', async () => {
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError("method")
);
});
getRequiredInputError('resource-group')
)
})
test('it throws error without cluster name', async () => {
process.env['INPUT_RESOURCE-GROUP'] = 'group'
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError('cluster-name')
)
})
describe('runs az cli commands', () => {
const group = 'group'
const name = 'name'
const path = 'path'
const kubeconfig = 'kubeconfig'
describe("service account method", () => {
beforeEach(() => {
process.env["INPUT_METHOD"] = "service-account";
});
process.env['INPUT_RESOURCE-GROUP'] = group
process.env['INPUT_CLUSTER-NAME'] = name
it("throws an error without token", async () => {
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError("token")
);
});
jest.spyOn(io, 'which').mockImplementation(async () => path)
jest.spyOn(az, 'runAzCliCommand').mockImplementation(async () => {})
jest
.spyOn(az, 'runAzKubeconfigCommandBlocking')
.mockImplementation(async () => kubeconfig)
})
it("gets the kubeconfig", async () => {
const token = "token";
process.env["INPUT_TOKEN"] = token;
it('throws an error without method', async () => {
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError('method')
)
})
expect(await getArcKubeconfig()).toBe(kubeconfig);
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
path,
[
"connectedk8s",
"proxy",
"-n",
name,
"-g",
group,
"--token",
token,
"-f",
KUBECONFIG_LOCATION,
],
KUBECONFIG_LOCATION
);
});
});
describe('service account method', () => {
beforeEach(() => {
process.env['INPUT_METHOD'] = 'service-account'
})
describe("service principal method", () => {
beforeEach(() => {
process.env["INPUT_METHOD"] = "service-principal";
});
it('throws an error without token', async () => {
await expect(getArcKubeconfig()).rejects.toThrow(
getRequiredInputError('token')
)
})
it("gets the kubeconfig", async () => {
expect(await getArcKubeconfig()).toBe(kubeconfig);
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
path,
[
"connectedk8s",
"proxy",
"-n",
name,
"-g",
group,
"-f",
KUBECONFIG_LOCATION,
],
KUBECONFIG_LOCATION
);
});
});
});
});
it('gets the kubeconfig', async () => {
const token = 'token'
process.env['INPUT_TOKEN'] = token
expect(await getArcKubeconfig()).toBe(kubeconfig)
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
path,
[
'connectedk8s',
'proxy',
'-n',
name,
'-g',
group,
'--token',
token,
'-f',
KUBECONFIG_LOCATION
],
KUBECONFIG_LOCATION
)
})
})
describe('service principal method', () => {
beforeEach(() => {
process.env['INPUT_METHOD'] = 'service-principal'
})
it('gets the kubeconfig', async () => {
expect(await getArcKubeconfig()).toBe(kubeconfig)
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
path,
[
'connectedk8s',
'proxy',
'-n',
name,
'-g',
group,
'-f',
KUBECONFIG_LOCATION
],
KUBECONFIG_LOCATION
)
})
})
})
})

View File

@ -1,68 +1,68 @@
import * as core from "@actions/core";
import * as io from "@actions/io";
import { Method, parseMethod } from "../types/method";
import * as path from "path";
import { runAzCliCommand, runAzKubeconfigCommandBlocking } from "./azCommands";
import * as core from '@actions/core'
import * as io from '@actions/io'
import {Method, parseMethod} from '../types/method'
import * as path from 'path'
import {runAzCliCommand, runAzKubeconfigCommandBlocking} from './azCommands'
const RUNNER_TEMP: string = process.env["RUNNER_TEMP"] || "";
const RUNNER_TEMP: string = process.env['RUNNER_TEMP'] || ''
export const KUBECONFIG_LOCATION: string = path.join(
RUNNER_TEMP,
`arc_kubeconfig_${Date.now()}`
);
RUNNER_TEMP,
`arc_kubeconfig_${Date.now()}`
)
/**
* Gets the kubeconfig based on provided method for an Arc Kubernetes cluster
* @returns The kubeconfig wrapped in a Promise
*/
export async function getArcKubeconfig(): Promise<string> {
const resourceGroupName = core.getInput("resource-group", { required: true });
const clusterName = core.getInput("cluster-name", { required: true });
const azPath = await io.which("az", true);
const resourceGroupName = core.getInput('resource-group', {required: true})
const clusterName = core.getInput('cluster-name', {required: true})
const azPath = await io.which('az', true)
const method: Method | undefined = parseMethod(
core.getInput("method", { required: true })
);
const method: Method | undefined = parseMethod(
core.getInput('method', {required: true})
)
await runAzCliCommand(azPath, ["extension", "add", "-n", "connectedk8s"]);
await runAzCliCommand(azPath, ['extension', 'add', '-n', 'connectedk8s'])
switch (method) {
case Method.SERVICE_ACCOUNT:
const saToken = core.getInput("token", { required: true });
return await runAzKubeconfigCommandBlocking(
azPath,
[
"connectedk8s",
"proxy",
"-n",
clusterName,
"-g",
resourceGroupName,
"--token",
saToken,
"-f",
KUBECONFIG_LOCATION,
],
KUBECONFIG_LOCATION
);
case Method.SERVICE_PRINCIPAL:
return await runAzKubeconfigCommandBlocking(
azPath,
[
"connectedk8s",
"proxy",
"-n",
clusterName,
"-g",
resourceGroupName,
"-f",
KUBECONFIG_LOCATION,
],
KUBECONFIG_LOCATION
);
case undefined:
core.warning("Defaulting to kubeconfig method");
case Method.KUBECONFIG:
default:
throw Error("Kubeconfig method not supported for Arc cluster");
}
switch (method) {
case Method.SERVICE_ACCOUNT:
const saToken = core.getInput('token', {required: true})
return await runAzKubeconfigCommandBlocking(
azPath,
[
'connectedk8s',
'proxy',
'-n',
clusterName,
'-g',
resourceGroupName,
'--token',
saToken,
'-f',
KUBECONFIG_LOCATION
],
KUBECONFIG_LOCATION
)
case Method.SERVICE_PRINCIPAL:
return await runAzKubeconfigCommandBlocking(
azPath,
[
'connectedk8s',
'proxy',
'-n',
clusterName,
'-g',
resourceGroupName,
'-f',
KUBECONFIG_LOCATION
],
KUBECONFIG_LOCATION
)
case undefined:
core.warning('Defaulting to kubeconfig method')
case Method.KUBECONFIG:
default:
throw Error('Kubeconfig method not supported for Arc cluster')
}
}

View File

@ -1,14 +1,14 @@
import * as actions from "@actions/exec";
import { runAzCliCommand } from "./azCommands";
import * as actions from '@actions/exec'
import {runAzCliCommand} from './azCommands'
describe("Az commands", () => {
test("it runs an az cli command", async () => {
const path = "path";
const args = ["args"];
describe('Az commands', () => {
test('it runs an az cli command', async () => {
const path = 'path'
const args = ['args']
jest.spyOn(actions, "exec").mockImplementation(async () => 0);
jest.spyOn(actions, 'exec').mockImplementation(async () => 0)
expect(await runAzCliCommand(path, args));
expect(actions.exec).toBeCalledWith(path, args, {});
});
});
expect(await runAzCliCommand(path, args))
expect(actions.exec).toBeCalledWith(path, args, {})
})
})

View File

@ -1,9 +1,9 @@
import * as fs from "fs";
import { ExecOptions } from "@actions/exec/lib/interfaces";
import { exec } from "@actions/exec";
import { spawn } from "child_process";
import * as fs from 'fs'
import {ExecOptions} from '@actions/exec/lib/interfaces'
import {exec} from '@actions/exec'
import {spawn} from 'child_process'
const AZ_TIMEOUT_SECONDS: number = 120;
const AZ_TIMEOUT_SECONDS: number = 120
/**
* Executes an az cli command
@ -12,11 +12,11 @@ const AZ_TIMEOUT_SECONDS: number = 120;
* @param options Optional options for the command execution
*/
export async function runAzCliCommand(
azPath: string,
args: string[],
options: ExecOptions = {}
azPath: string,
args: string[],
options: ExecOptions = {}
) {
await exec(azPath, args, options);
await exec(azPath, args, options)
}
/**
* Executes an az cli command that will set the kubeconfig
@ -26,19 +26,19 @@ export async function runAzCliCommand(
* @returns The contents of the kubeconfig
*/
export async function runAzKubeconfigCommandBlocking(
azPath: string,
args: string[],
kubeconfigPath: string
azPath: string,
args: string[],
kubeconfigPath: string
): Promise<string> {
const proc = spawn(azPath, args, {
detached: true,
stdio: "ignore",
});
proc.unref();
const proc = spawn(azPath, args, {
detached: true,
stdio: 'ignore'
})
proc.unref()
await sleep(AZ_TIMEOUT_SECONDS);
return fs.readFileSync(kubeconfigPath).toString();
await sleep(AZ_TIMEOUT_SECONDS)
return fs.readFileSync(kubeconfigPath).toString()
}
const sleep = (seconds: number) =>
new Promise((resolve) => setTimeout(resolve, seconds * 1000));
new Promise((resolve) => setTimeout(resolve, seconds * 1000))

View File

@ -1,150 +1,149 @@
import * as fs from "fs";
import { getRequiredInputError } from "../../tests/util";
import { createKubeconfig, getDefaultKubeconfig } from "./default";
import * as fs from 'fs'
import {getRequiredInputError} from '../../tests/util'
import {createKubeconfig, getDefaultKubeconfig} from './default'
describe("Default kubeconfig", () => {
test("it creates a kubeconfig with proper format", () => {
const certAuth = "certAuth";
const token = "token";
const clusterUrl = "clusterUrl";
describe('Default kubeconfig', () => {
test('it creates a kubeconfig with proper format', () => {
const certAuth = 'certAuth'
const token = 'token'
const clusterUrl = 'clusterUrl'
const kc = createKubeconfig(certAuth, token, clusterUrl);
const expected = JSON.stringify({
apiVersion: "v1",
kind: "Config",
clusters: [
{
name: "default",
cluster: {
server: clusterUrl,
"certificate-authority-data": certAuth,
"insecure-skip-tls-verify": false,
},
},
],
users: [{ name: "default-user", user: { token } }],
contexts: [
{
name: "loaded-context",
context: {
cluster: "default",
user: "default-user",
name: "loaded-context",
},
},
],
preferences: {},
"current-context": "loaded-context",
});
const kc = createKubeconfig(certAuth, token, clusterUrl)
const expected = JSON.stringify({
apiVersion: 'v1',
kind: 'Config',
clusters: [
{
name: 'default',
cluster: {
server: clusterUrl,
'certificate-authority-data': certAuth,
'insecure-skip-tls-verify': false
}
}
],
users: [{name: 'default-user', user: {token}}],
contexts: [
{
name: 'loaded-context',
context: {
cluster: 'default',
user: 'default-user',
name: 'loaded-context'
}
}
],
preferences: {},
'current-context': 'loaded-context'
})
expect(kc).toBe(expected)
})
expect(kc).toBe(expected);
});
test("it throws error without method", () => {
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("method")
);
});
describe("default method", () => {
beforeEach(() => {
process.env["INPUT_METHOD"] = "default";
});
test("it throws error without kubeconfig", () => {
test('it throws error without method', () => {
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("kubeconfig")
);
});
getRequiredInputError('method')
)
})
test("it gets default config through kubeconfig input", () => {
const kc = "example kc";
process.env["INPUT_KUBECONFIG"] = kc;
describe('default method', () => {
beforeEach(() => {
process.env['INPUT_METHOD'] = 'default'
})
expect(getDefaultKubeconfig()).toBe(kc);
});
});
test('it throws error without kubeconfig', () => {
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError('kubeconfig')
)
})
test("it defaults to default method", () => {
process.env["INPUT_METHOD"] = "unknown";
test('it gets default config through kubeconfig input', () => {
const kc = 'example kc'
process.env['INPUT_KUBECONFIG'] = kc
const kc = "example kc";
process.env["INPUT_KUBECONFIG"] = kc;
expect(getDefaultKubeconfig()).toBe(kc)
})
})
expect(getDefaultKubeconfig()).toBe(kc);
});
test('it defaults to default method', () => {
process.env['INPUT_METHOD'] = 'unknown'
test("it defaults to default method from service-principal", () => {
process.env["INPUT_METHOD"] = "service-principal";
const kc = 'example kc'
process.env['INPUT_KUBECONFIG'] = kc
const kc = "example kc";
process.env["INPUT_KUBECONFIG"] = kc;
expect(getDefaultKubeconfig()).toBe(kc)
})
expect(getDefaultKubeconfig()).toBe(kc);
});
test('it defaults to default method from service-principal', () => {
process.env['INPUT_METHOD'] = 'service-principal'
describe("service-account method", () => {
beforeEach(() => {
process.env["INPUT_METHOD"] = "service-account";
});
const kc = 'example kc'
process.env['INPUT_KUBECONFIG'] = kc
test("it throws error without cluster url", () => {
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("k8s-url")
);
});
expect(getDefaultKubeconfig()).toBe(kc)
})
test("it throws error without k8s secret", () => {
process.env["INPUT_K8S-URL"] = "url";
describe('service-account method', () => {
beforeEach(() => {
process.env['INPUT_METHOD'] = 'service-account'
})
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("k8s-secret")
);
});
test('it throws error without cluster url', () => {
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError('k8s-url')
)
})
test("it gets kubeconfig through service-account", () => {
const k8sUrl = "https://testing-dns-4za.hfp.earth.azmk8s.io:443";
const token = "ZXlKaGJHY2lPcUpTVXpJMU5pSX=";
const cert = "LS0tLS1CRUdJTiBDRWyUSUZJQ";
const k8sSecret = fs.readFileSync("tests/sample-secret.yml").toString();
test('it throws error without k8s secret', () => {
process.env['INPUT_K8S-URL'] = 'url'
process.env["INPUT_K8S-URL"] = k8sUrl;
process.env["INPUT_K8S-SECRET"] = k8sSecret;
expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError('k8s-secret')
)
})
const expectedConfig = JSON.stringify({
apiVersion: "v1",
kind: "Config",
clusters: [
{
name: "default",
cluster: {
server: k8sUrl,
"certificate-authority-data": cert,
"insecure-skip-tls-verify": false,
},
},
],
users: [
{
name: "default-user",
user: { token: Buffer.from(token, "base64").toString() },
},
],
contexts: [
{
name: "loaded-context",
context: {
cluster: "default",
user: "default-user",
name: "loaded-context",
},
},
],
preferences: {},
"current-context": "loaded-context",
});
test('it gets kubeconfig through service-account', () => {
const k8sUrl = 'https://testing-dns-4za.hfp.earth.azmk8s.io:443'
const token = 'ZXlKaGJHY2lPcUpTVXpJMU5pSX='
const cert = 'LS0tLS1CRUdJTiBDRWyUSUZJQ'
const k8sSecret = fs.readFileSync('tests/sample-secret.yml').toString()
expect(getDefaultKubeconfig()).toBe(expectedConfig);
});
});
});
process.env['INPUT_K8S-URL'] = k8sUrl
process.env['INPUT_K8S-SECRET'] = k8sSecret
const expectedConfig = JSON.stringify({
apiVersion: 'v1',
kind: 'Config',
clusters: [
{
name: 'default',
cluster: {
server: k8sUrl,
'certificate-authority-data': cert,
'insecure-skip-tls-verify': false
}
}
],
users: [
{
name: 'default-user',
user: {token: Buffer.from(token, 'base64').toString()}
}
],
contexts: [
{
name: 'loaded-context',
context: {
cluster: 'default',
user: 'default-user',
name: 'loaded-context'
}
}
],
preferences: {},
'current-context': 'loaded-context'
})
expect(getDefaultKubeconfig()).toBe(expectedConfig)
})
})
})

View File

@ -1,50 +1,52 @@
import * as core from "@actions/core";
import * as jsyaml from "js-yaml";
import { KubeConfig } from "@kubernetes/client-node";
import { K8sSecret, parseK8sSecret } from "../types/k8sSecret";
import { Method, parseMethod } from "../types/method";
import * as core from '@actions/core'
import * as jsyaml from 'js-yaml'
import {KubeConfig} from '@kubernetes/client-node'
import {K8sSecret, parseK8sSecret} from '../types/k8sSecret'
import {Method, parseMethod} from '../types/method'
/**
* Gets the kubeconfig based on provided method for a default Kubernetes cluster
* @returns The kubeconfig
*/
export function getDefaultKubeconfig(): string {
const method: Method | undefined = parseMethod(
core.getInput("method", { required: true })
);
const method: Method | undefined = parseMethod(
core.getInput('method', {required: true})
)
switch (method) {
case Method.SERVICE_ACCOUNT: {
const clusterUrl = core.getInput("k8s-url", { required: true });
core.debug(
"Found clusterUrl. Creating kubeconfig using certificate and token"
);
switch (method) {
case Method.SERVICE_ACCOUNT: {
const clusterUrl = core.getInput('k8s-url', {required: true})
core.debug(
'Found clusterUrl. Creating kubeconfig using certificate and token'
)
const k8sSecret: string = core.getInput("k8s-secret", {
required: true,
});
const parsedK8sSecret: K8sSecret = parseK8sSecret(jsyaml.load(k8sSecret));
const certAuth: string = parsedK8sSecret.data["ca.crt"];
const token: string = Buffer.from(
parsedK8sSecret.data.token,
"base64"
).toString();
const k8sSecret: string = core.getInput('k8s-secret', {
required: true
})
const parsedK8sSecret: K8sSecret = parseK8sSecret(
jsyaml.load(k8sSecret)
)
const certAuth: string = parsedK8sSecret.data['ca.crt']
const token: string = Buffer.from(
parsedK8sSecret.data.token,
'base64'
).toString()
return createKubeconfig(certAuth, token, clusterUrl);
}
case Method.SERVICE_PRINCIPAL: {
core.warning(
"Service Principal method not supported for default cluster type"
);
}
case undefined: {
core.warning("Defaulting to kubeconfig method");
}
default: {
core.debug("Setting context using kubeconfig");
return core.getInput("kubeconfig", { required: true });
}
}
return createKubeconfig(certAuth, token, clusterUrl)
}
case Method.SERVICE_PRINCIPAL: {
core.warning(
'Service Principal method not supported for default cluster type'
)
}
case undefined: {
core.warning('Defaulting to kubeconfig method')
}
default: {
core.debug('Setting context using kubeconfig')
return core.getInput('kubeconfig', {required: true})
}
}
}
/**
@ -55,22 +57,22 @@ export function getDefaultKubeconfig(): string {
* @returns The kubeconfig as a string
*/
export function createKubeconfig(
certAuth: string,
token: string,
clusterUrl: string
certAuth: string,
token: string,
clusterUrl: string
): string {
const kc = new KubeConfig();
kc.loadFromClusterAndUser(
{
name: "default",
server: clusterUrl,
caData: certAuth,
skipTLSVerify: false,
},
{
name: "default-user",
token,
}
);
return kc.exportConfig();
const kc = new KubeConfig()
kc.loadFromClusterAndUser(
{
name: 'default',
server: clusterUrl,
caData: certAuth,
skipTLSVerify: false
},
{
name: 'default-user',
token
}
)
return kc.exportConfig()
}

View File

@ -1,30 +1,30 @@
import { getRequiredInputError } from "../tests/util";
import { run } from "./run";
import fs from "fs";
import * as utils from "./utils";
import {getRequiredInputError} from '../tests/util'
import {run} from './run'
import fs from 'fs'
import * as utils from './utils'
describe("Run", () => {
it("throws error without cluster type", async () => {
await expect(run()).rejects.toThrow(getRequiredInputError("cluster-type"));
});
describe('Run', () => {
it('throws error without cluster type', async () => {
await expect(run()).rejects.toThrow(getRequiredInputError('cluster-type'))
})
it("writes kubeconfig and sets context", async () => {
const kubeconfig = "kubeconfig";
it('writes kubeconfig and sets context', async () => {
const kubeconfig = 'kubeconfig'
process.env["INPUT_CLUSTER-TYPE"] = "default";
process.env["RUNNER_TEMP"] = "/sample/path";
process.env['INPUT_CLUSTER-TYPE'] = 'default'
process.env['RUNNER_TEMP'] = '/sample/path'
jest
.spyOn(utils, "getKubeconfig")
.mockImplementation(async () => kubeconfig);
jest.spyOn(fs, "writeFileSync").mockImplementation(() => {});
jest.spyOn(fs, "chmodSync").mockImplementation(() => {});
jest.spyOn(utils, "setContext").mockImplementation(() => kubeconfig);
jest
.spyOn(utils, 'getKubeconfig')
.mockImplementation(async () => kubeconfig)
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {})
jest.spyOn(fs, 'chmodSync').mockImplementation(() => {})
jest.spyOn(utils, 'setContext').mockImplementation(() => kubeconfig)
expect(await run());
expect(utils.getKubeconfig).toHaveBeenCalled();
expect(fs.writeFileSync).toHaveBeenCalled();
expect(fs.chmodSync).toHaveBeenCalled();
expect(utils.setContext).toHaveBeenCalled();
});
});
expect(await run())
expect(utils.getKubeconfig).toHaveBeenCalled()
expect(fs.writeFileSync).toHaveBeenCalled()
expect(fs.chmodSync).toHaveBeenCalled()
expect(utils.setContext).toHaveBeenCalled()
})
})

View File

@ -1,36 +1,36 @@
import * as core from "@actions/core";
import * as path from "path";
import * as fs from "fs";
import { Cluster, parseCluster } from "./types/cluster";
import { setContext, getKubeconfig } from "./utils";
import * as core from '@actions/core'
import * as path from 'path'
import * as fs from 'fs'
import {Cluster, parseCluster} from './types/cluster'
import {setContext, getKubeconfig} from './utils'
/**
* Sets the Kubernetes context based on supplied action inputs
*/
export async function run() {
// get inputs
const clusterType: Cluster | undefined = parseCluster(
core.getInput("cluster-type", {
required: true,
})
);
const runnerTempDirectory: string = process.env["RUNNER_TEMP"];
const kubeconfigPath: string = path.join(
runnerTempDirectory,
`kubeconfig_${Date.now()}`
);
// get inputs
const clusterType: Cluster | undefined = parseCluster(
core.getInput('cluster-type', {
required: true
})
)
const runnerTempDirectory: string = process.env['RUNNER_TEMP']
const kubeconfigPath: string = path.join(
runnerTempDirectory,
`kubeconfig_${Date.now()}`
)
// get kubeconfig and update context
const kubeconfig: string = await getKubeconfig(clusterType);
const kubeconfigWithContext: string = setContext(kubeconfig);
// get kubeconfig and update context
const kubeconfig: string = await getKubeconfig(clusterType)
const kubeconfigWithContext: string = setContext(kubeconfig)
// output kubeconfig
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`);
fs.writeFileSync(kubeconfigPath, kubeconfigWithContext);
fs.chmodSync(kubeconfigPath, "600");
core.debug("Setting KUBECONFIG environment variable");
core.exportVariable("KUBECONFIG", kubeconfigPath);
// output kubeconfig
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`)
fs.writeFileSync(kubeconfigPath, kubeconfigWithContext)
fs.chmodSync(kubeconfigPath, '600')
core.debug('Setting KUBECONFIG environment variable')
core.exportVariable('KUBECONFIG', kubeconfigPath)
}
// Run the application
run().catch(core.setFailed);
run().catch(core.setFailed)

View File

@ -1,24 +1,24 @@
import { Cluster, parseCluster } from "./cluster";
import {Cluster, parseCluster} from './cluster'
describe("Cluster type", () => {
test("it has required values", () => {
const vals = <any>Object.values(Cluster);
expect(vals.includes("arc")).toBe(true);
expect(vals.includes("generic")).toBe(true);
});
describe('Cluster type', () => {
test('it has required values', () => {
const vals = <any>Object.values(Cluster)
expect(vals.includes('arc')).toBe(true)
expect(vals.includes('generic')).toBe(true)
})
test("it can parse valid values from a string", () => {
expect(parseCluster("arc")).toBe(Cluster.ARC);
expect(parseCluster("Arc")).toBe(Cluster.ARC);
expect(parseCluster("ARC")).toBe(Cluster.ARC);
test('it can parse valid values from a string', () => {
expect(parseCluster('arc')).toBe(Cluster.ARC)
expect(parseCluster('Arc')).toBe(Cluster.ARC)
expect(parseCluster('ARC')).toBe(Cluster.ARC)
expect(parseCluster("generic")).toBe(Cluster.GENERIC);
expect(parseCluster("Generic")).toBe(Cluster.GENERIC);
expect(parseCluster("GENERIC")).toBe(Cluster.GENERIC);
});
expect(parseCluster('generic')).toBe(Cluster.GENERIC)
expect(parseCluster('Generic')).toBe(Cluster.GENERIC)
expect(parseCluster('GENERIC')).toBe(Cluster.GENERIC)
})
test("it will return undefined if it can't parse values from a string", () => {
expect(parseCluster("invalid")).toBe(undefined);
expect(parseCluster("unsupportedType")).toBe(undefined);
});
});
test("it will return undefined if it can't parse values from a string", () => {
expect(parseCluster('invalid')).toBe(undefined)
expect(parseCluster('unsupportedType')).toBe(undefined)
})
})

View File

@ -1,6 +1,6 @@
export enum Cluster {
ARC = "arc",
GENERIC = "generic",
ARC = 'arc',
GENERIC = 'generic'
}
/**
@ -9,8 +9,8 @@ export enum Cluster {
* @returns The Cluster enum or undefined if it can't be parsed
*/
export const parseCluster = (str: string): Cluster | undefined =>
Cluster[
Object.keys(Cluster).filter(
(k) => Cluster[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Cluster
];
Cluster[
Object.keys(Cluster).filter(
(k) => Cluster[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Cluster
]

View File

@ -1,33 +1,33 @@
import { parseK8sSecret, K8sSecret } from "./k8sSecret";
import {parseK8sSecret, K8sSecret} from './k8sSecret'
describe("K8sSecret type", () => {
describe("Parsing from any", () => {
test("it returns a type guarded secret", () => {
const secret = { data: { token: "token", "ca.crt": "cert" } };
expect(() => parseK8sSecret(secret)).not.toThrow();
});
describe('K8sSecret type', () => {
describe('Parsing from any', () => {
test('it returns a type guarded secret', () => {
const secret = {data: {token: 'token', 'ca.crt': 'cert'}}
expect(() => parseK8sSecret(secret)).not.toThrow()
})
test("it throws an error when secret not provided", () => {
expect(() => parseK8sSecret(undefined)).toThrow();
});
test('it throws an error when secret not provided', () => {
expect(() => parseK8sSecret(undefined)).toThrow()
})
test("it throws an error when there is no data field", () => {
const secret = {};
expect(() => parseK8sSecret(secret)).toThrow();
});
test('it throws an error when there is no data field', () => {
const secret = {}
expect(() => parseK8sSecret(secret)).toThrow()
})
test("it throws an error when there is no token", () => {
const secret = {
data: {
"ca.crt": "cert",
},
};
expect(() => parseK8sSecret(secret)).toThrow();
});
test('it throws an error when there is no token', () => {
const secret = {
data: {
'ca.crt': 'cert'
}
}
expect(() => parseK8sSecret(secret)).toThrow()
})
test("it throws an error when there is no ca.crt field", () => {
const secret = { data: { token: "token" } };
expect(() => parseK8sSecret(secret)).toThrow();
});
});
});
test('it throws an error when there is no ca.crt field', () => {
const secret = {data: {token: 'token'}}
expect(() => parseK8sSecret(secret)).toThrow()
})
})
})

View File

@ -1,10 +1,10 @@
import * as util from "util";
import * as util from 'util'
export interface K8sSecret {
data: {
token: string;
"ca.crt": string;
};
data: {
token: string
'ca.crt': string
}
}
/**
@ -13,13 +13,13 @@ export interface K8sSecret {
* @returns A type guarded K8sSecret
*/
export function parseK8sSecret(secret: any): K8sSecret {
if (!secret) throw Error("K8s secret yaml is invalid");
if (!secret.data) throw k8sSecretMissingFieldError("data");
if (!secret.data.token) throw k8sSecretMissingFieldError("token");
if (!secret.data["ca.crt"]) throw k8sSecretMissingFieldError("ca.crt");
if (!secret) throw Error('K8s secret yaml is invalid')
if (!secret.data) throw k8sSecretMissingFieldError('data')
if (!secret.data.token) throw k8sSecretMissingFieldError('token')
if (!secret.data['ca.crt']) throw k8sSecretMissingFieldError('ca.crt')
return secret as K8sSecret;
return secret as K8sSecret
}
const k8sSecretMissingFieldError = (field: string): Error =>
Error(util.format("K8s secret yaml does not contain %s field", field));
Error(util.format('K8s secret yaml does not contain %s field', field))

View File

@ -1,29 +1,29 @@
import { Method, parseMethod } from "./method";
import {Method, parseMethod} from './method'
describe("Method type", () => {
test("it has required values", () => {
const vals = <any>Object.values(Method);
expect(vals.includes("kubeconfig")).toBe(true);
expect(vals.includes("service-account")).toBe(true);
expect(vals.includes("service-principal")).toBe(true);
});
describe('Method type', () => {
test('it has required values', () => {
const vals = <any>Object.values(Method)
expect(vals.includes('kubeconfig')).toBe(true)
expect(vals.includes('service-account')).toBe(true)
expect(vals.includes('service-principal')).toBe(true)
})
test("it can parse valid values from a string", () => {
expect(parseMethod("kubeconfig")).toBe(Method.KUBECONFIG);
expect(parseMethod("Kubeconfig")).toBe(Method.KUBECONFIG);
expect(parseMethod("KUBECONFIG")).toBe(Method.KUBECONFIG);
test('it can parse valid values from a string', () => {
expect(parseMethod('kubeconfig')).toBe(Method.KUBECONFIG)
expect(parseMethod('Kubeconfig')).toBe(Method.KUBECONFIG)
expect(parseMethod('KUBECONFIG')).toBe(Method.KUBECONFIG)
expect(parseMethod("service-account")).toBe(Method.SERVICE_ACCOUNT);
expect(parseMethod("Service-Account")).toBe(Method.SERVICE_ACCOUNT);
expect(parseMethod("SERVICE-ACCOUNT")).toBe(Method.SERVICE_ACCOUNT);
expect(parseMethod('service-account')).toBe(Method.SERVICE_ACCOUNT)
expect(parseMethod('Service-Account')).toBe(Method.SERVICE_ACCOUNT)
expect(parseMethod('SERVICE-ACCOUNT')).toBe(Method.SERVICE_ACCOUNT)
expect(parseMethod("service-principal")).toBe(Method.SERVICE_PRINCIPAL);
expect(parseMethod("Service-Principal")).toBe(Method.SERVICE_PRINCIPAL);
expect(parseMethod("SERVICE-PRINCIPAL")).toBe(Method.SERVICE_PRINCIPAL);
});
expect(parseMethod('service-principal')).toBe(Method.SERVICE_PRINCIPAL)
expect(parseMethod('Service-Principal')).toBe(Method.SERVICE_PRINCIPAL)
expect(parseMethod('SERVICE-PRINCIPAL')).toBe(Method.SERVICE_PRINCIPAL)
})
test("it will return undefined if it can't parse values from a string", () => {
expect(parseMethod("invalid")).toBe(undefined);
expect(parseMethod("unsupportedType")).toBe(undefined);
});
});
test("it will return undefined if it can't parse values from a string", () => {
expect(parseMethod('invalid')).toBe(undefined)
expect(parseMethod('unsupportedType')).toBe(undefined)
})
})

View File

@ -1,7 +1,7 @@
export enum Method {
KUBECONFIG = "kubeconfig",
SERVICE_ACCOUNT = "service-account",
SERVICE_PRINCIPAL = "service-principal",
KUBECONFIG = 'kubeconfig',
SERVICE_ACCOUNT = 'service-account',
SERVICE_PRINCIPAL = 'service-principal'
}
/**
@ -10,8 +10,8 @@ export enum Method {
* @returns The Method enum or undefined if it can't be parsed
*/
export const parseMethod = (str: string): Method | undefined =>
Method[
Object.keys(Method).filter(
(k) => Method[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Method
];
Method[
Object.keys(Method).filter(
(k) => Method[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Method
]

View File

@ -1,47 +1,47 @@
import fs from "fs";
import * as arc from "./kubeconfigs/arc";
import * as def from "./kubeconfigs/default";
import { Cluster } from "./types/cluster";
import { getKubeconfig, setContext } from "./utils";
import fs from 'fs'
import * as arc from './kubeconfigs/arc'
import * as def from './kubeconfigs/default'
import {Cluster} from './types/cluster'
import {getKubeconfig, setContext} from './utils'
describe("Utils", () => {
describe("get kubeconfig", () => {
test("it gets arc kubeconfig when type is arc", async () => {
const arcKubeconfig = "arckubeconfig";
jest
.spyOn(arc, "getArcKubeconfig")
.mockImplementation(async () => arcKubeconfig);
describe('Utils', () => {
describe('get kubeconfig', () => {
test('it gets arc kubeconfig when type is arc', async () => {
const arcKubeconfig = 'arckubeconfig'
jest
.spyOn(arc, 'getArcKubeconfig')
.mockImplementation(async () => arcKubeconfig)
expect(await getKubeconfig(Cluster.ARC)).toBe(arcKubeconfig);
});
expect(await getKubeconfig(Cluster.ARC)).toBe(arcKubeconfig)
})
test("it defaults to default kubeconfig", async () => {
const defaultKubeconfig = "arckubeconfig";
jest
.spyOn(def, "getDefaultKubeconfig")
.mockImplementation(() => defaultKubeconfig);
test('it defaults to default kubeconfig', async () => {
const defaultKubeconfig = 'arckubeconfig'
jest
.spyOn(def, 'getDefaultKubeconfig')
.mockImplementation(() => defaultKubeconfig)
expect(await getKubeconfig(undefined)).toBe(defaultKubeconfig);
expect(await getKubeconfig(Cluster.GENERIC)).toBe(defaultKubeconfig);
});
});
expect(await getKubeconfig(undefined)).toBe(defaultKubeconfig)
expect(await getKubeconfig(Cluster.GENERIC)).toBe(defaultKubeconfig)
})
})
describe("set context", () => {
const kc = fs.readFileSync("tests/sample-kubeconfig.yml").toString();
describe('set context', () => {
const kc = fs.readFileSync('tests/sample-kubeconfig.yml').toString()
test("it doesn't change kubeconfig without context", () => {
expect(setContext(kc)).toBe(kc);
});
test("it doesn't change kubeconfig without context", () => {
expect(setContext(kc)).toBe(kc)
})
test("it writes the context to the kubeconfig", () => {
process.env["INPUT_CONTEXT"] = "example";
test('it writes the context to the kubeconfig', () => {
process.env['INPUT_CONTEXT'] = 'example'
const received = JSON.parse(setContext(kc));
const expectedKc = JSON.parse(
fs.readFileSync("tests/expected-kubeconfig.json").toString()
);
const received = JSON.parse(setContext(kc))
const expectedKc = JSON.parse(
fs.readFileSync('tests/expected-kubeconfig.json').toString()
)
expect(received).toMatchObject(expectedKc);
});
});
});
expect(received).toMatchObject(expectedKc)
})
})
})

View File

@ -1,9 +1,9 @@
import * as core from "@actions/core";
import * as fs from "fs";
import { KubeConfig } from "@kubernetes/client-node";
import { getDefaultKubeconfig } from "./kubeconfigs/default";
import { getArcKubeconfig } from "./kubeconfigs/arc";
import { Cluster } from "./types/cluster";
import * as core from '@actions/core'
import * as fs from 'fs'
import {KubeConfig} from '@kubernetes/client-node'
import {getDefaultKubeconfig} from './kubeconfigs/default'
import {getArcKubeconfig} from './kubeconfigs/arc'
import {Cluster} from './types/cluster'
/**
* Gets the kubeconfig based on Kubernetes cluster type
@ -11,19 +11,19 @@ import { Cluster } from "./types/cluster";
* @returns A promise of the kubeconfig
*/
export async function getKubeconfig(
type: Cluster | undefined
type: Cluster | undefined
): Promise<string> {
switch (type) {
case Cluster.ARC: {
return await getArcKubeconfig();
}
case undefined: {
core.warning("Cluster type not recognized. Defaulting to generic.");
}
default: {
return getDefaultKubeconfig();
}
}
switch (type) {
case Cluster.ARC: {
return await getArcKubeconfig()
}
case undefined: {
core.warning('Cluster type not recognized. Defaulting to generic.')
}
default: {
return getDefaultKubeconfig()
}
}
}
/**
@ -32,17 +32,17 @@ export async function getKubeconfig(
* @returns Updated kubeconfig with the context
*/
export function setContext(kubeconfig: string): string {
const context: string = core.getInput("context");
if (!context) {
core.debug("Can't set context because context is unspecified.");
return kubeconfig;
}
const context: string = core.getInput('context')
if (!context) {
core.debug("Can't set context because context is unspecified.")
return kubeconfig
}
// load current kubeconfig
const kc = new KubeConfig();
kc.loadFromString(kubeconfig);
// load current kubeconfig
const kc = new KubeConfig()
kc.loadFromString(kubeconfig)
// update kubeconfig
kc.setCurrentContext(context);
return kc.exportConfig();
// update kubeconfig
kc.setCurrentContext(context)
return kc.exportConfig()
}