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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 9089 additions and 9033 deletions

View File

@ -1,7 +1,7 @@
--- ---
name: "Issue: Bug Report / Feature Request" name: 'Issue: Bug Report / Feature Request'
about: Create a report to help us improve about: Create a report to help us improve
title: "" title: ''
labels: need-to-triage labels: need-to-triage
assignees: "@Azure/aks-atlanta" assignees: '@Azure/aks-atlanta'
--- ---

View File

@ -3,7 +3,7 @@ name: Setting Default Labels
# Controls when the action will run. # Controls when the action will run.
on: on:
schedule: schedule:
- cron: "0 0/3 * * *" - cron: '0 0/3 * * *'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel # A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs: jobs:
@ -17,19 +17,19 @@ jobs:
name: Setting Issue as Idle name: Setting Issue as Idle
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue is idle because it has been open for 14 days with no activity." stale-issue-message: 'This issue is idle because it has been open for 14 days with no activity.'
stale-issue-label: "idle" stale-issue-label: 'idle'
days-before-stale: 14 days-before-stale: 14
days-before-close: -1 days-before-close: -1
operations-per-run: 100 operations-per-run: 100
exempt-issue-labels: "backlog" exempt-issue-labels: 'backlog'
- uses: actions/stale@v3 - uses: actions/stale@v3
name: Setting PR as Idle name: Setting PR as Idle
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-pr-message: "This PR is idle because it has been open for 14 days with no activity." stale-pr-message: 'This PR is idle because it has been open for 14 days with no activity.'
stale-pr-label: "idle" stale-pr-label: 'idle'
days-before-stale: 14 days-before-stale: 14
days-before-close: -1 days-before-close: -1
operations-per-run: 100 operations-per-run: 100

View File

@ -3,11 +3,11 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- "releases/*" - 'releases/*'
push: push:
branches: branches:
- main - main
- "releases/*" - 'releases/*'
jobs: jobs:
kubeconfig-method-integration-test: kubeconfig-method-integration-test:

18
.github/workflows/prettify-code.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: 'Run prettify'
on:
pull_request:
push:
branches: [main]
jobs:
prettier:
name: Prettier Check
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Enforce Prettier
uses: actionsx/prettier@v2
with:
args: --check .

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
release: release:
description: "Define release version (ex: v1, v2, v3)" description: 'Define release version (ex: v1, v2, v3)'
required: true required: true
jobs: jobs:

View File

@ -3,11 +3,11 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- "releases/*" - 'releases/*'
push: push:
branches: branches:
- main - main
- "releases/*" - 'releases/*'
jobs: jobs:
unit-test: unit-test:

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
# dependencies
/node_modules
coverage
/lib

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"trailingComma": "none",
"bracketSpacing": false,
"semi": false,
"tabWidth": 3,
"singleQuote": true,
"printWidth": 80
}

View File

@ -80,7 +80,7 @@ kubectl get secret <service-account-secret-name> -n <namespace> -o yaml
cluster-type: arc cluster-type: arc
cluster-name: <cluster-name> cluster-name: <cluster-name>
resource-group: <resource-group> resource-group: <resource-group>
token: "${{ secrets.SA_TOKEN }}" token: '${{ secrets.SA_TOKEN }}'
``` ```
### Service principal approach for arc cluster ### Service principal approach for arc cluster

View File

@ -4,7 +4,7 @@
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/). Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](<https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)>) of a security vulnerability, please report it to us as described below.
## Reporting Security Issues ## Reporting Security Issues
@ -14,13 +14,13 @@ You should receive a response within 24 hours. If for some reason you do not, pl
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue - Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL) - The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue - Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue - Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible) - Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue - Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly. This information will help us triage your report more quickly.

View File

