Skip to content

Commit 58ba3f0

Browse files
authored
new commit with all changes (#258)
1 parent e9693a7 commit 58ba3f0

File tree

8 files changed

+165
-36
lines changed

8 files changed

+165
-36
lines changed

.github/workflows/run-integration-tests-basic.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
test/integration/manifests/test.yml
6565
action: deploy
6666

67-
- name: Checking if deployments and services were created with canary labels and original tag
67+
- name: Checking if deployments and services were created
6868
run: |
6969
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx
7070
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
name: Cluster Integration Tests - private cluster
2+
on:
3+
pull_request:
4+
branches:
5+
- 'releases/*'
6+
push:
7+
branches:
8+
- main
9+
workflow_dispatch:
10+
11+
jobs:
12+
run-integration-test:
13+
name: Run Minikube Integration Tests
14+
runs-on: ubuntu-latest
15+
env:
16+
KUBECONFIG: /home/runner/.kube/config
17+
NAMESPACE: test-${{ github.run_id }}
18+
permissions:
19+
contents: read
20+
id-token: write
21+
steps:
22+
- uses: actions/checkout@v3
23+
24+
- name: Install dependencies
25+
run: |
26+
rm -rf node_modules/
27+
npm install
28+
- name: Install ncc
29+
run: npm i -g @vercel/ncc
30+
- name: Build
31+
run: ncc build src/run.ts -o lib
32+
- name: Azure login
33+
uses: azure/[email protected]
34+
with:
35+
client-id: ${{ secrets.AZURE_CLIENT_ID }}
36+
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
37+
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
38+
39+
- uses: Azure/setup-kubectl@v3
40+
name: Install Kubectl
41+
42+
- name: Create private AKS cluster and set context
43+
run: |
44+
set +x
45+
# create cluster
46+
az group create --location eastus --name ${{ env.NAMESPACE }}
47+
az aks create --name ${{ env.NAMESPACE }} --resource-group ${{ env.NAMESPACE }} --enable-private-cluster --generate-ssh-keys
48+
az aks get-credentials --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
49+
50+
- name: Create namespace to run tests
51+
run: |
52+
az aks command invoke --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }} --command "kubectl create ns ${{ env.NAMESPACE }}"
53+
54+
- uses: actions/setup-python@v2
55+
name: Install Python
56+
with:
57+
python-version: '3.x'
58+
59+
- name: Executing deploy action for pod
60+
uses: ./
61+
with:
62+
namespace: ${{ env.NAMESPACE }}
63+
images: nginx:1.14.2
64+
manifests: |
65+
test/integration/manifests/test.yml
66+
action: deploy
67+
private-cluster: true
68+
resource-group: ${{ env.NAMESPACE }}
69+
name: ${{ env.NAMESPACE }}
70+
71+
- name: Checking if deployments and services were created
72+
run: |
73+
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx
74+
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx
75+
76+
- name: Clean up AKS cluster
77+
if: ${{ always() }}
78+
run: |
79+
echo "deleting AKS cluster and resource group"
80+
az aks delete --yes --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
81+
az group delete --resource-group ${{ env.NAMESPACE }} --yes

action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ inputs:
66
namespace:
77
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
88
required: false
9+
default: default
910
manifests:
1011
description: 'Path to the manifest files which will be used for deployment.'
1112
required: true

src/types/kubectl.test.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,6 @@ describe('Kubectl class', () => {
4848
return execReturn
4949
})
5050
})
51-
52-
describe('omits default namespace from commands', () => {
53-
it('executes a command without appending --namespace arg', async () => {
54-
// no args
55-
const command = 'command'
56-
expect(await kubectl.executeCommand(command)).toBe(execReturn)
57-
expect(exec.getExecOutput).toBeCalledWith(kubectlPath, [command], {
58-
silent: false
59-
})
60-
})
61-
})
6251
})
6352

