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:
parent
e972a5b196
commit
b6c5bf067a
@ -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'
|
||||||
---
|
---
|
||||||
|
52
.github/workflows/default-labels.yml
vendored
52
.github/workflows/default-labels.yml
vendored
@ -2,34 +2,34 @@ 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:
|
||||||
build:
|
build:
|
||||||
# The type of runner that the job will run on
|
# The type of runner that the job will run on
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v3
|
- uses: actions/stale@v3
|
||||||
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
|
||||||
|
278
.github/workflows/integration-tests.yml
vendored
278
.github/workflows/integration-tests.yml
vendored
@ -1,143 +1,143 @@
|
|||||||
name: Run Integration Tests
|
name: Run Integration Tests
|
||||||
on:
|
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:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Source Code
|
- name: Checkout Source Code
|
||||||
id: checkout-code
|
id: checkout-code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Npm Install and Build
|
- name: Npm Install and Build
|
||||||
id: npm-build
|
id: npm-build
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
- name: Set Context
|
- name: Set Context
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
method: kubeconfig
|
method: kubeconfig
|
||||||
context: exp-scratch
|
context: exp-scratch
|
||||||
kubeconfig: |
|
kubeconfig: |
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
clusters:
|
clusters:
|
||||||
- cluster:
|
- cluster:
|
||||||
certificate-authority: fake-ca-file
|
certificate-authority: fake-ca-file
|
||||||
server: https://1.2.3.4
|
server: https://1.2.3.4
|
||||||
name: development
|
name: development
|
||||||
- cluster:
|
- cluster:
|
||||||
insecure-skip-tls-verify: true
|
insecure-skip-tls-verify: true
|
||||||
server: https://5.6.7.8
|
server: https://5.6.7.8
|
||||||
name: scratch
|
name: scratch
|
||||||
contexts:
|
contexts:
|
||||||
- context:
|
- context:
|
||||||
cluster: development
|
cluster: development
|
||||||
namespace: frontend
|
namespace: frontend
|
||||||
user: developer
|
user: developer
|
||||||
name: dev-frontend
|
name: dev-frontend
|
||||||
- context:
|
- context:
|
||||||
cluster: development
|
cluster: development
|
||||||
namespace: storage
|
namespace: storage
|
||||||
user: developer
|
user: developer
|
||||||
name: dev-storage
|
name: dev-storage
|
||||||
- context:
|
- context:
|
||||||
cluster: scratch
|
cluster: scratch
|
||||||
namespace: default
|
namespace: default
|
||||||
user: experimenter
|
user: experimenter
|
||||||
name: exp-scratch
|
name: exp-scratch
|
||||||
current-context: ""
|
current-context: ""
|
||||||
kind: Config
|
kind: Config
|
||||||
preferences: {}
|
preferences: {}
|
||||||
users:
|
users:
|
||||||
- name: developer
|
- name: developer
|
||||||
user:
|
user:
|
||||||
client-certificate: fake-cert-file
|
client-certificate: fake-cert-file
|
||||||
client-key: fake-key-file
|
client-key: fake-key-file
|
||||||
- name: experimenter
|
- name: experimenter
|
||||||
user:
|
user:
|
||||||
password: some-password
|
password: some-password
|
||||||
username: exp
|
username: exp
|
||||||
- name: Vertify Results
|
- name: Vertify Results
|
||||||
run: |
|
run: |
|
||||||
echo "$EXPECTED_KC" > /tmp/expected_kc.json
|
echo "$EXPECTED_KC" > /tmp/expected_kc.json
|
||||||
DIFF=$(diff <(jq -S -c . $KUBECONFIG) <(jq -S -c . /tmp/expected_kc.json))
|
DIFF=$(diff <(jq -S -c . $KUBECONFIG) <(jq -S -c . /tmp/expected_kc.json))
|
||||||
if [ "$DIFF" != "" ]; then exit 1; else echo -e "Kubeconfig matches expected"; fi
|
if [ "$DIFF" != "" ]; then exit 1; else echo -e "Kubeconfig matches expected"; fi
|
||||||
env:
|
env:
|
||||||
EXPECTED_KC: |
|
EXPECTED_KC: |
|
||||||
{
|
{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"clusters": [
|
"clusters": [
|
||||||
{
|
{
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"certificate-authority": "fake-ca-file",
|
"certificate-authority": "fake-ca-file",
|
||||||
"insecure-skip-tls-verify": false,
|
"insecure-skip-tls-verify": false,
|
||||||
"server": "https://1.2.3.4"
|
"server": "https://1.2.3.4"
|
||||||
},
|
},
|
||||||
"name": "development"
|
"name": "development"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"insecure-skip-tls-verify": true,
|
"insecure-skip-tls-verify": true,
|
||||||
"server": "https://5.6.7.8"
|
"server": "https://5.6.7.8"
|
||||||
},
|
},
|
||||||
"name": "scratch"
|
"name": "scratch"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contexts": [
|
"contexts": [
|
||||||
{
|
{
|
||||||
"context": {
|
"context": {
|
||||||
"cluster": "development",
|
"cluster": "development",
|
||||||
"name": "dev-frontend",
|
"name": "dev-frontend",
|
||||||
"namespace": "frontend",
|
"namespace": "frontend",
|
||||||
"user": "developer"
|
"user": "developer"
|
||||||
},
|
},
|
||||||
"name": "dev-frontend"
|
"name": "dev-frontend"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": {
|
"context": {
|
||||||
"cluster": "development",
|
"cluster": "development",
|
||||||
"name": "dev-storage",
|
"name": "dev-storage",
|
||||||
"namespace": "storage",
|
"namespace": "storage",
|
||||||
"user": "developer"
|
"user": "developer"
|
||||||
},
|
},
|
||||||
"name": "dev-storage"
|
"name": "dev-storage"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": {
|
"context": {
|
||||||
"cluster": "scratch",
|
"cluster": "scratch",
|
||||||
"name": "exp-scratch",
|
"name": "exp-scratch",
|
||||||
"namespace": "default",
|
"namespace": "default",
|
||||||
"user": "experimenter"
|
"user": "experimenter"
|
||||||
},
|
},
|
||||||
"name": "exp-scratch"
|
"name": "exp-scratch"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"current-context": "exp-scratch",
|
"current-context": "exp-scratch",
|
||||||
"kind": "Config",
|
"kind": "Config",
|
||||||
"preferences": {
|
"preferences": {
|
||||||
},
|
},
|
||||||
"users": [
|
"users": [
|
||||||
{
|
{
|
||||||
"name": "developer",
|
"name": "developer",
|
||||||
"user": {
|
"user": {
|
||||||
"client-certificate": "fake-cert-file",
|
"client-certificate": "fake-cert-file",
|
||||||
"client-key": "fake-key-file"
|
"client-key": "fake-key-file"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "experimenter",
|
"name": "experimenter",
|
||||||
"user": {
|
"user": {
|
||||||
"password": "some-password",
|
"password": "some-password",
|
||||||
"username": "exp"
|
"username": "exp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
18
.github/workflows/prettify-code.yml
vendored
Normal file
18
.github/workflows/prettify-code.yml
vendored
Normal 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 .
|
18
.github/workflows/release-pr.yml
vendored
18
.github/workflows/release-pr.yml
vendored
@ -1,14 +1,14 @@
|
|||||||
name: Create release PR
|
name: Create release PR
|
||||||
|
|
||||||
on:
|
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:
|
||||||
release-pr:
|
release-pr:
|
||||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||||
with:
|
with:
|
||||||
release: ${{ github.event.inputs.release }}
|
release: ${{ github.event.inputs.release }}
|
||||||
|
10
.github/workflows/tag-and-draft.yml
vendored
10
.github/workflows/tag-and-draft.yml
vendored
@ -1,10 +1,10 @@
|
|||||||
name: Tag and create release draft
|
name: Tag and create release draft
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- releases/*
|
- releases/*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tag-and-release:
|
tag-and-release:
|
||||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main
|
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main
|
||||||
|
32
.github/workflows/unit-tests.yml
vendored
32
.github/workflows/unit-tests.yml
vendored
@ -1,20 +1,20 @@
|
|||||||
name: Run Unit Tests
|
name: Run Unit Tests
|
||||||
on:
|
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:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Run Unit Tests
|
- name: Run Unit Tests
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm test
|
npm test
|
||||||
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
coverage
|
||||||
|
/lib
|
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 3,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
# Microsoft Open Source Code of Conduct
|
# Microsoft Open Source Code of Conduct
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
|
|
||||||
Resources:
|
Resources:
|
||||||
|
|
||||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||||
|
218
README.md
218
README.md
@ -1,109 +1,109 @@
|
|||||||
# Kubernetes set context
|
# Kubernetes set context
|
||||||
|
|
||||||
This action can be used to set cluster context before other actions like [`azure/k8s-deploy`](https://github.com/Azure/k8s-deploy/tree/master) and [`azure/k8s-create-secret`](https://github.com/Azure/k8s-create-secret/tree/master). It should also be used before `kubectl` commands (in script) are 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) and [`azure/k8s-create-secret`](https://github.com/Azure/k8s-create-secret/tree/master). It should also be used before `kubectl` commands (in script) are run subsequently in the workflow.
|
||||||
|
|
||||||
It is a requirement to use [`azure/login`](https://github.com/Azure/login/tree/master) in your workflow before using this action when using the `service-account` or `service-principal` methods.
|
It is a requirement to use [`azure/login`](https://github.com/Azure/login/tree/master) in your workflow before using this action when using the `service-account` or `service-principal` methods.
|
||||||
|
|
||||||
There are three approaches for specifying the deployment target:
|
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
|
- Service principal approach (only applicable for arc cluster) where service principal provided with 'creds' is used as input to action
|
||||||
|
|
||||||
In all these approaches it is recommended to store these contents (kubeconfig file content or secret content) in a [secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets/).
|
In all these approaches it is recommended to store these contents (kubeconfig file content or secret content) in a [secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets/).
|
||||||
|
|
||||||
Refer to the [action metadata file](./action.yml) for details about inputs. Note that different inputs are required for different method and cluster types. Use the below examples as a reference.
|
Refer to the [action metadata file](./action.yml) for details about inputs. Note that different inputs are required for different method and cluster types. Use the below examples as a reference.
|
||||||
|
|
||||||
## Example usage
|
## Example usage
|
||||||
|
|
||||||
### Kubeconfig approach
|
### Kubeconfig approach
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: azure/k8s-set-context@v2
|
- uses: azure/k8s-set-context@v2
|
||||||
with:
|
with:
|
||||||
method: kubeconfig
|
method: kubeconfig
|
||||||
kubeconfig: <your kubeconfig>
|
kubeconfig: <your kubeconfig>
|
||||||
context: <context name> # current-context from kubeconfig is used as default
|
context: <context name> # current-context from kubeconfig is used as default
|
||||||
```
|
```
|
||||||
|
|
||||||
**Please note** that the input requires the _contents_ of the kubeconfig file, and not its path.
|
**Please note** that the input requires the _contents_ of the kubeconfig file, and not its path.
|
||||||
|
|
||||||
Following are the ways to fetch kubeconfig file onto your local development machine so that the same can be used in the action input shown above.
|
Following are the ways to fetch kubeconfig file onto your local development machine so that the same can be used in the action input shown above.
|
||||||
|
|
||||||
#### Azure Kubernetes Service cluster
|
#### Azure Kubernetes Service cluster
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
az aks get-credentials --name
|
az aks get-credentials --name
|
||||||
--resource-group
|
--resource-group
|
||||||
[--admin]
|
[--admin]
|
||||||
[--file]
|
[--file]
|
||||||
[--overwrite-existing]
|
[--overwrite-existing]
|
||||||
[--subscription]
|
[--subscription]
|
||||||
```
|
```
|
||||||
|
|
||||||
Further details can be found in [az aks get-credentials documentation](https://docs.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az-aks-get-credentials).
|
Further details can be found in [az aks get-credentials documentation](https://docs.microsoft.com/en-us/cli/azure/aks?view=azure-cli-latest#az-aks-get-credentials).
|
||||||
|
|
||||||
#### Generic Kubernetes cluster
|
#### Generic Kubernetes cluster
|
||||||
|
|
||||||
Please refer to documentation on fetching [kubeconfig for any generic K8s cluster](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/)
|
Please refer to documentation on fetching [kubeconfig for any generic K8s cluster](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/)
|
||||||
|
|
||||||
### Service account approach
|
### Service account approach
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: azure/k8s-set-context@v2
|
- uses: azure/k8s-set-context@v2
|
||||||
with:
|
with:
|
||||||
method: service-account
|
method: service-account
|
||||||
k8s-url: <URL of the cluster's API server>
|
k8s-url: <URL of the cluster's API server>
|
||||||
k8s-secret: <secret associated with the service account>
|
k8s-secret: <secret associated with the service account>
|
||||||
```
|
```
|
||||||
|
|
||||||
For fetching Server URL, execute the following command on your shell:
|
For fetching Server URL, execute the following command on your shell:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl config view --minify -o 'jsonpath={.clusters[0].cluster.server}'
|
kubectl config view --minify -o 'jsonpath={.clusters[0].cluster.server}'
|
||||||
```
|
```
|
||||||
|
|
||||||
For fetching Secret object required to connect and authenticate with the cluster, the following sequence of commands need to be run:
|
For fetching Secret object required to connect and authenticate with the cluster, the following sequence of commands need to be run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get serviceAccounts <service-account-name> -n <namespace> -o 'jsonpath={.secrets[*].name}'
|
kubectl get serviceAccounts <service-account-name> -n <namespace> -o 'jsonpath={.secrets[*].name}'
|
||||||
|
|
||||||
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
|
### Service account approach for arc cluster
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: azure/k8s-set-context@v2
|
- uses: azure/k8s-set-context@v2
|
||||||
with:
|
with:
|
||||||
method: service-account
|
method: service-account
|
||||||
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
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: azure/k8s-set-context@v2
|
- uses: azure/k8s-set-context@v2
|
||||||
with:
|
with:
|
||||||
method: service-principal
|
method: service-principal
|
||||||
cluster-type: arc
|
cluster-type: arc
|
||||||
cluster-name: <cluster-name>
|
cluster-name: <cluster-name>
|
||||||
resource-group: <resource-group>
|
resource-group: <resource-group>
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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
|
||||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||||
|
|
||||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
70
SECURITY.md
70
SECURITY.md
@ -1,35 +1,35 @@
|
|||||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.1 BLOCK -->
|
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.1 BLOCK -->
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
|
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
|
||||||
|
|
||||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
## Preferred Languages
|
## Preferred Languages
|
||||||
|
|
||||||
We prefer all communications to be in English.
|
We prefer all communications to be in English.
|
||||||
|
|
||||||
## Policy
|
## Policy
|
||||||
|
|
||||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||||
|
|
||||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||||
|
78
action.yml
78
action.yml
@ -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'
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
module.exports = {
|
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: {
|
||||||
global: {
|
global: {
|
||||||
branches: 0,
|
branches: 0,
|
||||||
functions: 40,
|
functions: 40,
|
||||||
lines: 22,
|
lines: 22,
|
||||||
statements: 22,
|
statements: 22
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
15900
package-lock.json
generated
15900
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
69
package.json
69
package.json
@ -1,33 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "k8s-set-context-action",
|
"name": "k8s-set-context-action",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/run.js",
|
"main": "lib/run.js",
|
||||||
"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 .",
|
||||||
"keywords": [
|
"format-check": "prettier --check ."
|
||||||
"actions",
|
},
|
||||||
"node",
|
"keywords": [
|
||||||
"setup"
|
"actions",
|
||||||
],
|
"node",
|
||||||
"author": "GitHub",
|
"setup"
|
||||||
"license": "MIT",
|
],
|
||||||
"dependencies": {
|
"author": "GitHub",
|
||||||
"@actions/core": "^1.2.6",
|
"license": "MIT",
|
||||||
"@actions/exec": "^1.0.0",
|
"dependencies": {
|
||||||
"@actions/io": "^1.1.2",
|
"@actions/core": "^1.2.6",
|
||||||
"@kubernetes/client-node": "^0.16.0",
|
"@actions/exec": "^1.0.0",
|
||||||
"js-yaml": "^4.1.0"
|
"@actions/io": "^1.1.2",
|
||||||
},
|
"@kubernetes/client-node": "^0.16.0",
|
||||||
"devDependencies": {
|
"js-yaml": "^4.1.0"
|
||||||
"@types/jest": "^28.1.2",
|
},
|
||||||
"@types/js-yaml": "^4.0.4",
|
"devDependencies": {
|
||||||
"@types/node": "^16.0.0",
|
"@types/jest": "^28.1.2",
|
||||||
"jest": "^28.1.1",
|
"@types/js-yaml": "^4.0.4",
|
||||||
"ts-jest": "^28.0.5",
|
"@types/node": "^16.0.0",
|
||||||
"typescript": "4.7.4"
|
"jest": "^28.1.1",
|
||||||
}
|
"prettier": "2.7.1",
|
||||||
}
|
"ts-jest": "^28.0.5",
|
||||||
|
"typescript": "4.7.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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(
|
|
||||||
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 () => {
|
|
||||||
await expect(getArcKubeconfig()).rejects.toThrow(
|
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(() => {
|
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 () => {
|
jest.spyOn(io, 'which').mockImplementation(async () => path)
|
||||||
await expect(getArcKubeconfig()).rejects.toThrow(
|
jest.spyOn(az, 'runAzCliCommand').mockImplementation(async () => {})
|
||||||
getRequiredInputError("token")
|
jest
|
||||||
);
|
.spyOn(az, 'runAzKubeconfigCommandBlocking')
|
||||||
});
|
.mockImplementation(async () => kubeconfig)
|
||||||
|
})
|
||||||
|
|
||||||
it("gets the kubeconfig", async () => {
|
it('throws an error without method', async () => {
|
||||||
const token = "token";
|
await expect(getArcKubeconfig()).rejects.toThrow(
|
||||||
process.env["INPUT_TOKEN"] = token;
|
getRequiredInputError('method')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
expect(await getArcKubeconfig()).toBe(kubeconfig);
|
describe('service account method', () => {
|
||||||
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
|
beforeEach(() => {
|
||||||
path,
|
process.env['INPUT_METHOD'] = 'service-account'
|
||||||
[
|
})
|
||||||
"connectedk8s",
|
|
||||||
"proxy",
|
|
||||||
"-n",
|
|
||||||
name,
|
|
||||||
"-g",
|
|
||||||
group,
|
|
||||||
"--token",
|
|
||||||
token,
|
|
||||||
"-f",
|
|
||||||
KUBECONFIG_LOCATION,
|
|
||||||
],
|
|
||||||
KUBECONFIG_LOCATION
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("service principal method", () => {
|
it('throws an error without token', async () => {
|
||||||
beforeEach(() => {
|
await expect(getArcKubeconfig()).rejects.toThrow(
|
||||||
process.env["INPUT_METHOD"] = "service-principal";
|
getRequiredInputError('token')
|
||||||
});
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("gets the kubeconfig", async () => {
|
it('gets the kubeconfig', async () => {
|
||||||
expect(await getArcKubeconfig()).toBe(kubeconfig);
|
const token = 'token'
|
||||||
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
|
process.env['INPUT_TOKEN'] = token
|
||||||
path,
|
|
||||||
[
|
expect(await getArcKubeconfig()).toBe(kubeconfig)
|
||||||
"connectedk8s",
|
expect(az.runAzKubeconfigCommandBlocking).toHaveBeenCalledWith(
|
||||||
"proxy",
|
path,
|
||||||
"-n",
|
[
|
||||||
name,
|
'connectedk8s',
|
||||||
"-g",
|
'proxy',
|
||||||
group,
|
'-n',
|
||||||
"-f",
|
name,
|
||||||
KUBECONFIG_LOCATION,
|
'-g',
|
||||||
],
|
group,
|
||||||
KUBECONFIG_LOCATION
|
'--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
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -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')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, {})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -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
|
||||||
@ -12,11 +12,11 @@ const AZ_TIMEOUT_SECONDS: number = 120;
|
|||||||
* @param options Optional options for the command execution
|
* @param options Optional options for the command execution
|
||||||
*/
|
*/
|
||||||
export async function runAzCliCommand(
|
export async function runAzCliCommand(
|
||||||
azPath: string,
|
azPath: string,
|
||||||
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
|
||||||
@ -26,19 +26,19 @@ export async function runAzCliCommand(
|
|||||||
* @returns The contents of the kubeconfig
|
* @returns The contents of the kubeconfig
|
||||||
*/
|
*/
|
||||||
export async function runAzKubeconfigCommandBlocking(
|
export async function runAzKubeconfigCommandBlocking(
|
||||||
azPath: string,
|
azPath: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
kubeconfigPath: string
|
kubeconfigPath: string
|
||||||
): 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))
|
||||||
|
@ -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(
|
|
||||||
getRequiredInputError("method")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("default method", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
process.env["INPUT_METHOD"] = "default";
|
|
||||||
});
|
|
||||||
|
|
||||||
test("it throws error without kubeconfig", () => {
|
|
||||||
expect(() => getDefaultKubeconfig()).toThrow(
|
expect(() => getDefaultKubeconfig()).toThrow(
|
||||||
getRequiredInputError("kubeconfig")
|
getRequiredInputError('method')
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
test("it gets default config through kubeconfig input", () => {
|
describe('default method', () => {
|
||||||
const kc = "example kc";
|
beforeEach(() => {
|
||||||
process.env["INPUT_KUBECONFIG"] = kc;
|
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", () => {
|
test('it gets default config through kubeconfig input', () => {
|
||||||
process.env["INPUT_METHOD"] = "unknown";
|
const kc = 'example kc'
|
||||||
|
process.env['INPUT_KUBECONFIG'] = kc
|
||||||
|
|
||||||
const kc = "example kc";
|
expect(getDefaultKubeconfig()).toBe(kc)
|
||||||
process.env["INPUT_KUBECONFIG"] = 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", () => {
|
const kc = 'example kc'
|
||||||
process.env["INPUT_METHOD"] = "service-principal";
|
process.env['INPUT_KUBECONFIG'] = kc
|
||||||
|
|
||||||
const kc = "example kc";
|
expect(getDefaultKubeconfig()).toBe(kc)
|
||||||
process.env["INPUT_KUBECONFIG"] = kc;
|
})
|
||||||
|
|
||||||
expect(getDefaultKubeconfig()).toBe(kc);
|
test('it defaults to default method from service-principal', () => {
|
||||||
});
|
process.env['INPUT_METHOD'] = 'service-principal'
|
||||||
|
|
||||||
describe("service-account method", () => {
|
const kc = 'example kc'
|
||||||
beforeEach(() => {
|
process.env['INPUT_KUBECONFIG'] = kc
|
||||||
process.env["INPUT_METHOD"] = "service-account";
|
|
||||||
});
|
|
||||||
|
|
||||||
test("it throws error without cluster url", () => {
|
expect(getDefaultKubeconfig()).toBe(kc)
|
||||||
expect(() => getDefaultKubeconfig()).toThrow(
|
})
|
||||||
getRequiredInputError("k8s-url")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("it throws error without k8s secret", () => {
|
describe('service-account method', () => {
|
||||||
process.env["INPUT_K8S-URL"] = "url";
|
beforeEach(() => {
|
||||||
|
process.env['INPUT_METHOD'] = 'service-account'
|
||||||
|
})
|
||||||
|
|
||||||
expect(() => getDefaultKubeconfig()).toThrow(
|
test('it throws error without cluster url', () => {
|
||||||
getRequiredInputError("k8s-secret")
|
expect(() => getDefaultKubeconfig()).toThrow(
|
||||||
);
|
getRequiredInputError('k8s-url')
|
||||||
});
|
)
|
||||||
|
})
|
||||||
|
|
||||||
test("it gets kubeconfig through service-account", () => {
|
test('it throws error without k8s secret', () => {
|
||||||
const k8sUrl = "https://testing-dns-4za.hfp.earth.azmk8s.io:443";
|
process.env['INPUT_K8S-URL'] = 'url'
|
||||||
const token = "ZXlKaGJHY2lPcUpTVXpJMU5pSX=";
|
|
||||||
const cert = "LS0tLS1CRUdJTiBDRWyUSUZJQ";
|
|
||||||
const k8sSecret = fs.readFileSync("tests/sample-secret.yml").toString();
|
|
||||||
|
|
||||||
process.env["INPUT_K8S-URL"] = k8sUrl;
|
expect(() => getDefaultKubeconfig()).toThrow(
|
||||||
process.env["INPUT_K8S-SECRET"] = k8sSecret;
|
getRequiredInputError('k8s-secret')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const expectedConfig = JSON.stringify({
|
test('it gets kubeconfig through service-account', () => {
|
||||||
apiVersion: "v1",
|
const k8sUrl = 'https://testing-dns-4za.hfp.earth.azmk8s.io:443'
|
||||||
kind: "Config",
|
const token = 'ZXlKaGJHY2lPcUpTVXpJMU5pSX='
|
||||||
clusters: [
|
const cert = 'LS0tLS1CRUdJTiBDRWyUSUZJQ'
|
||||||
{
|
const k8sSecret = fs.readFileSync('tests/sample-secret.yml').toString()
|
||||||
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);
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -1,50 +1,52 @@
|
|||||||
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
|
||||||
* @returns The kubeconfig
|
* @returns The kubeconfig
|
||||||
*/
|
*/
|
||||||
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 token: string = Buffer.from(
|
)
|
||||||
parsedK8sSecret.data.token,
|
const certAuth: string = parsedK8sSecret.data['ca.crt']
|
||||||
"base64"
|
const token: string = Buffer.from(
|
||||||
).toString();
|
parsedK8sSecret.data.token,
|
||||||
|
'base64'
|
||||||
|
).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})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,22 +57,22 @@ export function getDefaultKubeconfig(): string {
|
|||||||
* @returns The kubeconfig as a string
|
* @returns The kubeconfig as a string
|
||||||
*/
|
*/
|
||||||
export function createKubeconfig(
|
export function createKubeconfig(
|
||||||
certAuth: string,
|
certAuth: string,
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
52
src/run.ts
52
src/run.ts
@ -1,36 +1,36 @@
|
|||||||
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
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
|
@ -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)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export enum Cluster {
|
export enum Cluster {
|
||||||
ARC = "arc",
|
ARC = 'arc',
|
||||||
GENERIC = "generic",
|
GENERIC = 'generic'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,8 +9,8 @@ export enum Cluster {
|
|||||||
* @returns The Cluster enum or undefined if it can't be parsed
|
* @returns The Cluster enum or undefined if it can't be parsed
|
||||||
*/
|
*/
|
||||||
export const parseCluster = (str: string): Cluster | undefined =>
|
export const parseCluster = (str: string): Cluster | undefined =>
|
||||||
Cluster[
|
Cluster[
|
||||||
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
|
||||||
];
|
]
|
||||||
|
@ -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()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,8 +10,8 @@ export enum Method {
|
|||||||
* @returns The Method enum or undefined if it can't be parsed
|
* @returns The Method enum or undefined if it can't be parsed
|
||||||
*/
|
*/
|
||||||
export const parseMethod = (str: string): Method | undefined =>
|
export const parseMethod = (str: string): Method | undefined =>
|
||||||
Method[
|
Method[
|
||||||
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
|
||||||
];
|
]
|
||||||
|
@ -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)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
58
src/utils.ts
58
src/utils.ts
@ -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
|
||||||
@ -11,19 +11,19 @@ import { Cluster } from "./types/cluster";
|
|||||||
* @returns A promise of the kubeconfig
|
* @returns A promise of the kubeconfig
|
||||||
*/
|
*/
|
||||||
export async function getKubeconfig(
|
export async function getKubeconfig(
|
||||||
type: Cluster | undefined
|
type: Cluster | undefined
|
||||||
): 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()
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
{
|
{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Config",
|
"kind": "Config",
|
||||||
"clusters": [
|
"clusters": [
|
||||||
{
|
{
|
||||||
"name": "example",
|
"name": "example",
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"server": "http://example.com:8080",
|
"server": "http://example.com:8080",
|
||||||
"insecure-skip-tls-verify": false
|
"insecure-skip-tls-verify": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
"users": [],
|
||||||
"users": [],
|
"contexts": [
|
||||||
"contexts": [
|
{
|
||||||
{
|
"name": "example",
|
||||||
"name": "example",
|
"context": {
|
||||||
"context": {
|
"cluster": "example",
|
||||||
"cluster": "example",
|
"name": "example",
|
||||||
"name": "example",
|
"user": "example",
|
||||||
"user": "example",
|
"namespace": "example"
|
||||||
"namespace": "example"
|
}
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
],
|
"preferences": {},
|
||||||
"preferences": {},
|
"current-context": "example"
|
||||||
"current-context": "example"
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Config
|
kind: Config
|
||||||
clusters:
|
clusters:
|
||||||
- cluster:
|
- cluster:
|
||||||
server: http://example.com:8080
|
server: http://example.com:8080
|
||||||
name: example
|
name: example
|
||||||
contexts:
|
contexts:
|
||||||
- context:
|
- context:
|
||||||
cluster: example
|
cluster: example
|
||||||
namespace: example
|
namespace: example
|
||||||
user: example
|
user: example
|
||||||
name: example
|
name: example
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
ca.crt: LS0tLS1CRUdJTiBDRWyUSUZJQ
|
ca.crt: LS0tLS1CRUdJTiBDRWyUSUZJQ
|
||||||
namespace: ZGVmBXUsLdA==
|
namespace: ZGVmBXUsLdA==
|
||||||
token: ZXlKaGJHY2lPcUpTVXpJMU5pSX=
|
token: ZXlKaGJHY2lPcUpTVXpJMU5pSX=
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
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
|
||||||
fieldsV1:
|
fieldsV1:
|
||||||
f:data:
|
f:data:
|
||||||
.: {}
|
.: {}
|
||||||
f:ca.crt: {}
|
f:ca.crt: {}
|
||||||
f:namespace: {}
|
f:namespace: {}
|
||||||
f:token: {}
|
f:token: {}
|
||||||
f:metadata:
|
f:metadata:
|
||||||
f:annotations:
|
f:annotations:
|
||||||
.: {}
|
.: {}
|
||||||
f:kubernetes.io/service-account.name: {}
|
f:kubernetes.io/service-account.name: {}
|
||||||
f:kubernetes.io/service-account.uid: {}
|
f:kubernetes.io/service-account.uid: {}
|
||||||
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
|
||||||
|
@ -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}`)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES6",
|
"target": "ES6",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "tests", "src/**/*.test.ts"]
|
"exclude": ["node_modules", "tests", "src/**/*.test.ts"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user