@ -1,39 +1,39 @@
name: "Kubernetes Set Context" name: 'Kubernetes Set Context'
description: "Set the context of a target Kubernetes cluster and export the kubeconfig which is used by subsequent actions" description: 'Set the context of a target Kubernetes cluster and export the kubeconfig which is used by subsequent actions'
inputs: inputs:
# Please ensure you have used azure/login in the workflow before this action # Please ensure you have used azure/login in the workflow before this action
cluster-type: cluster-type:
description: "Acceptable values: generic or arc" description: 'Acceptable values: generic or arc'
required: true required: true
default: "generic" default: 'generic'
method: method:
description: "Acceptable values: kubeconfig or service-account or service-principal" description: 'Acceptable values: kubeconfig or service-account or service-principal'
required: true required: true
default: "kubeconfig" default: 'kubeconfig'
kubeconfig: kubeconfig:
description: "Contents of kubeconfig file" description: 'Contents of kubeconfig file'
required: false required: false
context: context:
description: "If your kubeconfig has multiple contexts, use this field to use a specific context, otherwise the default one would be chosen" description: 'If your kubeconfig has multiple contexts, use this field to use a specific context, otherwise the default one would be chosen'
required: false required: false
k8s-url: k8s-url:
description: "Cluster Url" description: 'Cluster Url'
required: false required: false
k8s-secret: k8s-secret:
description: "Service account secret (run kubectl get serviceaccounts <service-account-name> -o yaml and copy the service-account-secret-name)" description: 'Service account secret (run kubectl get serviceaccounts <service-account-name> -o yaml and copy the service-account-secret-name)'
required: false required: false
token: token:
description: "Token extracted from the secret of service account (should be base 64 decoded)" description: 'Token extracted from the secret of service account (should be base 64 decoded)'
required: false required: false
resource-group: resource-group:
description: "Azure resource group name" description: 'Azure resource group name'
required: false required: false
cluster-name: cluster-name:
description: "Azure connected cluster name" description: 'Azure connected cluster name'
required: false required: false
branding: branding:
color: "blue" color: 'blue'
runs: runs:
using: "node16" using: 'node16'
main: "lib/run.js" main: 'lib/run.js'

View File

@ -2,11 +2,11 @@ module.exports = {
restoreMocks: true, restoreMocks: true,
clearMocks: true, clearMocks: true,
resetMocks: true, resetMocks: true,
moduleFileExtensions: ["js", "ts"], moduleFileExtensions: ['js', 'ts'],
testEnvironment: "node", testEnvironment: 'node',
testMatch: ["**/*.test.ts"], testMatch: ['**/*.test.ts'],
transform: { transform: {
"^.+\\.ts$": "ts-jest", '^.+\\.ts$': 'ts-jest'
}, },
verbose: true, verbose: true,
coverageThreshold: { coverageThreshold: {
@ -14,7 +14,7 @@ module.exports = {
branches: 0, branches: 0,
functions: 40, functions: 40,
lines: 22, lines: 22,
statements: 22, statements: 22
}, }
}, }
}; }

22
package-lock.json generated
View File

