k8s-deploy/test/integration/k8s-deploy-test.py
github-actions[bot] d89c89ba4e
v4 new release (#261)
* Add missing API switch for GHES (#200)

* Vidya reddy/prettier code (#203)

* switch none deployment strategy to basic (#204)

* switch none deployment strategy to basic

* update readme

* update deployment strategy fallthrough logic

* comment fixed

* add disclaimer for basic strategy only supporting deploy action

* Hari/beautify logs (#206)

* Logging changes for deploy

* Logging Changes with group

* format check changes

* Add ncc build to build script (#208)

Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>

* Logging Changes for Promote, Reject actions (#207)

* add clean function (#211)

* Added Traffic split annotations (#215)

* Added Traffic split annotations

* traffic split - blueGreen deployment

* traffic split - canary deployment

* Traffic split annotations - canary deployment

* updated Readme and action.yml

* Traffic split - canary deployment

* clean code

* Clean code

* Clean code

* Create annotation object

* Updated Readme and action.yml

* Spelling correction

Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>

* Swap annotation key to actions.github.com prefix (#216)

* Private Cluster functionality (#214)

* Fixed Blue/Green Strategy Ingress Route-Method Glitch  (#217)

* Added some tests, not sure what else to try but gonna think of more examples

* forgot some files

* reverted package-lock.json

* Added empty dir test

* Cleaned up some extra spaces

* Add node modules and compiled JavaScript from main

* forgot to actually include functionality

* removed unnecessary files

* Update .gitignore

* Update .gitignore

* Update .gitignore

* thx david

* renamed searchFilesRec

* integrations test fix

* added examples to README

* added note about depth

* added additional note

* removed ticks

* changed version string

* removed conflict on readme

* Added tests for bluegreen helper and resolved issue with ingress not being read correctly, still have to figure out why new services aren't showing up

* resolved services name issue

* looks functional, beginning refactor now

* refactored deploy methods for type error

* Removed refactor comments

* prettier

* implemented Oliver's feedback

* prettier

* added optional chaining operator

* removed refactor comment

Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MacBook-Pro.local>
Co-authored-by: Oliver King <oking3@uncc.edu>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>

* Blue/Green Refactor (#229)

* fresh new branch

* Added coverage to gitignore

Signed-off-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>

* reverted package-lock.json

Signed-off-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>

* consider slashes while cleaning labels (#231)

fix prettier format check errors

* Fix README.md typo (#235)

* Bump @actions/core from 1.9.0 to 1.9.1 (#233)

Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add permissions to README.md (#236)

* Add permissions to README.md

* remove space

* prettier

* remove extra changes

* fix spacing

* Add the bug report and feature request form (#237)

* Added the bug report and feature request form

* updated the url

* Fix issue form (#238)

* Fix description about baseline-and-canary-replicas (#241)

* Resolved issue with Canary deploy (#247)

* Added support message (#249)

* Deploy with Manifests from URLs (#251)

* added functionality, need to add/modify existing tests

* added tests

* updated readme

* prettier

* Fix private cluster kubectl exit code bug (#252)

* add private cluster exitCode check

* add proper output

* Added Integration Tests, Resolved Bugs With Annotations (#255)

* First commit - made manifests for test deployments, made manifests for i tests for other deployment strategies

* broke down blue/green

* added latest tags to test manifests for new tags

* remade tester

* ready to test bgi

* using all but first index of argv

* careless error with dicts

* added test to namespace

* realized i was silencing error

* indexing containers

* keyerror

* logging bc python errors are weird

* expected still string

* parsed args behaving weirdly

* test seems to be working now, applying changes to other YAMLs now

* blue/green ready to test

* oops

* oops

* Added additional labels to check

* hyphen

* Added our annotations

* lol

* added our labels to services too

* nonetype issue'

* nonetype issue'

* narrowing down parameter

* fixed annotations issue with promote

* adding debhug statement to figure out why services aren't getting annotations

* this should fix annotations issue for service

* not sure why this wasn't caught by intellisense

* should be fixed with removing comma but adding logs in case

* added linkerd install

* verification

* upgraded kubernetes version

* removing crds

* proxy option

* Added smi extension

* logging service

* smi svcs also getting labeled now

* matching ts type

* not sure where stable service is going

* remaining svc and deployment should match

* keeping stable service and ts object

* updated tests to reflect keeping ts object

* no green svc after promote

* duh

* lol

* canary work

* canary test ready

* logging for ing, filename for canary

* changed ingress svc key and returning svc files from smi canary deployment

* ts name

* forgot about baseline in first deploy

* *

* *

* smi canary should annotate, fixed cleanup

* typescript issue plus percentage

* forgot to type extra method

* removed cleaned up objects from annotate list

* logging because services aren't getting removed

* moving to try/catch strategy of annotation since deletion can fail silently/with warnings

* moved label to individual

* removing canary service check after promote

* pod ready for testing

* set weights to 1000

* selectors

* *

* percentage

* *

* typing

* mixed up pod and smi

* fixed tests

* prettier

* forgot to remove canary

* cleanup

* Added oliver's feedback + more cleanup

* ncc as dev dependency

* npx

* going back to global ncc install bc npm is being weird

* prettier

* removed unnecessary post step

* new commit with all changes (#258)

* Fixing Ubuntu Runner Issue (#259)

* changed ubuntu runner

* changed minikube action

* Version formatting

* nonedriveR

* update kube version

* installing conntrack'

* updated other actions

* update bg ingress api version

* prettify

* updated ingress backend for new api version

* Added path type

* prettify

* Add skip tls flag (#260)

* Add node modules and compiled JavaScript from main

Signed-off-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: nv35 <76777923+nv35@users.noreply.github.com>
Co-authored-by: Vidya <59590642+Vidya2606@users.noreply.github.com>
Co-authored-by: David Gamero <david340804@gmail.com>
Co-authored-by: Hariharan Subramanian <105889062+hsubramanianaks@users.noreply.github.com>
Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
Co-authored-by: Oliver King <oking3@uncc.edu>
Co-authored-by: Marcus-Hines <marcus.chris.hines@gmail.com>
Co-authored-by: Jaiveer Katariya <35347859+jaiveerk@users.noreply.github.com>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MacBook-Pro.local>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
Co-authored-by: Alexander Bartsch <alex@dashlabs.de>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kenta Nakase <parroty@users.noreply.github.com>
Co-authored-by: Asa Gayle <azmatch.gayle@gmail.com>
2022-11-23 16:30:26 -05:00

253 lines
8.6 KiB
Python

from operator import truediv
import os
import sys
import json
from unicodedata import name
# This integration test is used to confirm that k8s resources of a specified name, type, and configuration have been deployed.
# Expected configurations are fed into the python script as command-line arguments and are compared to the configuration of resources that have been deployed.
# args will be formatted like labels=testkey:testValue,otherKey=otherValue
# or for singular ones, just with containerName=container
kindKey = "kind"
nameKey = "name"
containerKey = "containerName"
labelsKey = "labels"
annotationsKey = "annotations"
selectorLabelsKey = "selectorLabels"
namespaceKey = "namespace"
ingressServicesKey = "ingressServices"
tsServicesKey = "tsServices"
privateKey = "private"
def parseArgs(sysArgs):
argsDict = stringListToDict(sysArgs, "=")
# mandatory parameters
if not kindKey in argsDict:
raise ValueError(f"missing key: {kindKey}")
if not nameKey in argsDict:
raise ValueError(f"missing key: {nameKey}")
if not namespaceKey in argsDict:
raise ValueError(f"missing key: {namespaceKey}")
# reformat map-like parameters (eg, paramName=key1:value1,key2:value2)
if labelsKey in argsDict:
argsDict[labelsKey] = stringListToDict(
argsDict[labelsKey].split(","), ":")
if annotationsKey in argsDict:
argsDict[annotationsKey] = stringListToDict(
argsDict[annotationsKey].split(","), ":")
if selectorLabelsKey in argsDict:
argsDict[selectorLabelsKey] = stringListToDict(
argsDict[selectorLabelsKey].split(","), ":")
if tsServicesKey in argsDict:
argsDict[tsServicesKey] = stringListToDict(
argsDict[tsServicesKey].split(","), ":")
for key in argsDict[tsServicesKey]:
argsDict[tsServicesKey][key] = int(argsDict[tsServicesKey][key])
# reformat list-like parameters (eg, paramName=value1,value2,value3)
if ingressServicesKey in argsDict:
argsDict[ingressServicesKey] = argsDict[ingressServicesKey].split(",")
return argsDict
def stringListToDict(args: list[str], separator: str):
parsedArgs = {}
for arg in args:
print(f"parsing arg {arg}")
argSplit = arg.split(separator)
parsedArgs[argSplit[0]] = argSplit[1]
return parsedArgs
def verifyDeployment(deployment, parsedArgs):
# test container image, labels, annotations, selector labels
if not containerKey in parsedArgs:
raise ValueError(
f"expected container image name not provided to inspect deployment {parsedArgs[nameKey]}")
actualImageName = deployment['spec']['template']['spec']['containers'][0]['image']
if not actualImageName == parsedArgs[containerKey]:
return False, f"expected container image name {parsedArgs[containerKey]} but got {actualImageName} instead"
if not selectorLabelsKey in parsedArgs:
raise ValueError(
f"expected selector labels not provided to inspect deployment {parsedArgs[nameKey]}")
dictMatch, msg = compareDicts(
deployment['spec']['selector']['matchLabels'], parsedArgs[selectorLabelsKey], selectorLabelsKey)
if not dictMatch:
return dictMatch, msg
if labelsKey in parsedArgs:
dictMatch, msg = compareDicts(
deployment['metadata']['labels'], parsedArgs[labelsKey], labelsKey)
if not dictMatch:
return dictMatch, msg
if annotationsKey in parsedArgs:
dictMatch, msg = compareDicts(
deployment['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
if not dictMatch:
return dictMatch, msg
return True, ""
def verifyService(service, parsedArgs):
# test selector labels, labels, annotations
if not selectorLabelsKey in parsedArgs:
raise ValueError(
f"expected selector labels not provided to inspect service {parsedArgs[nameKey]}")
dictMatch, msg = compareDicts(
service['spec']['selector'], parsedArgs[selectorLabelsKey], selectorLabelsKey)
if not dictMatch:
return dictMatch, msg
if labelsKey in parsedArgs:
print(f" service is {service}")
dictMatch, msg = compareDicts(
service['metadata']['labels'], parsedArgs[labelsKey], labelsKey)
if not dictMatch:
return dictMatch, msg
if annotationsKey in parsedArgs:
dictMatch, msg = compareDicts(
service['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
if not dictMatch:
return dictMatch, msg
return True, ""
def verifyIngress(ingress, parsedArgs):
# test services in paths
if not ingressServicesKey in parsedArgs:
raise ValueError(
f"expected services not provided to inspect ingress {parsedArgs[nameKey]}")
expectedIngresses = parsedArgs[ingressServicesKey]
for i in range(len(ingress['spec']['rules'][0]['http']['paths'])):
print(
f"service obj is {ingress['spec']['rules'][0]['http']['paths'][i]}")
svcName = ingress['spec']['rules'][0]['http']['paths'][i]['backend']['service']['name']
if svcName != expectedIngresses[i]:
return False, f"for ingress {parsedArgs[nameKey]} expected svc name {expectedIngresses[i]} at position {i} but got {svcName}"
return True, ""
def verifyTSObject(tsObj, parsedArgs):
if not tsServicesKey in parsedArgs:
raise ValueError(
f"expected services not provided to inspect ts object {parsedArgs[nameKey]}")
expectedServices = parsedArgs[tsServicesKey]
actualServices = {}
backends = tsObj['spec']['backends']
for i in range(len(backends)):
svcName = backends[i]['service']
svcWeight = int(backends[i]['weight'])
actualServices[svcName] = svcWeight
dictResult, msg = compareDicts(
actualServices, expectedServices, tsServicesKey)
if not dictResult:
return False, msg
return True, ""
def compareDicts(actual: dict, expected: dict, paramName=""):
actualKeys = actual.keys()
expectedKeys = expected.keys()
if not actualKeys == expectedKeys:
msg = f'dicts had different keys.\n actual: {actual}\n expected: {expected}'
if not paramName == "":
msg = f"for param {paramName}, " + msg
return False, msg
for key in actualKeys:
if not actual[key] == expected[key]:
msg = f'dicts differed at key {key}.\n actual[{key}] is {actual[key]} and expected[{key}] is {expected[key]}'
if not paramName == "":
msg = f"for param {paramName}, " + msg
return False, msg
return True, ""
def main():
parsedArgs: dict = parseArgs(sys.argv[1:])
RESULT = False
msg = "unknown type (no verification method currently exists)"
k8_object = None
kind = parsedArgs[kindKey]
name = parsedArgs[nameKey]
namespace = parsedArgs[namespaceKey]
cmd = 'kubectl get '+kind + ' '+name+' -n '+namespace+' -o json'
k8s_object = None
azPrefix = ""
try:
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'
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)
if kind == 'Deployment':
RESULT, msg = verifyDeployment(
k8_object, parsedArgs)
if kind == 'Service':
RESULT, msg = verifyService(
k8_object, parsedArgs)
if kind == 'Ingress':
RESULT, msg = verifyIngress(k8_object, parsedArgs)
if kind == "TrafficSplit":
RESULT, msg = verifyTSObject(k8_object, parsedArgs)
if not RESULT:
sys.exit(f"{kind} {name} failed check: {msg}")
print('Test passed')
if __name__ == "__main__":
sys.exit(main())