mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-23 04:59:26 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7362e2ee9b | |||
| 2a96bf3d2c | |||
| caa56759c2 | |||
| bae916660e | |||
| 8e795671c2 | |||
| d784fb6c4d | |||
| 534896c172 | |||
| 634370ab70 | |||
| 0e57c19ffe | |||
| 9f25026d56 | |||
| b595866809 | |||
| 00f71d4310 |
@@ -1,28 +0,0 @@
|
|||||||
name: Bug Report
|
|
||||||
description: File a bug report, we will respond to this thread with any questions.
|
|
||||||
title: 'Bug: '
|
|
||||||
labels: ['bug', 'triage']
|
|
||||||
assignees: '@Azure/aks-atlanta'
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: What-happened
|
|
||||||
attributes:
|
|
||||||
label: What happened?
|
|
||||||
description: Tell us what happened and how is it different form the expected?
|
|
||||||
placeholder: Tell us what you see!
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: Runner
|
|
||||||
attributes:
|
|
||||||
label: Runner
|
|
||||||
description: What runner are you using?
|
|
||||||
placeholder: Mention the runner info (self-hosted, operating system)
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: Logs
|
|
||||||
attributes:
|
|
||||||
label: Relevant log output
|
|
||||||
description: Run in debug mode for the most verbose logs. Please feel free to attach a screenshot of the logs
|
|
||||||
render: shell
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: GitHub Action "aks-set-context" Support
|
|
||||||
url: https://github.com/Azure/aks-set-context
|
|
||||||
security: https://github.com/Azure/aks-set-context/blob/main/SECURITY.md
|
|
||||||
about: Please ask and answer questions here.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: File a Feature Request form, we will respond to this thread with any questions.
|
|
||||||
title: 'Feature Request: '
|
|
||||||
labels: ['Feature']
|
|
||||||
assignees: '@Azure/aks-atlanta'
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: Feature request
|
|
||||||
attributes:
|
|
||||||
label: Feature request
|
|
||||||
description: Provide example functionality and links to relevant docs
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
@@ -96,10 +96,6 @@ Following are the key capabilities of this action:
|
|||||||
<td>version-switch-buffer </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
<td>version-switch-buffer </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||||
<td>Acceptable values: 1-300.</br>Default value: 0.</br>Waits for the given input in minutes before routing traffic to '-green' workloads.</td>
|
<td>Acceptable values: 1-300.</br>Default value: 0.</br>Waits for the given input in minutes before routing traffic to '-green' workloads.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>private-cluster </br></br>(Optional and relevant only using K8's deploy for a cluster with private cluster enabled)</td>
|
|
||||||
<td>Acceptable values: true, false</br>Default value: false.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>force </br></br>(Optional)</td>
|
<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>
|
<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>
|
||||||
@@ -126,26 +122,6 @@ Following are the key capabilities of this action:
|
|||||||
image-pull-secret2
|
image-pull-secret2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Private cluster deployment
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: Azure/k8s-deploy@v4
|
|
||||||
with:
|
|
||||||
resource-group: yourResourceGroup
|
|
||||||
name: yourClusterName
|
|
||||||
action: deploy
|
|
||||||
strategy: basic
|
|
||||||
|
|
||||||
private-cluster: true
|
|
||||||
manifests: |
|
|
||||||
manifests/azure-vote-backend-deployment.yaml
|
|
||||||
manifests/azure-vote-backend-service.yaml
|
|
||||||
manifests/azure-vote-frontend-deployment.yaml
|
|
||||||
manifests/azure-vote-frontend-service.yaml
|
|
||||||
images: |
|
|
||||||
registry.azurecr.io/containername
|
|
||||||
```
|
|
||||||
|
|
||||||
### Canary deployment without service mesh
|
### Canary deployment without service mesh
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
-10
@@ -62,16 +62,6 @@ inputs:
|
|||||||
description: 'Annotate the target namespace'
|
description: 'Annotate the target namespace'
|
||||||
required: false
|
required: false
|
||||||
default: true
|
default: true
|
||||||
private-cluster:
|
|
||||||
description: 'True if cluster is AKS private cluster'
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
resource-group:
|
|
||||||
description: 'Name of resource group - Only required if using private cluster'
|
|
||||||
required: false
|
|
||||||
name:
|
|
||||||
description: 'Resource group name - Only required if using private cluster'
|
|
||||||
required: false
|
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
color: 'green'
|
color: 'green'
|
||||||
|
|||||||
Generated
+1984
-2400
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -23,8 +23,9 @@
|
|||||||
"@types/jest": "^26.0.0",
|
"@types/jest": "^26.0.0",
|
||||||
"@types/js-yaml": "^3.12.7",
|
"@types/js-yaml": "^3.12.7",
|
||||||
"@types/node": "^12.20.41",
|
"@types/node": "^12.20.41",
|
||||||
|
"@vercel/ncc": "^0.34.0",
|
||||||
"jest": "^26.0.0",
|
"jest": "^26.0.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "2.7.1",
|
||||||
"ts-jest": "^26.0.0",
|
"ts-jest": "^26.0.0",
|
||||||
"typescript": "3.9.5"
|
"typescript": "3.9.5"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-15
@@ -6,7 +6,6 @@ import {reject} from './actions/reject'
|
|||||||
import {Action, parseAction} from './types/action'
|
import {Action, parseAction} from './types/action'
|
||||||
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
||||||
import {getFilesFromDirectories} from './utilities/fileUtils'
|
import {getFilesFromDirectories} from './utilities/fileUtils'
|
||||||
import {PrivateKubectl} from './types/privatekubectl'
|
|
||||||
import {parseAnnotations} from './types/annotations'
|
import {parseAnnotations} from './types/annotations'
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
@@ -31,22 +30,10 @@ export async function run() {
|
|||||||
.filter((manifest) => manifest.length > 0) // remove any blanks
|
.filter((manifest) => manifest.length > 0) // remove any blanks
|
||||||
|
|
||||||
const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
|
const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
|
||||||
|
// create kubectl
|
||||||
const kubectlPath = await getKubectlPath()
|
const kubectlPath = await getKubectlPath()
|
||||||
const namespace = core.getInput('namespace') || 'default'
|
const namespace = core.getInput('namespace') || 'default'
|
||||||
const isPrivateCluster =
|
const kubectl = new Kubectl(kubectlPath, namespace, true)
|
||||||
core.getInput('private-cluster').toLowerCase() === 'true'
|
|
||||||
const resourceGroup = core.getInput('resource-group') || ''
|
|
||||||
const resourceName = core.getInput('name') || ''
|
|
||||||
|
|
||||||
const kubectl = isPrivateCluster
|
|
||||||
? new PrivateKubectl(
|
|
||||||
kubectlPath,
|
|
||||||
namespace,
|
|
||||||
true,
|
|
||||||
resourceGroup,
|
|
||||||
resourceName
|
|
||||||
)
|
|
||||||
: new Kubectl(kubectlPath, namespace, true)
|
|
||||||
|
|
||||||
// run action
|
// run action
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
import {
|
|
||||||
createWorkloadsWithLabel,
|
|
||||||
deleteWorkloadsAndServicesWithLabel,
|
|
||||||
getManifestObjects,
|
|
||||||
getNewBlueGreenObject,
|
|
||||||
GREEN_LABEL_VALUE,
|
|
||||||
isServiceRouted,
|
|
||||||
NONE_LABEL_VALUE
|
|
||||||
} from './blueGreenHelper'
|
|
||||||
import * as bgHelper from './blueGreenHelper'
|
|
||||||
import {Kubectl} from '../../types/kubectl'
|
|
||||||
import * as fileHelper from '../../utilities/fileUtils'
|
|
||||||
|
|
||||||
jest.mock('../../types/kubectl')
|
|
||||||
|
|
||||||
describe('bluegreenhelper functions', () => {
|
|
||||||
let testObjects
|
|
||||||
beforeEach(() => {
|
|
||||||
//@ts-ignore
|
|
||||||
Kubectl.mockClear()
|
|
||||||
testObjects = getManifestObjects(['test/unit/manifests/test-ingress.yml'])
|
|
||||||
|
|
||||||
jest
|
|
||||||
.spyOn(fileHelper, 'writeObjectsToFile')
|
|
||||||
.mockImplementationOnce(() => [''])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('it should parse objects correctly from one file', () => {
|
|
||||||
expect(testObjects.deploymentEntityList[0].kind).toBe('Deployment')
|
|
||||||
expect(testObjects.serviceEntityList[0].kind).toBe('Service')
|
|
||||||
expect(testObjects.ingressEntityList[0].kind).toBe('Ingress')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
testObjects.deploymentEntityList[0].spec.selector.matchLabels.app
|
|
||||||
).toBe('nginx')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('correctly makes new blue green object', () => {
|
|
||||||
const modifiedDeployment = getNewBlueGreenObject(
|
|
||||||
testObjects.deploymentEntityList[0],
|
|
||||||
GREEN_LABEL_VALUE
|
|
||||||
)
|
|
||||||
//@ts-ignore
|
|
||||||
expect(modifiedDeployment.metadata.name).toBe('nginx-deployment-green')
|
|
||||||
//@ts-ignore
|
|
||||||
expect(modifiedDeployment.metadata.labels['k8s.deploy.color']).toBe(
|
|
||||||
'green'
|
|
||||||
)
|
|
||||||
|
|
||||||
const modifiedSvc = getNewBlueGreenObject(
|
|
||||||
testObjects.serviceEntityList[0],
|
|
||||||
GREEN_LABEL_VALUE
|
|
||||||
)
|
|
||||||
//@ts-ignore
|
|
||||||
expect(modifiedSvc.metadata.name).toBe('nginx-service-green')
|
|
||||||
//@ts-ignore
|
|
||||||
expect(modifiedSvc.metadata.labels['k8s.deploy.color']).toBe('green')
|
|
||||||
})
|
|
||||||
|
|
||||||
test('correctly makes labeled workloads', () => {
|
|
||||||
const kubectl = new Kubectl('')
|
|
||||||
expect(Kubectl).toBeCalledTimes(1)
|
|
||||||
const cwlResult = createWorkloadsWithLabel(
|
|
||||||
kubectl,
|
|
||||||
testObjects.deploymentEntityList,
|
|
||||||
GREEN_LABEL_VALUE
|
|
||||||
)
|
|
||||||
cwlResult.then((value) => {
|
|
||||||
//@ts-ignore
|
|
||||||
expect(value.newFilePaths[0]).toBe('')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('correctly classifies routed services', () => {
|
|
||||||
expect(
|
|
||||||
isServiceRouted(
|
|
||||||
testObjects.serviceEntityList[0],
|
|
||||||
testObjects.deploymentEntityList
|
|
||||||
)
|
|
||||||
).toBe(true)
|
|
||||||
testObjects.serviceEntityList[0].spec.selector.app = 'fakeapp'
|
|
||||||
expect(
|
|
||||||
isServiceRouted(
|
|
||||||
testObjects.serviceEntityList[0],
|
|
||||||
testObjects.deploymentEntityList
|
|
||||||
)
|
|
||||||
).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('correctly deletes services and workloads according to label', () => {
|
|
||||||
const kubectl = new Kubectl('')
|
|
||||||
jest.spyOn(bgHelper, 'deleteObjects').mockReturnValue({} as Promise<void>)
|
|
||||||
|
|
||||||
let objectsToDelete = deleteWorkloadsAndServicesWithLabel(
|
|
||||||
kubectl,
|
|
||||||
NONE_LABEL_VALUE,
|
|
||||||
testObjects.deploymentEntityList,
|
|
||||||
testObjects.serviceEntityList
|
|
||||||
)
|
|
||||||
objectsToDelete.then((value) => {
|
|
||||||
expect(value).toHaveLength(2)
|
|
||||||
expect(value).toContainEqual
|
|
||||||
;({name: 'nginx-service', kind: 'Service'})
|
|
||||||
expect(value).toContainEqual({
|
|
||||||
name: 'nginx-deployment',
|
|
||||||
kind: 'Deployment'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
objectsToDelete = deleteWorkloadsAndServicesWithLabel(
|
|
||||||
kubectl,
|
|
||||||
GREEN_LABEL_VALUE,
|
|
||||||
testObjects.deploymentEntityList,
|
|
||||||
testObjects.serviceEntityList
|
|
||||||
)
|
|
||||||
objectsToDelete.then((value) => {
|
|
||||||
expect(value).toHaveLength(2)
|
|
||||||
expect(value).toContainEqual({
|
|
||||||
name: 'nginx-service-green',
|
|
||||||
kind: 'Service'
|
|
||||||
})
|
|
||||||
expect(value).toContainEqual({
|
|
||||||
name: 'nginx-deployment-green',
|
|
||||||
kind: 'Deployment'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -112,7 +112,6 @@ export async function deleteWorkloadsWithLabel(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await deleteObjects(kubectl, resourcesToDelete)
|
await deleteObjects(kubectl, resourcesToDelete)
|
||||||
return resourcesToDelete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteWorkloadsAndServicesWithLabel(
|
export async function deleteWorkloadsAndServicesWithLabel(
|
||||||
@@ -144,7 +143,6 @@ export async function deleteWorkloadsAndServicesWithLabel(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await deleteObjects(kubectl, resourcesToDelete)
|
await deleteObjects(kubectl, resourcesToDelete)
|
||||||
return resourcesToDelete
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteObjects(kubectl: Kubectl, deleteList: any[]) {
|
export async function deleteObjects(kubectl: Kubectl, deleteList: any[]) {
|
||||||
@@ -239,6 +237,9 @@ export async function createWorkloadsWithLabel(
|
|||||||
deploymentObjectList.forEach((inputObject) => {
|
deploymentObjectList.forEach((inputObject) => {
|
||||||
// creating deployment with label
|
// creating deployment with label
|
||||||
const newBlueGreenObject = getNewBlueGreenObject(inputObject, nextLabel)
|
const newBlueGreenObject = getNewBlueGreenObject(inputObject, nextLabel)
|
||||||
|
core.debug(
|
||||||
|
'New blue-green object is: ' + JSON.stringify(newBlueGreenObject)
|
||||||
|
)
|
||||||
newObjectsList.push(newBlueGreenObject)
|
newObjectsList.push(newBlueGreenObject)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -279,7 +280,7 @@ export function addBlueGreenLabelsAndAnnotations(
|
|||||||
updateObjectLabels(inputObject, newLabels, false)
|
updateObjectLabels(inputObject, newLabels, false)
|
||||||
updateSelectorLabels(inputObject, newLabels, false)
|
updateSelectorLabels(inputObject, newLabels, false)
|
||||||
|
|
||||||
// updating spec labels if it is not a service
|
// updating spec labels if it is a service
|
||||||
if (!isServiceEntity(inputObject.kind)) {
|
if (!isServiceEntity(inputObject.kind)) {
|
||||||
updateSpecLabels(inputObject, newLabels, false)
|
updateSpecLabels(inputObject, newLabels, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
import {getManifestObjects, GREEN_LABEL_VALUE} from './blueGreenHelper'
|
|
||||||
import {
|
|
||||||
deployBlueGreenIngress,
|
|
||||||
getUpdatedBlueGreenIngress,
|
|
||||||
isIngressRouted,
|
|
||||||
routeBlueGreenIngress
|
|
||||||
} from './ingressBlueGreenHelper'
|
|
||||||
import {Kubectl} from '../../types/kubectl'
|
|
||||||
import * as fileHelper from '../../utilities/fileUtils'
|
|
||||||
|
|
||||||
jest.mock('../../types/kubectl')
|
|
||||||
|
|
||||||
describe('ingress blue green helpers', () => {
|
|
||||||
let testObjects
|
|
||||||
const betaFilepath = ['test/unit/manifests/test-ingress.yml']
|
|
||||||
const ingressFilepath = ['test/unit/manifests/test-ingress-new.yml']
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
//@ts-ignore
|
|
||||||
Kubectl.mockClear()
|
|
||||||
testObjects = getManifestObjects(ingressFilepath)
|
|
||||||
jest
|
|
||||||
.spyOn(fileHelper, 'writeObjectsToFile')
|
|
||||||
.mockImplementationOnce(() => [''])
|
|
||||||
})
|
|
||||||
|
|
||||||
test('it should correctly classify ingresses', () => {
|
|
||||||
expect(
|
|
||||||
isIngressRouted(
|
|
||||||
testObjects.ingressEntityList[0],
|
|
||||||
testObjects.serviceNameMap
|
|
||||||
)
|
|
||||||
).toBe(true)
|
|
||||||
testObjects.ingressEntityList[0].spec.rules[0].http.paths = {}
|
|
||||||
expect(
|
|
||||||
isIngressRouted(
|
|
||||||
testObjects.ingressEntityList[0],
|
|
||||||
testObjects.serviceNameMap
|
|
||||||
)
|
|
||||||
).toBe(false)
|
|
||||||
|
|
||||||
expect(
|
|
||||||
isIngressRouted(
|
|
||||||
getManifestObjects(betaFilepath).ingressEntityList[0],
|
|
||||||
testObjects.serviceNameMap
|
|
||||||
)
|
|
||||||
).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('it should correctly update ingresses', () => {
|
|
||||||
const updatedIng = getUpdatedBlueGreenIngress(
|
|
||||||
testObjects.ingressEntityList[0],
|
|
||||||
testObjects.serviceNameMap,
|
|
||||||
GREEN_LABEL_VALUE
|
|
||||||
)
|
|
||||||
//@ts-ignore
|
|
||||||
expect(updatedIng.metadata.labels['k8s.deploy.color']).toBe('green')
|
|
||||||
//@ts-ignore
|
|
||||||
expect(updatedIng.spec.rules[0].http.paths[0].backend.service.name).toBe(
|
|
||||||
'nginx-service-green'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('correctly prepares blue/green ingresses for deployment', () => {
|
|
||||||
const kc = new Kubectl('')
|
|
||||||
const generatedObjects = routeBlueGreenIngress(
|
|
||||||
kc,
|
|
||||||
GREEN_LABEL_VALUE,
|
|
||||||
testObjects.serviceNameMap,
|
|
||||||
testObjects.ingressEntityList
|
|
||||||
)
|
|
||||||
generatedObjects.then((value) => {
|
|
||||||
expect(value).toHaveLength(1)
|
|
||||||
//@ts-ignore
|
|
||||||
expect(value[0].metadata.name).toBe('nginx-ingress')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
test('correctly deploys services', () => {
|
|
||||||
const kc = new Kubectl('')
|
|
||||||
const result = deployBlueGreenIngress(kc, ingressFilepath)
|
|
||||||
|
|
||||||
result.then((value) => {
|
|
||||||
const nol = value.newObjectsList
|
|
||||||
//@ts-ignore
|
|
||||||
expect(nol[0].metadata.name).toBe('nginx-service-green')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
} from './blueGreenHelper'
|
} from './blueGreenHelper'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
const BACKEND = 'backend'
|
const BACKEND = 'BACKEND'
|
||||||
|
|
||||||
export async function deployBlueGreenIngress(
|
export async function deployBlueGreenIngress(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
@@ -24,12 +24,13 @@ export async function deployBlueGreenIngress(
|
|||||||
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
||||||
|
|
||||||
// create deployments with green label value
|
// create deployments with green label value
|
||||||
const workloadDeployment = await createWorkloadsWithLabel(
|
const result = createWorkloadsWithLabel(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifestObjects.deploymentEntityList,
|
manifestObjects.deploymentEntityList,
|
||||||
GREEN_LABEL_VALUE
|
GREEN_LABEL_VALUE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// create new services and other objects
|
||||||
let newObjectsList = []
|
let newObjectsList = []
|
||||||
manifestObjects.serviceEntityList.forEach((inputObject) => {
|
manifestObjects.serviceEntityList.forEach((inputObject) => {
|
||||||
const newBlueGreenObject = getNewBlueGreenObject(
|
const newBlueGreenObject = getNewBlueGreenObject(
|
||||||
@@ -45,12 +46,7 @@ export async function deployBlueGreenIngress(
|
|||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
||||||
await kubectl.apply(manifestFiles)
|
await kubectl.apply(manifestFiles)
|
||||||
|
|
||||||
core.debug(
|
return result
|
||||||
'new objects after processing services and other objects: \n' +
|
|
||||||
JSON.stringify(newObjectsList)
|
|
||||||
)
|
|
||||||
|
|
||||||
return {workloadDeployment, newObjectsList}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function promoteBlueGreenIngress(
|
export async function promoteBlueGreenIngress(
|
||||||
@@ -58,13 +54,14 @@ export async function promoteBlueGreenIngress(
|
|||||||
manifestObjects
|
manifestObjects
|
||||||
) {
|
) {
|
||||||
//checking if anything to promote
|
//checking if anything to promote
|
||||||
const {areValid, invalidIngresses} = validateIngresses(
|
if (
|
||||||
kubectl,
|
!validateIngressesState(
|
||||||
manifestObjects.ingressEntityList,
|
kubectl,
|
||||||
manifestObjects.serviceNameMap
|
manifestObjects.ingressEntityList,
|
||||||
)
|
manifestObjects.serviceNameMap
|
||||||
if (!areValid) {
|
)
|
||||||
throw 'Ingresses are not in promote state' + invalidIngresses.toString()
|
) {
|
||||||
|
throw 'Ingress not in promote state'
|
||||||
}
|
}
|
||||||
|
|
||||||
// create stable deployments with new configuration
|
// create stable deployments with new configuration
|
||||||
@@ -141,18 +138,17 @@ export async function routeBlueGreenIngress(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core.debug('New objects: ' + JSON.stringify(newObjectsList))
|
||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
||||||
await kubectl.apply(manifestFiles)
|
await kubectl.apply(manifestFiles)
|
||||||
return newObjectsList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateIngresses(
|
export function validateIngressesState(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
ingressEntityList: any[],
|
ingressEntityList: any[],
|
||||||
serviceNameMap: Map<string, string>
|
serviceNameMap: Map<string, string>
|
||||||
): {areValid: boolean; invalidIngresses: string[]} {
|
): boolean {
|
||||||
let areValid: boolean = true
|
let areIngressesTargetingNewServices: boolean = true
|
||||||
const invalidIngresses = []
|
|
||||||
ingressEntityList.forEach(async (inputObject) => {
|
ingressEntityList.forEach(async (inputObject) => {
|
||||||
if (isIngressRouted(inputObject, serviceNameMap)) {
|
if (isIngressRouted(inputObject, serviceNameMap)) {
|
||||||
//querying existing ingress
|
//querying existing ingress
|
||||||
@@ -162,32 +158,33 @@ export function validateIngresses(
|
|||||||
inputObject.metadata.name
|
inputObject.metadata.name
|
||||||
)
|
)
|
||||||
|
|
||||||
let isValid =
|
if (!!existingIngress) {
|
||||||
!!existingIngress &&
|
const currentLabel: string =
|
||||||
existingIngress?.metadata?.labels[BLUE_GREEN_VERSION_LABEL] ===
|
existingIngress?.metadata?.labels[BLUE_GREEN_VERSION_LABEL]
|
||||||
GREEN_LABEL_VALUE
|
|
||||||
if (!isValid) {
|
// if not green label, then wrong configuration
|
||||||
invalidIngresses.push(inputObject.metadata.name)
|
if (currentLabel != GREEN_LABEL_VALUE)
|
||||||
|
areIngressesTargetingNewServices = false
|
||||||
|
} else {
|
||||||
|
// no ingress at all, so nothing to promote
|
||||||
|
areIngressesTargetingNewServices = false
|
||||||
}
|
}
|
||||||
// to be valid, ingress should exist and should be green
|
|
||||||
areValid = areValid && isValid
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return {areValid, invalidIngresses}
|
return areIngressesTargetingNewServices
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isIngressRouted(
|
function isIngressRouted(
|
||||||
ingressObject: any,
|
ingressObject: any,
|
||||||
serviceNameMap: Map<string, string>
|
serviceNameMap: Map<string, string>
|
||||||
): boolean {
|
): boolean {
|
||||||
let isIngressRouted: boolean = false
|
let isIngressRouted: boolean = false
|
||||||
// check if ingress targets a service in the given manifests
|
// check if ingress targets a service in the given manifests
|
||||||
JSON.parse(JSON.stringify(ingressObject), (key, value) => {
|
JSON.parse(JSON.stringify(ingressObject), (key, value) => {
|
||||||
isIngressRouted =
|
if (key === 'serviceName' && serviceNameMap.has(value)) {
|
||||||
isIngressRouted || (key === 'service' && value.hasOwnProperty('name'))
|
isIngressRouted = true
|
||||||
isIngressRouted =
|
}
|
||||||
isIngressRouted || (key === 'serviceName' && serviceNameMap.has(value))
|
|
||||||
|
|
||||||
return value
|
return value
|
||||||
})
|
})
|
||||||
@@ -209,18 +206,15 @@ export function getUpdatedBlueGreenIngress(
|
|||||||
addBlueGreenLabelsAndAnnotations(newObject, type)
|
addBlueGreenLabelsAndAnnotations(newObject, type)
|
||||||
|
|
||||||
// update ingress labels
|
// update ingress labels
|
||||||
if (inputObject.apiVersion === 'networking.k8s.io/v1beta1') {
|
|
||||||
return updateIngressBackendBetaV1(newObject, serviceNameMap)
|
|
||||||
}
|
|
||||||
return updateIngressBackend(newObject, serviceNameMap)
|
return updateIngressBackend(newObject, serviceNameMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateIngressBackendBetaV1(
|
export function updateIngressBackend(
|
||||||
inputObject: any,
|
inputObject: any,
|
||||||
serviceNameMap: Map<string, string>
|
serviceNameMap: Map<string, string>
|
||||||
): any {
|
): any {
|
||||||
inputObject = JSON.parse(JSON.stringify(inputObject), (key, value) => {
|
inputObject = JSON.parse(JSON.stringify(inputObject), (key, value) => {
|
||||||
if (key.toLowerCase() === BACKEND) {
|
if (key.toUpperCase() === BACKEND) {
|
||||||
const {serviceName} = value
|
const {serviceName} = value
|
||||||
if (serviceNameMap.has(serviceName)) {
|
if (serviceNameMap.has(serviceName)) {
|
||||||
// update service name with corresponding bluegreen name only if service is provied in given manifests
|
// update service name with corresponding bluegreen name only if service is provied in given manifests
|
||||||
@@ -233,20 +227,3 @@ export function updateIngressBackendBetaV1(
|
|||||||
|
|
||||||
return inputObject
|
return inputObject
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateIngressBackend(
|
|
||||||
inputObject: any,
|
|
||||||
serviceNameMap: Map<string, string>
|
|
||||||
): any {
|
|
||||||
inputObject = JSON.parse(JSON.stringify(inputObject), (key, value) => {
|
|
||||||
if (
|
|
||||||
key.toLowerCase() === BACKEND &&
|
|
||||||
serviceNameMap.has(value?.service?.name)
|
|
||||||
) {
|
|
||||||
value.service.name = serviceNameMap.get(value.service.name)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
})
|
|
||||||
|
|
||||||
return inputObject
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,21 +19,21 @@ export async function deployBlueGreenService(
|
|||||||
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
||||||
|
|
||||||
// create deployments with green label value
|
// create deployments with green label value
|
||||||
const workloadDeployment = await createWorkloadsWithLabel(
|
const result = await createWorkloadsWithLabel(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifestObjects.deploymentEntityList,
|
manifestObjects.deploymentEntityList,
|
||||||
GREEN_LABEL_VALUE
|
GREEN_LABEL_VALUE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// create other non deployment and non service entities
|
||||||
const newObjectsList = manifestObjects.otherObjects
|
const newObjectsList = manifestObjects.otherObjects
|
||||||
.concat(manifestObjects.ingressEntityList)
|
.concat(manifestObjects.ingressEntityList)
|
||||||
.concat(manifestObjects.unroutedServiceEntityList)
|
.concat(manifestObjects.unroutedServiceEntityList)
|
||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
||||||
|
|
||||||
if (manifestFiles.length > 0) await kubectl.apply(manifestFiles)
|
if (manifestFiles.length > 0) await kubectl.apply(manifestFiles)
|
||||||
|
|
||||||
// returning deployment details to check for rollout stability
|
// returning deployment details to check for rollout stability
|
||||||
return {workloadDeployment, newObjectsList}
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function promoteBlueGreenService(
|
export async function promoteBlueGreenService(
|
||||||
@@ -76,6 +76,7 @@ export async function rejectBlueGreenService(
|
|||||||
manifestObjects.deploymentEntityList
|
manifestObjects.deploymentEntityList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function routeBlueGreenService(
|
export async function routeBlueGreenService(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
nextLabel: string,
|
nextLabel: string,
|
||||||
|
|||||||
@@ -41,13 +41,11 @@ export async function deployBlueGreenSMI(
|
|||||||
await setupSMI(kubectl, manifestObjects.serviceEntityList, annotations)
|
await setupSMI(kubectl, manifestObjects.serviceEntityList, annotations)
|
||||||
|
|
||||||
// create new deloyments
|
// create new deloyments
|
||||||
const workloadDeployment = await createWorkloadsWithLabel(
|
return await createWorkloadsWithLabel(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifestObjects.deploymentEntityList,
|
manifestObjects.deploymentEntityList,
|
||||||
GREEN_LABEL_VALUE
|
GREEN_LABEL_VALUE
|
||||||
)
|
)
|
||||||
|
|
||||||
return {workloadDeployment, newObjectsList}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function promoteBlueGreenSMI(kubectl: Kubectl, manifestObjects) {
|
export async function promoteBlueGreenSMI(kubectl: Kubectl, manifestObjects) {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export async function deployManifests(
|
|||||||
core.getInput('route-method', {required: true})
|
core.getInput('route-method', {required: true})
|
||||||
)
|
)
|
||||||
|
|
||||||
const {workloadDeployment, newObjectsList} = await Promise.resolve(
|
const {result, newFilePaths} = await Promise.resolve(
|
||||||
(routeStrategy == RouteStrategy.INGRESS &&
|
(routeStrategy == RouteStrategy.INGRESS &&
|
||||||
deployBlueGreenIngress(kubectl, files)) ||
|
deployBlueGreenIngress(kubectl, files)) ||
|
||||||
(routeStrategy == RouteStrategy.SMI &&
|
(routeStrategy == RouteStrategy.SMI &&
|
||||||
@@ -68,8 +68,8 @@ export async function deployManifests(
|
|||||||
deployBlueGreenService(kubectl, files)
|
deployBlueGreenService(kubectl, files)
|
||||||
)
|
)
|
||||||
|
|
||||||
checkForErrors([workloadDeployment.result])
|
checkForErrors([result])
|
||||||
return workloadDeployment.newFilePaths
|
return newFilePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
case DeploymentStrategy.BASIC: {
|
case DeploymentStrategy.BASIC: {
|
||||||
@@ -143,7 +143,7 @@ export async function annotateAndLabelResources(
|
|||||||
const workflowFilePath = await getWorkflowFilePath(githubToken)
|
const workflowFilePath = await getWorkflowFilePath(githubToken)
|
||||||
|
|
||||||
const deploymentConfig = await getDeploymentConfig()
|
const deploymentConfig = await getDeploymentConfig()
|
||||||
const annotationKeyLabel = getWorkflowAnnotationKeyLabel()
|
const annotationKeyLabel = getWorkflowAnnotationKeyLabel(workflowFilePath)
|
||||||
|
|
||||||
await annotateResources(
|
await annotateResources(
|
||||||
files,
|
files,
|
||||||
|
|||||||
+5
-12
@@ -10,25 +10,18 @@ export interface Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Kubectl {
|
export class Kubectl {
|
||||||
protected readonly kubectlPath: string
|
private readonly kubectlPath: string
|
||||||
protected readonly namespace: string
|
private readonly namespace: string
|
||||||
protected readonly ignoreSSLErrors: boolean
|
private readonly ignoreSSLErrors: boolean
|
||||||
protected readonly resourceGroup: string
|
|
||||||
protected readonly name: string
|
|
||||||
protected isPrivateCluster: boolean
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kubectlPath: string,
|
kubectlPath: string,
|
||||||
namespace: string = 'default',
|
namespace: string = 'default',
|
||||||
ignoreSSLErrors: boolean = false,
|
ignoreSSLErrors: boolean = false
|
||||||
resourceGroup: string = '',
|
|
||||||
name: string = ''
|
|
||||||
) {
|
) {
|
||||||
this.kubectlPath = kubectlPath
|
this.kubectlPath = kubectlPath
|
||||||
this.ignoreSSLErrors = !!ignoreSSLErrors
|
this.ignoreSSLErrors = !!ignoreSSLErrors
|
||||||
this.namespace = namespace
|
this.namespace = namespace
|
||||||
this.resourceGroup = resourceGroup
|
|
||||||
this.name = name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async apply(
|
public async apply(
|
||||||
@@ -162,7 +155,7 @@ export class Kubectl {
|
|||||||
return this.execute(['delete', ...args])
|
return this.execute(['delete', ...args])
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async execute(args: string[], silent: boolean = false) {
|
private async execute(args: string[], silent: boolean = false) {
|
||||||
if (this.ignoreSSLErrors) {
|
if (this.ignoreSSLErrors) {
|
||||||
args.push('--insecure-skip-tls-verify')
|
args.push('--insecure-skip-tls-verify')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
import {Kubectl} from './kubectl'
|
|
||||||
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
|
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as os from 'os'
|
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as path from 'path'
|
|
||||||
|
|
||||||
export class PrivateKubectl extends Kubectl {
|
|
||||||
protected async execute(args: string[], silent: boolean = false) {
|
|
||||||
args.unshift('kubectl')
|
|
||||||
let kubectlCmd = args.join(' ')
|
|
||||||
let addFileFlag = false
|
|
||||||
let eo = <ExecOptions>{silent}
|
|
||||||
|
|
||||||
if (this.containsFilenames(kubectlCmd)) {
|
|
||||||
// For private clusters, files will referenced solely by their basename
|
|
||||||
kubectlCmd = this.replaceFilnamesWithBasenames(kubectlCmd)
|
|
||||||
addFileFlag = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const privateClusterArgs = [
|
|
||||||
'aks',
|
|
||||||
'command',
|
|
||||||
'invoke',
|
|
||||||
'--resource-group',
|
|
||||||
this.resourceGroup,
|
|
||||||
'--name',
|
|
||||||
this.name,
|
|
||||||
'--command',
|
|
||||||
kubectlCmd
|
|
||||||
]
|
|
||||||
|
|
||||||
if (addFileFlag) {
|
|
||||||
const filenames = this.extractFilesnames(kubectlCmd).split(' ')
|
|
||||||
|
|
||||||
const tempDirectory =
|
|
||||||
process.env['runner.tempDirectory'] || os.tmpdir() + '/manifests'
|
|
||||||
eo.cwd = tempDirectory
|
|
||||||
privateClusterArgs.push(...['--file', '.'])
|
|
||||||
|
|
||||||
let filenamesArr = filenames[0].split(',')
|
|
||||||
for (let index = 0; index < filenamesArr.length; index++) {
|
|
||||||
const file = filenamesArr[index]
|
|
||||||
|
|
||||||
if (!file) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
this.moveFileToTempManifestDir(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
core.debug(
|
|
||||||
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
|
|
||||||
)
|
|
||||||
return await getExecOutput('az', privateClusterArgs, eo)
|
|
||||||
}
|
|
||||||
|
|
||||||
private replaceFilnamesWithBasenames(kubectlCmd: string) {
|
|
||||||
let exFilenames = this.extractFilesnames(kubectlCmd)
|
|
||||||
let filenames = exFilenames.split(' ')
|
|
||||||
let filenamesArr = filenames[0].split(',')
|
|
||||||
|
|
||||||
for (let index = 0; index < filenamesArr.length; index++) {
|
|
||||||
filenamesArr[index] = path.basename(filenamesArr[index])
|
|
||||||
}
|
|
||||||
|
|
||||||
let baseFilenames = filenamesArr.join()
|
|
||||||
|
|
||||||
let result = kubectlCmd.replace(exFilenames, baseFilenames)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
public extractFilesnames(strToParse: string) {
|
|
||||||
let start = strToParse.indexOf('-filename')
|
|
||||||
let offset = 7
|
|
||||||
|
|
||||||
if (start == -1) {
|
|
||||||
start = strToParse.indexOf('-f')
|
|
||||||
|
|
||||||
if (start == -1) {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
private containsFilenames(str: string) {
|
|
||||||
return str.includes('-f ') || str.includes('filename ')
|
|
||||||
}
|
|
||||||
|
|
||||||
private createTempManifestsDirectory() {
|
|
||||||
const manifestsDir = '/tmp/manifests'
|
|
||||||
if (!fs.existsSync('/tmp/manifests')) {
|
|
||||||
fs.mkdirSync('/tmp/manifests', {recursive: true})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private moveFileToTempManifestDir(file: string) {
|
|
||||||
this.createTempManifestsDirectory()
|
|
||||||
if (!fs.existsSync('/tmp/' + file)) {
|
|
||||||
core.debug(
|
|
||||||
'/tmp/' +
|
|
||||||
file +
|
|
||||||
' does not exist, and therefore cannot be moved to the manifest directory'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.copyFile('/tmp/' + file, '/tmp/manifests/' + file, function (err) {
|
|
||||||
if (err) {
|
|
||||||
core.debug(
|
|
||||||
'Could not rename ' +
|
|
||||||
'/tmp/' +
|
|
||||||
file +
|
|
||||||
' to ' +
|
|
||||||
'/tmp/manifests/' +
|
|
||||||
file +
|
|
||||||
' ERROR: ' +
|
|
||||||
err
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
core.debug(
|
|
||||||
"Successfully moved file '" +
|
|
||||||
file +
|
|
||||||
"' from /tmp to /tmp/manifest directory"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,12 +12,11 @@ describe('File utils', () => {
|
|||||||
'test/unit/manifests/manifest_test_dir/another_layer/deep-service.yaml',
|
'test/unit/manifests/manifest_test_dir/another_layer/deep-service.yaml',
|
||||||
'test/unit/manifests/manifest_test_dir/nested-test-service.yaml',
|
'test/unit/manifests/manifest_test_dir/nested-test-service.yaml',
|
||||||
'test/unit/manifests/test-ingress.yml',
|
'test/unit/manifests/test-ingress.yml',
|
||||||
'test/unit/manifests/test-ingress-new.yml',
|
|
||||||
'test/unit/manifests/test-service.yml'
|
'test/unit/manifests/test-service.yml'
|
||||||
]
|
]
|
||||||
|
|
||||||
// is there a more efficient way to test equality w random order?
|
// is there a more efficient way to test equality w random order?
|
||||||
expect(testSearch).toHaveLength(6)
|
expect(testSearch).toHaveLength(5)
|
||||||
expectedManifests.forEach((fileName) => {
|
expectedManifests.forEach((fileName) => {
|
||||||
expect(testSearch).toContain(fileName)
|
expect(testSearch).toContain(fileName)
|
||||||
})
|
})
|
||||||
@@ -54,7 +53,7 @@ describe('File utils', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
getFilesFromDirectories([outerPath, fileAtOuter, innerPath])
|
getFilesFromDirectories([outerPath, fileAtOuter, innerPath])
|
||||||
).toHaveLength(6)
|
).toHaveLength(5)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function writeManifestToFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getManifestFileName(kind: string, name: string) {
|
function getManifestFileName(kind: string, name: string) {
|
||||||
const filePath = `${kind}_${name}_${getCurrentTime().toString()}`
|
const filePath = `${kind}_${name}_ ${getCurrentTime().toString()}`
|
||||||
const tempDirectory = getTempDirectory()
|
const tempDirectory = getTempDirectory()
|
||||||
return path.join(tempDirectory, path.basename(filePath))
|
return path.join(tempDirectory, path.basename(filePath))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,24 @@
|
|||||||
import {cleanLabel} from '../utilities/workflowAnnotationUtils'
|
import {
|
||||||
|
cleanLabel,
|
||||||
|
prefixObjectKeys
|
||||||
|
} from '../utilities/workflowAnnotationUtils'
|
||||||
|
|
||||||
describe('WorkflowAnnotationUtils', () => {
|
describe('WorkflowAnnotationUtils', () => {
|
||||||
|
describe('prefixObjectKeys', () => {
|
||||||
|
it('should prefix an object with a given prefix', () => {
|
||||||
|
const obj = {
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux'
|
||||||
|
}
|
||||||
|
const prefix = 'prefix.'
|
||||||
|
const expected = {
|
||||||
|
'prefix.foo': 'bar',
|
||||||
|
'prefix.baz': 'qux'
|
||||||
|
}
|
||||||
|
expect(prefixObjectKeys(obj, prefix)).toEqual(expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('cleanLabel', () => {
|
describe('cleanLabel', () => {
|
||||||
it('should clean label', () => {
|
it('should clean label', () => {
|
||||||
const alreadyClean = 'alreadyClean'
|
const alreadyClean = 'alreadyClean'
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import {DeploymentConfig} from '../types/deploymentConfig'
|
import {DeploymentConfig} from '../types/deploymentConfig'
|
||||||
|
|
||||||
const ANNOTATION_PREFIX = 'actions.github.com'
|
const ANNOTATION_PREFIX = 'actions.github.com/'
|
||||||
|
|
||||||
|
export function prefixObjectKeys(obj: any, prefix: string): any {
|
||||||
|
return Object.keys(obj).reduce((newObj, key) => {
|
||||||
|
newObj[prefix + key] = obj[key]
|
||||||
|
return newObj
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
export function getWorkflowAnnotations(
|
export function getWorkflowAnnotations(
|
||||||
lastSuccessRunSha: string,
|
lastSuccessRunSha: string,
|
||||||
@@ -24,11 +31,21 @@ export function getWorkflowAnnotations(
|
|||||||
helmChartPaths: deploymentConfig.helmChartFilePaths,
|
helmChartPaths: deploymentConfig.helmChartFilePaths,
|
||||||
provider: 'GitHub'
|
provider: 'GitHub'
|
||||||
}
|
}
|
||||||
return JSON.stringify(annotationObject)
|
const prefixedAnnotationObject = prefixObjectKeys(
|
||||||
|
annotationObject,
|
||||||
|
ANNOTATION_PREFIX
|
||||||
|
)
|
||||||
|
return JSON.stringify(prefixedAnnotationObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorkflowAnnotationKeyLabel(): string {
|
export function getWorkflowAnnotationKeyLabel(
|
||||||
return `${ANNOTATION_PREFIX}/k8s-deploy`
|
workflowFilePath: string
|
||||||
|
): string {
|
||||||
|
const hashKey = require('crypto')
|
||||||
|
.createHash('MD5')
|
||||||
|
.update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
|
||||||
|
.digest('hex')
|
||||||
|
return `githubWorkflow_${hashKey}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: nginx-deployment
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: nginx
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: nginx
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: nginx
|
|
||||||
image: nginx:1.14.2
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: nginx-service
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: nginx
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 80
|
|
||||||
targetPort: 80
|
|
||||||
---
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: nginx-ingress
|
|
||||||
annotations:
|
|
||||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- http:
|
|
||||||
paths:
|
|
||||||
- path: /testpath
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: nginx-service
|
|
||||||
port:
|
|
||||||
number: 80
|
|
||||||
Reference in New Issue
Block a user