@ -20,6 +20,7 @@
"@types/js-yaml": "^4.0.4", "@types/js-yaml": "^4.0.4",
"@types/node": "^16.0.0", "@types/node": "^16.0.0",
"jest": "^28.1.1", "jest": "^28.1.1",
"prettier": "2.7.1",
"ts-jest": "^28.0.5", "ts-jest": "^28.0.5",
"typescript": "4.7.4" "typescript": "4.7.4"
} }
@ -4390,6 +4391,21 @@
"uuid": "bin/uuid" "uuid": "bin/uuid"
} }
}, },
"node_modules/prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/v8-to-istanbul": { "node_modules/v8-to-istanbul": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz",
@ -7359,6 +7375,12 @@
"find-up": "^4.0.0" "find-up": "^4.0.0"
} }
}, },
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true
},
"pretty-format": { "pretty-format": {
"version": "28.1.1", "version": "28.1.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.1.tgz",

View File

@ -6,7 +6,9 @@
"scripts": { "scripts": {
"build": "tsc --outDir ./lib --rootDir ./src", "build": "tsc --outDir ./lib --rootDir ./src",
"test": "jest", "test": "jest",
"test-coverage": "jest --coverage" "test-coverage": "jest --coverage",
"format": "prettier --write .",
"format-check": "prettier --check ."
}, },
"keywords": [ "keywords": [
"actions", "actions",
@ -27,6 +29,7 @@
"@types/js-yaml": "^4.0.4", "@types/js-yaml": "^4.0.4",
"@types/node": "^16.0.0", "@types/node": "^16.0.0",
"jest": "^28.1.1", "jest": "^28.1.1",
"prettier": "2.7.1",
"ts-jest": "^28.0.5", "ts-jest": "^28.0.5",
"typescript": "4.7.4" "typescript": "4.7.4"
} }

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
import * as fs from "fs"; import * as fs from 'fs'
import { ExecOptions } from "@actions/exec/lib/interfaces"; import {ExecOptions} from '@actions/exec/lib/interfaces'
import { exec } from "@actions/exec"; import {exec} from '@actions/exec'
import { spawn } from "child_process"; import {spawn} from 'child_process'
const AZ_TIMEOUT_SECONDS: number = 120; const AZ_TIMEOUT_SECONDS: number = 120
/** /**
* Executes an az cli command * Executes an az cli command
@ -16,7 +16,7 @@ export async function runAzCliCommand(
args: string[], args: string[],
options: ExecOptions = {} options: ExecOptions = {}
) { ) {
await exec(azPath, args, options); await exec(azPath, args, options)
} }
/** /**
* Executes an az cli command that will set the kubeconfig * Executes an az cli command that will set the kubeconfig
@ -32,13 +32,13 @@ export async function runAzKubeconfigCommandBlocking(
): Promise<string> { ): Promise<string> {
const proc = spawn(azPath, args, { const proc = spawn(azPath, args, {
detached: true, detached: true,
stdio: "ignore", stdio: 'ignore'
}); })
proc.unref(); proc.unref()
await sleep(AZ_TIMEOUT_SECONDS); await sleep(AZ_TIMEOUT_SECONDS)
return fs.readFileSync(kubeconfigPath).toString(); return fs.readFileSync(kubeconfigPath).toString()
} }
const sleep = (seconds: number) => 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 * as fs from 'fs'
import { getRequiredInputError } from "../../tests/util"; import {getRequiredInputError} from '../../tests/util'
import { createKubeconfig, getDefaultKubeconfig } from "./default"; import {createKubeconfig, getDefaultKubeconfig} from './default'
describe("Default kubeconfig", () => { describe('Default kubeconfig', () => {
test("it creates a kubeconfig with proper format", () => { test('it creates a kubeconfig with proper format', () => {
const certAuth = "certAuth"; const certAuth = 'certAuth'
const token = "token"; const token = 'token'
const clusterUrl = "clusterUrl"; const clusterUrl = 'clusterUrl'
const kc = createKubeconfig(certAuth, token, clusterUrl); const kc = createKubeconfig(certAuth, token, clusterUrl)
const expected = JSON.stringify({ const expected = JSON.stringify({
apiVersion: "v1", apiVersion: 'v1',
kind: "Config", kind: 'Config',
clusters: [ clusters: [
{ {
name: "default", name: 'default',
cluster: { cluster: {
server: clusterUrl, server: clusterUrl,
"certificate-authority-data": certAuth, 'certificate-authority-data': certAuth,
"insecure-skip-tls-verify": false, 'insecure-skip-tls-verify': false
}, }
}, }
], ],
users: [{ name: "default-user", user: { token } }], users: [{name: 'default-user', user: {token}}],
contexts: [ contexts: [
{ {
name: "loaded-context", name: 'loaded-context',
context: { context: {
cluster: "default", cluster: 'default',
user: "default-user", user: 'default-user',
name: "loaded-context", name: 'loaded-context'
}, }
}, }
], ],
preferences: {}, preferences: {},
"current-context": "loaded-context", 'current-context': 'loaded-context'
}); })
expect(kc).toBe(expected)
})
expect(kc).toBe(expected); test('it throws error without method', () => {
});
test("it throws error without method", () => {
expect(() => getDefaultKubeconfig()).toThrow( expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("method") getRequiredInputError('method')
); )
}); })
describe("default method", () => { describe('default method', () => {
beforeEach(() => { beforeEach(() => {
process.env["INPUT_METHOD"] = "default"; process.env['INPUT_METHOD'] = 'default'
}); })
test("it throws error without kubeconfig", () => { test('it throws error without kubeconfig', () => {
expect(() => getDefaultKubeconfig()).toThrow( expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("kubeconfig") getRequiredInputError('kubeconfig')
); )
}); })
test("it gets default config through kubeconfig input", () => { test('it gets default config through kubeconfig input', () => {
const kc = "example kc"; const kc = 'example kc'
process.env["INPUT_KUBECONFIG"] = kc; process.env['INPUT_KUBECONFIG'] = kc
expect(getDefaultKubeconfig()).toBe(kc); expect(getDefaultKubeconfig()).toBe(kc)
}); })
}); })
test("it defaults to default method", () => { test('it defaults to default method', () => {
process.env["INPUT_METHOD"] = "unknown"; process.env['INPUT_METHOD'] = 'unknown'
const kc = "example kc"; const kc = 'example kc'
process.env["INPUT_KUBECONFIG"] = kc; process.env['INPUT_KUBECONFIG'] = kc
expect(getDefaultKubeconfig()).toBe(kc); expect(getDefaultKubeconfig()).toBe(kc)
}); })
test("it defaults to default method from service-principal", () => { test('it defaults to default method from service-principal', () => {
process.env["INPUT_METHOD"] = "service-principal"; process.env['INPUT_METHOD'] = 'service-principal'
const kc = "example kc"; const kc = 'example kc'
process.env["INPUT_KUBECONFIG"] = kc; process.env['INPUT_KUBECONFIG'] = kc
expect(getDefaultKubeconfig()).toBe(kc); expect(getDefaultKubeconfig()).toBe(kc)
}); })
describe("service-account method", () => { describe('service-account method', () => {
beforeEach(() => { beforeEach(() => {
process.env["INPUT_METHOD"] = "service-account"; process.env['INPUT_METHOD'] = 'service-account'
}); })
test("it throws error without cluster url", () => { test('it throws error without cluster url', () => {
expect(() => getDefaultKubeconfig()).toThrow( expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("k8s-url") getRequiredInputError('k8s-url')
); )
}); })
test("it throws error without k8s secret", () => { test('it throws error without k8s secret', () => {
process.env["INPUT_K8S-URL"] = "url"; process.env['INPUT_K8S-URL'] = 'url'
expect(() => getDefaultKubeconfig()).toThrow( expect(() => getDefaultKubeconfig()).toThrow(
getRequiredInputError("k8s-secret") getRequiredInputError('k8s-secret')
); )
}); })
test("it gets kubeconfig through service-account", () => { test('it gets kubeconfig through service-account', () => {
const k8sUrl = "https://testing-dns-4za.hfp.earth.azmk8s.io:443"; const k8sUrl = 'https://testing-dns-4za.hfp.earth.azmk8s.io:443'
const token = "ZXlKaGJHY2lPcUpTVXpJMU5pSX="; const token = 'ZXlKaGJHY2lPcUpTVXpJMU5pSX='
const cert = "LS0tLS1CRUdJTiBDRWyUSUZJQ"; const cert = 'LS0tLS1CRUdJTiBDRWyUSUZJQ'
const k8sSecret = fs.readFileSync("tests/sample-secret.yml").toString(); const k8sSecret = fs.readFileSync('tests/sample-secret.yml').toString()
process.env["INPUT_K8S-URL"] = k8sUrl; process.env['INPUT_K8S-URL'] = k8sUrl
process.env["INPUT_K8S-SECRET"] = k8sSecret; process.env['INPUT_K8S-SECRET'] = k8sSecret
const expectedConfig = JSON.stringify({ const expectedConfig = JSON.stringify({
apiVersion: "v1", apiVersion: 'v1',
kind: "Config", kind: 'Config',
clusters: [ clusters: [
{ {
name: "default", name: 'default',
cluster: { cluster: {
server: k8sUrl, server: k8sUrl,
"certificate-authority-data": cert, 'certificate-authority-data': cert,
"insecure-skip-tls-verify": false, 'insecure-skip-tls-verify': false
}, }
}, }
], ],
users: [ users: [
{ {
name: "default-user", name: 'default-user',
user: { token: Buffer.from(token, "base64").toString() }, user: {token: Buffer.from(token, 'base64').toString()}
}, }
], ],
contexts: [ contexts: [
{ {
name: "loaded-context", name: 'loaded-context',
context: { context: {
cluster: "default", cluster: 'default',
user: "default-user", user: 'default-user',
name: "loaded-context", name: 'loaded-context'
}, }
}, }
], ],
preferences: {}, preferences: {},
"current-context": "loaded-context", 'current-context': 'loaded-context'
}); })
expect(getDefaultKubeconfig()).toBe(expectedConfig); expect(getDefaultKubeconfig()).toBe(expectedConfig)
}); })
}); })
}); })

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import * as core from "@actions/core"; import * as core from '@actions/core'
import * as path from "path"; import * as path from 'path'
import * as fs from "fs"; import * as fs from 'fs'
import { Cluster, parseCluster } from "./types/cluster"; import {Cluster, parseCluster} from './types/cluster'
import { setContext, getKubeconfig } from "./utils"; import {setContext, getKubeconfig} from './utils'
/** /**
* Sets the Kubernetes context based on supplied action inputs * Sets the Kubernetes context based on supplied action inputs
@ -10,27 +10,27 @@ import { setContext, getKubeconfig } from "./utils";
export async function run() { export async function run() {
// get inputs // get inputs
const clusterType: Cluster | undefined = parseCluster( const clusterType: Cluster | undefined = parseCluster(
core.getInput("cluster-type", { core.getInput('cluster-type', {
required: true, required: true
}) })
); )
const runnerTempDirectory: string = process.env["RUNNER_TEMP"]; const runnerTempDirectory: string = process.env['RUNNER_TEMP']
const kubeconfigPath: string = path.join( const kubeconfigPath: string = path.join(
runnerTempDirectory, runnerTempDirectory,
`kubeconfig_${Date.now()}` `kubeconfig_${Date.now()}`
); )
// get kubeconfig and update context // get kubeconfig and update context
const kubeconfig: string = await getKubeconfig(clusterType); const kubeconfig: string = await getKubeconfig(clusterType)
const kubeconfigWithContext: string = setContext(kubeconfig); const kubeconfigWithContext: string = setContext(kubeconfig)
// output kubeconfig // output kubeconfig
core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`); core.debug(`Writing kubeconfig contents to ${kubeconfigPath}`)
fs.writeFileSync(kubeconfigPath, kubeconfigWithContext); fs.writeFileSync(kubeconfigPath, kubeconfigWithContext)
fs.chmodSync(kubeconfigPath, "600"); fs.chmodSync(kubeconfigPath, '600')
core.debug("Setting KUBECONFIG environment variable"); core.debug('Setting KUBECONFIG environment variable')
core.exportVariable("KUBECONFIG", kubeconfigPath); core.exportVariable('KUBECONFIG', kubeconfigPath)
} }
// Run the application // 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", () => { describe('Cluster type', () => {
test("it has required values", () => { test('it has required values', () => {
const vals = <any>Object.values(Cluster); const vals = <any>Object.values(Cluster)
expect(vals.includes("arc")).toBe(true); expect(vals.includes('arc')).toBe(true)
expect(vals.includes("generic")).toBe(true); expect(vals.includes('generic')).toBe(true)
}); })
test("it can parse valid values from a string", () => { 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('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", () => { test("it will return undefined if it can't parse values from a string", () => {
expect(parseCluster("invalid")).toBe(undefined); expect(parseCluster('invalid')).toBe(undefined)
expect(parseCluster("unsupportedType")).toBe(undefined); expect(parseCluster('unsupportedType')).toBe(undefined)
}); })
}); })

View File

@ -1,6 +1,6 @@
export enum Cluster { export enum Cluster {
ARC = "arc", ARC = 'arc',
GENERIC = "generic", GENERIC = 'generic'
} }
/** /**
@ -13,4 +13,4 @@ export const parseCluster = (str: string): Cluster | undefined =>
Object.keys(Cluster).filter( Object.keys(Cluster).filter(
(k) => Cluster[k].toString().toLowerCase() === str.toLowerCase() (k) => Cluster[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Cluster )[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('K8sSecret type', () => {
describe("Parsing from any", () => { describe('Parsing from any', () => {
test("it returns a type guarded secret", () => { test('it returns a type guarded secret', () => {
const secret = { data: { token: "token", "ca.crt": "cert" } }; const secret = {data: {token: 'token', 'ca.crt': 'cert'}}
expect(() => parseK8sSecret(secret)).not.toThrow(); expect(() => parseK8sSecret(secret)).not.toThrow()
}); })
test("it throws an error when secret not provided", () => { test('it throws an error when secret not provided', () => {
expect(() => parseK8sSecret(undefined)).toThrow(); expect(() => parseK8sSecret(undefined)).toThrow()
}); })
test("it throws an error when there is no data field", () => { test('it throws an error when there is no data field', () => {
const secret = {}; const secret = {}
expect(() => parseK8sSecret(secret)).toThrow(); expect(() => parseK8sSecret(secret)).toThrow()
}); })
test("it throws an error when there is no token", () => { test('it throws an error when there is no token', () => {
const secret = { const secret = {
data: { data: {
"ca.crt": "cert", 'ca.crt': 'cert'
}, }
}; }
expect(() => parseK8sSecret(secret)).toThrow(); expect(() => parseK8sSecret(secret)).toThrow()
}); })
test("it throws an error when there is no ca.crt field", () => { test('it throws an error when there is no ca.crt field', () => {
const secret = { data: { token: "token" } }; const secret = {data: {token: 'token'}}
expect(() => parseK8sSecret(secret)).toThrow(); expect(() => parseK8sSecret(secret)).toThrow()
}); })
}); })
}); })

View File

@ -1,10 +1,10 @@
import * as util from "util"; import * as util from 'util'
export interface K8sSecret { export interface K8sSecret {
data: { data: {
token: string; token: string
"ca.crt": string; 'ca.crt': string
}; }
} }
/** /**
@ -13,13 +13,13 @@ export interface K8sSecret {
* @returns A type guarded K8sSecret * @returns A type guarded K8sSecret
*/ */
export function parseK8sSecret(secret: any): K8sSecret { export function parseK8sSecret(secret: any): K8sSecret {
if (!secret) throw Error("K8s secret yaml is invalid"); if (!secret) throw Error('K8s secret yaml is invalid')
if (!secret.data) throw k8sSecretMissingFieldError("data"); if (!secret.data) throw k8sSecretMissingFieldError('data')
if (!secret.data.token) throw k8sSecretMissingFieldError("token"); if (!secret.data.token) throw k8sSecretMissingFieldError('token')
if (!secret.data["ca.crt"]) throw k8sSecretMissingFieldError("ca.crt"); if (!secret.data['ca.crt']) throw k8sSecretMissingFieldError('ca.crt')
return secret as K8sSecret; return secret as K8sSecret
} }
const k8sSecretMissingFieldError = (field: string): Error => 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", () => { describe('Method type', () => {
test("it has required values", () => { test('it has required values', () => {
const vals = <any>Object.values(Method); const vals = <any>Object.values(Method)
expect(vals.includes("kubeconfig")).toBe(true); expect(vals.includes('kubeconfig')).toBe(true)
expect(vals.includes("service-account")).toBe(true); expect(vals.includes('service-account')).toBe(true)
expect(vals.includes("service-principal")).toBe(true); expect(vals.includes('service-principal')).toBe(true)
}); })
test("it can parse valid values from a string", () => { 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('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", () => { test("it will return undefined if it can't parse values from a string", () => {
expect(parseMethod("invalid")).toBe(undefined); expect(parseMethod('invalid')).toBe(undefined)
expect(parseMethod("unsupportedType")).toBe(undefined); expect(parseMethod('unsupportedType')).toBe(undefined)
}); })
}); })