6453
describe('with a success exec return in testNamespace', () => {

src/types/kubectl.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,18 @@ export class Kubectl {
102102
files: string | string[],
103103
annotation: string
104104
): Promise<ExecOutput> {
105+
const filesToAnnotate = createInlineArray(files)
106+
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`)
105107
const args = [
106108
'annotate',
107109
'-f',
108-
createInlineArray(files),
110+
filesToAnnotate,
109111
annotation,
110112
'--overwrite'
111113
]
114+
core.debug(
115+
`sending args from annotate to execute: ${JSON.stringify(args)}`
116+
)
112117
return await this.execute(args)
113118
}
114119

@@ -169,7 +174,7 @@ export class Kubectl {
169174
if (this.ignoreSSLErrors) {
170175
args.push('--insecure-skip-tls-verify')
171176
}
172-
if (this.namespace && this.namespace != 'default') {
177+
if (this.namespace) {
173178
args = args.concat(['--namespace', this.namespace])
174179
}
175180
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)

src/types/privatekubectl.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {PrivateKubectl} from './privatekubectl'
2+
3+
describe('Private kubectl', () => {
4+
const testString = `kubectl annotate -f test.yml,test2.yml,test3.yml -f test4.yml --filename test5.yml actions.github.com/k8s-deploy={"run":"3498366832","repository":"jaiveerk/k8s-deploy","workflow":"Minikube Integration Tests - private cluster","workflowFileName":"run-integration-tests-private.yml","jobName":"run-integration-test","createdBy":"jaiveerk","runUri":"https://github.com/jaiveerk/k8s-deploy/actions/runs/3498366832","commit":"c63b323186ea1320a31290de6dcc094c06385e75","lastSuccessRunCommit":"NA","branch":"refs/heads/main","deployTimestamp":1668787848577,"dockerfilePaths":{"nginx:1.14.2":""},"manifestsPaths":["https://github.com/jaiveerk/k8s-deploy/blob/c63b323186ea1320a31290de6dcc094c06385e75/test/integration/manifests/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832`
5+
const mockKube = new PrivateKubectl('')
6+
7+
it('should extract filenames correctly', () => {
8+
expect(mockKube.extractFilesnames(testString)).toEqual(
9+
'test.yml test2.yml test3.yml test4.yml test5.yml'
10+
)
11+
})
12+
})

src/types/privatekubectl.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Kubectl} from './kubectl'
2+
import * as minimist from 'minimist'
23
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
34
import * as core from '@actions/core'
45
import * as os from 'os'
@@ -7,6 +8,9 @@ import * as path from 'path'
78

89
export class PrivateKubectl extends Kubectl {
910
protected async execute(args: string[], silent: boolean = false) {
11+
if (this.namespace) {
12+
args = args.concat(['--namespace', this.namespace])
13+
}
1014
args.unshift('kubectl')
1115
let kubectlCmd = args.join(' ')
1216
let addFileFlag = false
@@ -22,6 +26,13 @@ export class PrivateKubectl extends Kubectl {
2226
addFileFlag = true
2327
}
2428

29+
if (this.resourceGroup === '') {
30+
throw Error('Resource group must be specified for private cluster')
31+
}
32+
if (this.name === '') {
33+
throw Error('Cluster name must be specified for private cluster')
34+
}
35+
2536
const privateClusterArgs = [
2637
'aks',
2738
'command',
@@ -31,7 +42,7 @@ export class PrivateKubectl extends Kubectl {
3142
'--name',
3243
this.name,
3344
'--command',
34-
kubectlCmd
45+
`${kubectlCmd}`
3546
]
3647

3748
if (addFileFlag) {
@@ -57,10 +68,13 @@ export class PrivateKubectl extends Kubectl {
5768
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
5869
)
5970

60-
const runOutput = await getExecOutput(
61-
'az',
62-
[...privateClusterArgs, '-o', 'json'],
63-
eo
71+
const allArgs = [...privateClusterArgs, '-o', 'json']
72+
core.debug(`full form of az command: az ${allArgs.join(' ')}`)
73+
const runOutput = await getExecOutput('az', allArgs, eo)
74+
core.debug(
75+
`from kubectl private cluster command got run output ${JSON.stringify(
76+
runOutput
77+
)}`
6478
)
6579
const runObj: {logs: string; exitCode: number} = JSON.parse(
6680
runOutput.stdout
@@ -93,23 +107,31 @@ export class PrivateKubectl extends Kubectl {
93107
}
94108

95109
public extractFilesnames(strToParse: string) {
96-
let start = strToParse.indexOf('-filename')
97-
let offset = 7
110+
const fileNames: string[] = []
111+
const argv = minimist(strToParse.split(' '))
112+
const fArg = 'f'
113+
const filenameArg = 'filename'
98114

99-
if (start == -1) {
100-
start = strToParse.indexOf('-f')
115+
fileNames.push(...this.extractFilesFromMinimist(argv, fArg))
116+
fileNames.push(...this.extractFilesFromMinimist(argv, filenameArg))
101117

102-
if (start == -1) {
103-
return ''
118+
return fileNames.join(' ')
119+
}
120+
121+
private extractFilesFromMinimist(argv, arg: string): string[] {
122+
if (!argv[arg]) {
123+
return []
124+
}
125+
const toReturn: string[] = []
126+
if (typeof argv[arg] === 'string') {
127+
toReturn.push(...argv[arg].split(','))
128+
} else {
129+
for (const value of argv[arg] as string[]) {
130+
toReturn.push(...value.split(','))
104131
}
105-
offset = 0
106132
}
107133

108-
let temp = strToParse.substring(start + offset)
109-
let end = temp.indexOf(' -')
110-
111-
//End could be case where the -f flag was last, or -f is followed by some additonal flag and it's arguments
112-
return temp.substring(3, end == -1 ? temp.length : end).trim()
134+
return toReturn
113135
}
114136

115137
private containsFilenames(str: string) {

test/integration/k8s-deploy-test.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespaceKey = "namespace"
2121
ingressServicesKey = "ingressServices"
2222
tsServicesKey = "tsServices"
23+
privateKey = "private"
2324

2425

2526
def parseArgs(sysArgs):
@@ -197,18 +198,36 @@ def main():
197198
kind = parsedArgs[kindKey]
198199
name = parsedArgs[nameKey]
199200
namespace = parsedArgs[namespaceKey]
200-
print('kubectl get '+kind+' '+name+' -n '+namespace+' -o json')
201+
cmd = 'kubectl get '+kind + ' '+name+' -n '+namespace+' -o json'
201202

203+
k8s_object = None
204+
azPrefix = ""
202205
try:
203-
k8_object = json.load(os.popen('kubectl get '+kind +
204-
' '+name+' -n '+namespace+' -o json'))
206+
if privateKey in parsedArgs:
207+
uniqueName = parsedArgs[privateKey]
208+
azPrefix = f"az aks command invoke --resource-group {uniqueName} --name {uniqueName} --command "
209+
cmd = azPrefix + "'" + cmd + "'"
210+
outputString = os.popen(cmd).read()
211+
successExit = "exitcode=0"
212+
if successExit not in outputString:
213+
raise ValueError(f"private cluster get failed for {kind} {name}")
214+
215+
objString = outputString.split(successExit)[1]
216+
k8_object = json.loads(objString)
217+
218+
else:
219+
k8_object = json.load(os.popen(cmd))
205220

206221
if k8_object == None:
207222
raise ValueError(f"{kind} {name} was not found")
223+
208224
except:
209225
msg = kind+' '+name+' not created or not found'
210-
foundObjects = json.load(
211-
os.popen('kubectl get '+kind+' -n '+namespace+' -o json'))
226+
getAllObjectsCmd = azPrefix + 'kubectl get '+kind+' -n '+namespace
227+
if not azPrefix == "":
228+
getAllObjectsCmd = azPrefix + "'{getAllObjectsCmd}'" # add extra set of quotes
229+
cmd = + "'" + cmd + "'"
230+
foundObjects = os.popen().read()
212231
suffix = f"resources of type {kind}: {foundObjects}"
213232
sys.exit(msg + " " + suffix)
214233

0 commit comments

Comments
 (0)