Skip to content

Commit

Permalink
new commit with all changes (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaiveerk authored Nov 21, 2022
1 parent e9693a7 commit 58ba3f0
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-integration-tests-basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
test/integration/manifests/test.yml
action: deploy

- name: Checking if deployments and services were created with canary labels and original tag
- name: Checking if deployments and services were created
run: |
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
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
81 changes: 81 additions & 0 deletions .github/workflows/run-integration-tests-private.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Cluster Integration Tests - private cluster
on:
pull_request:
branches:
- 'releases/*'
push:
branches:
- main
workflow_dispatch:

jobs:
run-integration-test:
name: Run Minikube Integration Tests
runs-on: ubuntu-latest
env:
KUBECONFIG: /home/runner/.kube/config
NAMESPACE: test-${{ github.run_id }}
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v3

- name: Install dependencies
run: |
rm -rf node_modules/
npm install
- name: Install ncc
run: npm i -g @vercel/ncc
- name: Build
run: ncc build src/run.ts -o lib
- name: Azure login
uses: azure/login@v1.4.3
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- uses: Azure/setup-kubectl@v3
name: Install Kubectl

- name: Create private AKS cluster and set context
run: |
set +x
# create cluster
az group create --location eastus --name ${{ env.NAMESPACE }}
az aks create --name ${{ env.NAMESPACE }} --resource-group ${{ env.NAMESPACE }} --enable-private-cluster --generate-ssh-keys
az aks get-credentials --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
- name: Create namespace to run tests
run: |
az aks command invoke --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }} --command "kubectl create ns ${{ env.NAMESPACE }}"
- uses: actions/setup-python@v2
name: Install Python
with:
python-version: '3.x'

- name: Executing deploy action for pod
uses: ./
with:
namespace: ${{ env.NAMESPACE }}
images: nginx:1.14.2
manifests: |
test/integration/manifests/test.yml
action: deploy
private-cluster: true
resource-group: ${{ env.NAMESPACE }}
name: ${{ env.NAMESPACE }}

- name: Checking if deployments and services were created
run: |
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
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
- name: Clean up AKS cluster
if: ${{ always() }}
run: |
echo "deleting AKS cluster and resource group"
az aks delete --yes --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
az group delete --resource-group ${{ env.NAMESPACE }} --yes
1 change: 1 addition & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ inputs:
namespace:
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
required: false
default: default
manifests:
description: 'Path to the manifest files which will be used for deployment.'
required: true
Expand Down
11 changes: 0 additions & 11 deletions src/types/kubectl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,6 @@ describe('Kubectl class', () => {
return execReturn
})
})

describe('omits default namespace from commands', () => {
it('executes a command without appending --namespace arg', async () => {
// no args
const command = 'command'
expect(await kubectl.executeCommand(command)).toBe(execReturn)
expect(exec.getExecOutput).toBeCalledWith(kubectlPath, [command], {
silent: false
})
})
})
})

describe('with a success exec return in testNamespace', () => {
Expand Down
9 changes: 7 additions & 2 deletions src/types/kubectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,18 @@ export class Kubectl {
files: string | string[],
annotation: string
): Promise<ExecOutput> {
const filesToAnnotate = createInlineArray(files)
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`)
const args = [
'annotate',
'-f',
createInlineArray(files),
filesToAnnotate,
annotation,
'--overwrite'
]
core.debug(
`sending args from annotate to execute: ${JSON.stringify(args)}`
)
return await this.execute(args)
}

Expand Down Expand Up @@ -169,7 +174,7 @@ export class Kubectl {
if (this.ignoreSSLErrors) {
args.push('--insecure-skip-tls-verify')
}
if (this.namespace && this.namespace != 'default') {
if (this.namespace) {
args = args.concat(['--namespace', this.namespace])
}
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
Expand Down
12 changes: 12 additions & 0 deletions src/types/privatekubectl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {PrivateKubectl} from './privatekubectl'

describe('Private kubectl', () => {
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`
const mockKube = new PrivateKubectl('')

it('should extract filenames correctly', () => {
expect(mockKube.extractFilesnames(testString)).toEqual(
'test.yml test2.yml test3.yml test4.yml test5.yml'
)
})
})
56 changes: 39 additions & 17 deletions src/types/privatekubectl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Kubectl} from './kubectl'
import * as minimist from 'minimist'
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
import * as core from '@actions/core'
import * as os from 'os'
Expand All @@ -7,6 +8,9 @@ import * as path from 'path'