View File

@ -1,7 +1,7 @@
export enum Method { export enum Method {
KUBECONFIG = "kubeconfig", KUBECONFIG = 'kubeconfig',
SERVICE_ACCOUNT = "service-account", SERVICE_ACCOUNT = 'service-account',
SERVICE_PRINCIPAL = "service-principal", SERVICE_PRINCIPAL = 'service-principal'
} }
/** /**
@ -14,4 +14,4 @@ export const parseMethod = (str: string): Method | undefined =>
Object.keys(Method).filter( Object.keys(Method).filter(
(k) => Method[k].toString().toLowerCase() === str.toLowerCase() (k) => Method[k].toString().toLowerCase() === str.toLowerCase()
)[0] as keyof typeof Method )[0] as keyof typeof Method
]; ]

View File

@ -1,47 +1,47 @@
import fs from "fs"; import fs from 'fs'
import * as arc from "./kubeconfigs/arc"; import * as arc from './kubeconfigs/arc'
import * as def from "./kubeconfigs/default"; import * as def from './kubeconfigs/default'
import { Cluster } from "./types/cluster"; import {Cluster} from './types/cluster'
import { getKubeconfig, setContext } from "./utils"; import {getKubeconfig, setContext} from './utils'
describe("Utils", () => { describe('Utils', () => {
describe("get kubeconfig", () => { describe('get kubeconfig', () => {
test("it gets arc kubeconfig when type is arc", async () => { test('it gets arc kubeconfig when type is arc', async () => {
const arcKubeconfig = "arckubeconfig"; const arcKubeconfig = 'arckubeconfig'
jest jest
.spyOn(arc, "getArcKubeconfig") .spyOn(arc, 'getArcKubeconfig')
.mockImplementation(async () => arcKubeconfig); .mockImplementation(async () => arcKubeconfig)
expect(await getKubeconfig(Cluster.ARC)).toBe(arcKubeconfig); expect(await getKubeconfig(Cluster.ARC)).toBe(arcKubeconfig)
}); })
test("it defaults to default kubeconfig", async () => { test('it defaults to default kubeconfig', async () => {
const defaultKubeconfig = "arckubeconfig"; const defaultKubeconfig = 'arckubeconfig'
jest jest
.spyOn(def, "getDefaultKubeconfig") .spyOn(def, 'getDefaultKubeconfig')
.mockImplementation(() => defaultKubeconfig); .mockImplementation(() => defaultKubeconfig)
expect(await getKubeconfig(undefined)).toBe(defaultKubeconfig); expect(await getKubeconfig(undefined)).toBe(defaultKubeconfig)
expect(await getKubeconfig(Cluster.GENERIC)).toBe(defaultKubeconfig); expect(await getKubeconfig(Cluster.GENERIC)).toBe(defaultKubeconfig)
}); })
}); })
describe("set context", () => { describe('set context', () => {
const kc = fs.readFileSync("tests/sample-kubeconfig.yml").toString(); const kc = fs.readFileSync('tests/sample-kubeconfig.yml').toString()
test("it doesn't change kubeconfig without context", () => { test("it doesn't change kubeconfig without context", () => {
expect(setContext(kc)).toBe(kc); expect(setContext(kc)).toBe(kc)
}); })
test("it writes the context to the kubeconfig", () => { test('it writes the context to the kubeconfig', () => {
process.env["INPUT_CONTEXT"] = "example"; process.env['INPUT_CONTEXT'] = 'example'
const received = JSON.parse(setContext(kc)); const received = JSON.parse(setContext(kc))
const expectedKc = JSON.parse( const expectedKc = JSON.parse(
fs.readFileSync("tests/expected-kubeconfig.json").toString() 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 core from '@actions/core'
import * as fs from "fs"; import * as fs from 'fs'
import { KubeConfig } from "@kubernetes/client-node"; import {KubeConfig} from '@kubernetes/client-node'
import { getDefaultKubeconfig } from "./kubeconfigs/default"; import {getDefaultKubeconfig} from './kubeconfigs/default'
import { getArcKubeconfig } from "./kubeconfigs/arc"; import {getArcKubeconfig} from './kubeconfigs/arc'
import { Cluster } from "./types/cluster"; import {Cluster} from './types/cluster'
/** /**
* Gets the kubeconfig based on Kubernetes cluster type * Gets the kubeconfig based on Kubernetes cluster type
@ -15,13 +15,13 @@ export async function getKubeconfig(
): Promise<string> { ): Promise<string> {
switch (type) { switch (type) {
case Cluster.ARC: { case Cluster.ARC: {
return await getArcKubeconfig(); return await getArcKubeconfig()
} }
case undefined: { case undefined: {
core.warning("Cluster type not recognized. Defaulting to generic."); core.warning('Cluster type not recognized. Defaulting to generic.')
} }
default: { default: {
return getDefaultKubeconfig(); return getDefaultKubeconfig()
} }
} }
} }
@ -32,17 +32,17 @@ export async function getKubeconfig(
* @returns Updated kubeconfig with the context * @returns Updated kubeconfig with the context
*/ */
export function setContext(kubeconfig: string): string { export function setContext(kubeconfig: string): string {
const context: string = core.getInput("context"); const context: string = core.getInput('context')
if (!context) { if (!context) {
core.debug("Can't set context because context is unspecified."); core.debug("Can't set context because context is unspecified.")
return kubeconfig; return kubeconfig
} }
// load current kubeconfig // load current kubeconfig
const kc = new KubeConfig(); const kc = new KubeConfig()
kc.loadFromString(kubeconfig); kc.loadFromString(kubeconfig)
// update kubeconfig // update kubeconfig
kc.setCurrentContext(context); kc.setCurrentContext(context)
return kc.exportConfig(); return kc.exportConfig()
} }

