mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-21 18:59:27 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 921d6c4ef8 | |||
| 93550c22f0 | |||
| 1fea8281df | |||
| 1b1edcdfc7 | |||
| 8cbe18c310 | |||
| 8efbc8ba92 | |||
| 699a70732d | |||
| a1d061da9d | |||
| 7c36b75ebe | |||
| 2f2901757b | |||
| 4aba7c26f3 | |||
| d6508445a1 | |||
| a462095a3c |
@@ -1,14 +1,15 @@
|
||||
name: Create release PR
|
||||
name: release Project
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Define release version (ex: v1, v2, v3)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release-pr:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||
release:
|
||||
uses: Azure/action-release-workflows/.github/workflows/release_js_project.yaml@81e6a8ed41ced9d131dea884ecae7b8c6dc4f799
|
||||
with:
|
||||
release: ${{ github.event.inputs.release }}
|
||||
changelogPath: ./CHANGELOG.md
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
name: Minikube Integration Tests - resource annotation
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
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 }}
|
||||
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: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation enabled by default
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments is created with additional resource annotation
|
||||
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_-_resource_annotation selectorLabels=app:nginx annotations=actions.github.com/k8s-deploy,deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
|
||||
- name: Cleaning previously created deployment
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation disabled
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
annotate-resources: false
|
||||
|
||||
- name: Checking if deployment is created without additional resource annotation
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 selectorLabels=app:nginx annotations=deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
@@ -0,0 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [4.10.0] - 2023-10-30
|
||||
|
||||
### Added
|
||||
|
||||
- #287 Make annotating resources optional
|
||||
- #283 Fix “Service” route-method of the Blue-Green strategy with some manifest files
|
||||
- #281 bump codeql to node 16
|
||||
- #279 upgrade codeql
|
||||
- #276 Fixes multiple namespaces bug
|
||||
@@ -113,9 +113,13 @@ Following are the key capabilities of this action:
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-resources</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the resources or not. If set to false all annotations are skipped completely.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not. Ignored when annotate-resources is set to false.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>skip-tls-verify</br></br>(Optional)</td>
|
||||
|
||||
+5
-1
@@ -59,8 +59,12 @@ inputs:
|
||||
description: 'Github token'
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-resources:
|
||||
description: 'Annotate the resources. If set to false all annotations are skipped completely.'
|
||||
required: false
|
||||
default: true
|
||||
annotate-namespace:
|
||||
description: 'Annotate the target namespace'
|
||||
description: 'Annotate the target namespace. Ignored when annotate-resources is set to false.'
|
||||
required: false
|
||||
default: true
|
||||
private-cluster:
|
||||
|
||||
+578
-542
File diff suppressed because it is too large
Load Diff
Generated
+584
-222
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -4,7 +4,7 @@
|
||||
"author": "Deepak Sattiraju",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "npx ncc build src/run.ts -o lib",
|
||||
"build": "npm i ncc && npx ncc build src/run.ts -o lib",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage=true",
|
||||
"format": "prettier --write .",
|
||||
@@ -18,7 +18,8 @@
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@types/minipass": "^3.1.2",
|
||||
"js-yaml": "3.13.1"
|
||||
"js-yaml": "3.13.1",
|
||||
"ncc": "^0.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.0",
|
||||
|
||||
@@ -65,17 +65,10 @@ export async function deploy(
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
await annotateAndLabelResources(
|
||||
deployedManifestFiles,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
allPods
|
||||
resourceTypes
|
||||
)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
+2
-19
@@ -129,19 +129,13 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
const resources: Resource[] = getResources(
|
||||
filesToAnnotate,
|
||||
models.DEPLOYMENT_TYPES.concat([
|
||||
models.DiscoveryAndLoadBalancerResource.SERVICE
|
||||
])
|
||||
)
|
||||
await annotateAndLabelResources(filesToAnnotate, kubectl, resources, allPods)
|
||||
await annotateAndLabelResources(filesToAnnotate, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -219,17 +213,6 @@ async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
await annotateAndLabelResources(
|
||||
deployedManifestFiles,
|
||||
kubectl,
|
||||
resources,
|
||||
allPods
|
||||
)
|
||||
await annotateAndLabelResources(deployedManifestFiles, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -147,8 +147,7 @@ export async function checkManifestStability(
|
||||
export async function annotateAndLabelResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[],
|
||||
allPods: any
|
||||
resourceTypes: Resource[]
|
||||
) {
|
||||
const defaultWorkflowFileName = 'k8s-deploy-failed-workflow-annotation'
|
||||
const githubToken = core.getInput('token')
|
||||
@@ -163,15 +162,20 @@ export async function annotateAndLabelResources(
|
||||
const deploymentConfig = await getDeploymentConfig()
|
||||
const annotationKeyLabel = getWorkflowAnnotationKeyLabel()
|
||||
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
allPods,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
|
||||
const shouldAnnotateResources = !(
|
||||
core.getInput('annotate-resources').toLowerCase() === 'false'
|
||||
)
|
||||
|
||||
if (shouldAnnotateResources) {
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
|
||||
}
|
||||
|
||||
await labelResources(files, kubectl, annotationKeyLabel).catch((err) =>
|
||||
core.warning(`Failed to label resources: ${err}`)
|
||||
@@ -182,7 +186,6 @@ async function annotateResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[],
|
||||
allPods: any,
|
||||
annotationKey: string,
|
||||
workflowFilePath: string,
|
||||
deploymentConfig: DeploymentConfig
|
||||
@@ -226,11 +229,13 @@ async function annotateResources(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const annotateResult = await kubectl.annotateFiles(
|
||||
file,
|
||||
annotationKeyValStr
|
||||
annotationKeyValStr,
|
||||
namespace
|
||||
)
|
||||
annotateResults.push(annotateResult)
|
||||
} catch (e) {
|
||||
@@ -249,8 +254,7 @@ async function annotateResources(
|
||||
resource.type,
|
||||
resource.name,
|
||||
resource.namespace,
|
||||
annotationKeyValStr,
|
||||
allPods
|
||||
annotationKeyValStr
|
||||
)
|
||||
).forEach((execResult) => annotateResults.push(execResult))
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ export async function annotateChildPods(
|
||||
resourceType: string,
|
||||
resourceName: string,
|
||||
namespace: string | undefined,
|
||||
annotationKeyValStr: string,
|
||||
allPods
|
||||
annotationKeyValStr: string
|
||||
): Promise<ExecOutput[]> {
|
||||
let owner = resourceName
|
||||
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
||||
@@ -70,6 +69,14 @@ export async function annotateChildPods(
|
||||
}
|
||||
|
||||
const commandExecutionResults = []
|
||||
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
|
||||
if (allPods?.items && allPods.items?.length > 0) {
|
||||
allPods.items.forEach((pod) => {
|
||||
const owners = pod?.metadata?.ownerReferences
|
||||
|
||||
@@ -7,7 +7,7 @@ def delete(kind, name, namespace):
|
||||
if (name == "all"):
|
||||
print('kubectl delete --all' + kind + ' -n ' + namespace)
|
||||
deletion = subprocess.Popen(
|
||||
['kubectl', 'delete', kind, name, '--namespace', namespace])
|
||||
['kubectl', 'delete', kind, '--all', '--namespace', namespace])
|
||||
result, err = deletion.communicate()
|
||||
else:
|
||||
print('kubectl delete ' + kind + ' ' + name + ' -n ' + namespace)
|
||||
@@ -21,7 +21,7 @@ def delete(kind, name, namespace):
|
||||
def main():
|
||||
kind = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
namespace = 'test-' + sys.argv[3]
|
||||
namespace = sys.argv[3]
|
||||
delete(kind, name, namespace)
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,6 @@ def parseArgs(sysArgs):
|
||||
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(","), ":")
|
||||
@@ -60,6 +56,9 @@ def parseArgs(sysArgs):
|
||||
if ingressServicesKey in argsDict:
|
||||
argsDict[ingressServicesKey] = argsDict[ingressServicesKey].split(",")
|
||||
|
||||
if annotationsKey in argsDict:
|
||||
argsDict[annotationsKey] = argsDict[annotationsKey].split(",")
|
||||
|
||||
return argsDict
|
||||
|
||||
|
||||
@@ -98,14 +97,14 @@ def verifyDeployment(deployment, parsedArgs):
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
dictMatch, msg = compareDicts(
|
||||
deployment['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if len(parsedArgs[annotationsKey]) != len(deployment['metadata']['annotations']):
|
||||
return False, f"expected {len(parsedArgs[annotationsKey])} annotations but found {len(deployment['metadata']['annotations'])}"
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
deployment['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
return True, ""
|
||||
|
||||
|
||||
def verifyService(service, parsedArgs):
|
||||
# test selector labels, labels, annotations
|
||||
if not selectorLabelsKey in parsedArgs:
|
||||
@@ -124,10 +123,10 @@ def verifyService(service, parsedArgs):
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
dictMatch, msg = compareDicts(
|
||||
service['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
service['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
@@ -188,6 +187,13 @@ def compareDicts(actual: dict, expected: dict, paramName=""):
|
||||
|
||||
return True, ""
|
||||
|
||||
def validateKeyPresence(actualDict: dict, expectedKeys: list):
|
||||
actualKeys = actualDict.keys()
|
||||
for key in expectedKeys:
|
||||
if key not in actualKeys:
|
||||
return False, f"expected key {key} not found in actual dict. \n actual dict keys {','.join(actualKeys)}"
|
||||
|
||||
return True, ""
|
||||
|
||||
def main():
|
||||
parsedArgs: dict = parseArgs(sys.argv[1:])
|
||||
@@ -220,7 +226,7 @@ def main():
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user