export class PrivateKubectl extends Kubectl {
protected async execute(args: string[], silent: boolean = false) {
if (this.namespace) {
args = args.concat(['--namespace', this.namespace])
}
args.unshift('kubectl')
let kubectlCmd = args.join(' ')
let addFileFlag = false
Expand All @@ -22,6 +26,13 @@ export class PrivateKubectl extends Kubectl {
addFileFlag = true
}

if (this.resourceGroup === '') {
throw Error('Resource group must be specified for private cluster')
}
if (this.name === '') {
throw Error('Cluster name must be specified for private cluster')
}

const privateClusterArgs = [
'aks',
'command',
Expand All @@ -31,7 +42,7 @@ export class PrivateKubectl extends Kubectl {
'--name',
this.name,
'--command',
kubectlCmd
`${kubectlCmd}`
]

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

const runOutput = await getExecOutput(
'az',
[...privateClusterArgs, '-o', 'json'],
eo
const allArgs = [...privateClusterArgs, '-o', 'json']
core.debug(`full form of az command: az ${allArgs.join(' ')}`)
const runOutput = await getExecOutput('az', allArgs, eo)
core.debug(
`from kubectl private cluster command got run output ${JSON.stringify(
runOutput
)}`
)
const runObj: {logs: string; exitCode: number} = JSON.parse(
runOutput.stdout
Expand Down Expand Up @@ -93,23 +107,31 @@ export class PrivateKubectl extends Kubectl {
}

public extractFilesnames(strToParse: string) {
let start = strToParse.indexOf('-filename')
let offset = 7
const fileNames: string[] = []
const argv = minimist(strToParse.split(' '))
const fArg = 'f'
const filenameArg = 'filename'

if (start == -1) {
start = strToParse.indexOf('-f')
fileNames.push(...this.extractFilesFromMinimist(argv, fArg))
fileNames.push(...this.extractFilesFromMinimist(argv, filenameArg))

if (start == -1) {
return ''
return fileNames.join(' ')
}

private extractFilesFromMinimist(argv, arg: string): string[] {
if (!argv[arg]) {
return []
}
const toReturn: string[] = []
if (typeof argv[arg] === 'string') {
toReturn.push(...argv[arg].split(','))
} else {
for (const value of argv[arg] as string[]) {
toReturn.push(...value.split(','))
}
offset = 0
}

let temp = strToParse.substring(start + offset)
let end = temp.indexOf(' -')

//End could be case where the -f flag was last, or -f is followed by some additonal flag and it's arguments
return temp.substring(3, end == -1 ? temp.length : end).trim()
return toReturn
}

private containsFilenames(str: string) {
Expand Down
29 changes: 24 additions & 5 deletions test/integration/k8s-deploy-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
namespaceKey = "namespace"
ingressServicesKey = "ingressServices"
tsServicesKey = "tsServices"
privateKey = "private"


def parseArgs(sysArgs):
Expand Down Expand Up @@ -197,18 +198,36 @@ def main():
kind = parsedArgs[kindKey]
name = parsedArgs[nameKey]
namespace = parsedArgs[namespaceKey]
print('kubectl get '+kind+' '+name+' -n '+namespace+' -o json')
cmd = 'kubectl get '+kind + ' '+name+' -n '+namespace+' -o json'

k8s_object = None
azPrefix = ""
try:
k8_object = json.load(os.popen('kubectl get '+kind +
' '+name+' -n '+namespace+' -o json'))
if privateKey in parsedArgs:
uniqueName = parsedArgs[privateKey]
azPrefix = f"az aks command invoke --resource-group {uniqueName} --name {uniqueName} --command "
cmd = azPrefix + "'" + cmd + "'"
outputString = os.popen(cmd).read()
successExit = "exitcode=0"
if successExit not in outputString:
raise ValueError(f"private cluster get failed for {kind} {name}")

objString = outputString.split(successExit)[1]
k8_object = json.loads(objString)

else:
k8_object = json.load(os.popen(cmd))

if k8_object == None:
raise ValueError(f"{kind} {name} was not found")

except:
msg = kind+' '+name+' not created or not found'
foundObjects = json.load(
os.popen('kubectl get '+kind+' -n '+namespace+' -o json'))
getAllObjectsCmd = azPrefix + 'kubectl get '+kind+' -n '+namespace
if not azPrefix == "":
getAllObjectsCmd = azPrefix + "'{getAllObjectsCmd}'" # add extra set of quotes
cmd = + "'" + cmd + "'"
foundObjects = os.popen().read()
suffix = f"resources of type {kind}: {foundObjects}"
sys.exit(msg + " " + suffix)

Expand Down

0 comments on commit 58ba3f0

Please sign in to comment.