View File

@ -8,7 +8,7 @@ metadata:
annotations: annotations:
kubernetes.io/service-account.name: default kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: e1414a3z-22fe-48d1-ab9e-18e4a5b91c kubernetes.io/service-account.uid: e1414a3z-22fe-48d1-ab9e-18e4a5b91c
creationTimestamp: "2020-03-02T06:40:31Z" creationTimestamp: '2020-03-02T06:40:31Z'
managedFields: managedFields:
- apiVersion: v1 - apiVersion: v1
fieldsType: FieldsV1 fieldsType: FieldsV1
@ -26,10 +26,10 @@ metadata:
f:type: {} f:type: {}
manager: kube-controller-manager manager: kube-controller-manager
operation: Update operation: Update
time: "2020-03-02T06:40:31Z" time: '2020-03-02T06:40:31Z'
name: default-token-bl8ra name: default-token-bl8ra
namespace: default namespace: default
resourceVersion: "278" resourceVersion: '278'
selfLink: /api/v1/namespaces/default/secrets/default-token-bl8ra selfLink: /api/v1/namespaces/default/secrets/default-token-bl8ra
uid: e6d8b21b-2e3a-4606-98za-54fb44fdc uid: e6d8b21b-2e3a-4606-98za-54fb44fdc
type: kubernetes.io/service-account-token type: kubernetes.io/service-account-token

View File

@ -4,4 +4,4 @@
* @returns Error with explanation message * @returns Error with explanation message
*/ */
export const getRequiredInputError = (inputName) => export const getRequiredInputError = (inputName) =>
Error(`Input required and not supplied: ${inputName}`); Error(`Input required and not supplied: ${inputName}`)