Blue green strategy - Refined some details. (#51)

* Refined some details.

* addressed comments

* updates readme

* Readme cleanup (#1)

* Small updates to readme

* added identified service terminology

* Adressed PR comments

* Added workflow to trigger L2 tests

* Renamed workflow file name

* Trigger integration tests through script and disable post check-in trigger.

Co-authored-by: Anirudh Raghunath <46741940+anraghun@users.noreply.github.com>
Co-authored-by: ajinkya599 <11447401+ajinkya599@users.noreply.github.com>
This commit is contained in:
Sundar 2020-09-17 12:22:14 +05:30 коммит произвёл GitHub
Родитель d394c2bba2
Коммит 29552c24a9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 350 добавлений и 327 удалений

32
.github/workflows/TriggerIntegrationTests.sh поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
token=$1
commit=$2
repository=$3
prNumber=$4
frombranch=$5
tobranch=$6
getPayLoad() {
cat <<EOF
{
"event_type": "K8sDeployActionPR",
"client_payload":
{
"action": "CreateSecret",
"commit": "$commit",
"repository": "$repository",
"prNumber": "$prNumber",
"tobranch": "$tobranch",
"frombranch": "$frombranch"
}
}
EOF
}
response=$(curl -X POST -H "Authorization: token $token" https://api.github.com/repos/Azure/azure-actions-integration-tests/dispatches --data "$(getPayLoad)")
if [ "$response" == "" ]; then
echo "Integration tests triggered successfully"
else
echo "Triggering integration tests failed with: '$response'"
exit 1
fi

23
.github/workflows/integration-tests.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
name: "Trigger Integration tests"
on:
# push:
# branches:
# - master
# - 'releases/*'
pull_request:
branches:
- master
- 'releases/*'
jobs:
trigger-integration-tests:
name: Trigger Integration tests
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
with:
path: IntegrationTests
- name: Trigger Test run
run: |
bash ./IntegrationTests/.github/workflows/TriggerIntegrationTests.sh ${{ secrets.L2_REPO_TOKEN }} ${{ github.event.pull_request.head.sha }} ${{ github.repository }} ${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.ref }} ${{ github.event.pull_request.base.ref }}

Просмотреть файл

@ -1,3 +1,4 @@
# Deploy manifests action for Kubernetes
This action can be used to deploy manifests to Kubernetes clusters.
@ -16,9 +17,18 @@ Following are the key capabilities of this action:
- **Secret handling**: The secret names specified as inputs in the action are used to augment the input manifest files with imagePullSecrets values before deploying to the cluster. Also, checkout the [Azure/k8s-create-secret](https://github.com/Azure/k8s-create-secret) action for creation of generic or docker-registry secrets in the cluster.
- **Deployment strategy** Choosing canary strategy with this action leads to creation of workloads suffixed with '-baseline' and '-canary'. There are two methods of traffic splitting supported in the action:
- **Service Mesh Interface**: Service Mesh Interface abstraction allows for plug-and-play configuration with service mesh providers such as Linkerd and Istio. Meanwhile, this action takes away the hard work of mapping SMI's TrafficSplit objects to the stable, baseline and canary services during the lifecycle of the deployment strategy. Service mesh based canary deployments using this action are more accurate as service mesh providers enable granular percentage traffic split (via service registry and sidecar containers injected into pods alongside application containers).
- **Only Kubernetes (no service mesh)**: In the absence of service mesh, while it may not be possible to achieve exact percentage split at the request level, it is still possible to perform canary deployments by deploying -baseline and -canary workload variants next to the stable variant. The service routes requests to pods of all three workload variants as the selector-label constraints are met (KubernetesManifest will honor these when creating -baseline and -canary variants). This achieves the intended effect of routing only a portion of total requests to the canary.
- **Deployment strategy** The action supports canary and blue-green deployment strategies:
- **Canary strategy**: Choosing canary strategy with this action leads to creation of workloads suffixed with '-baseline' and '-canary'. There are two methods of traffic splitting supported in the action:
- **Service Mesh Interface**: Service Mesh Interface abstraction allows for plug-and-play configuration with service mesh providers such as Linkerd and Istio. Meanwhile, this action takes away the hard work of mapping SMI's TrafficSplit objects to the stable, baseline and canary services during the lifecycle of the deployment strategy. Service mesh based canary deployments using this action are more accurate as service mesh providers enable granular percentage traffic split (via service registry and sidecar containers injected into pods alongside application containers).
- **Only Kubernetes (no service mesh)**: In the absence of service mesh, while it may not be possible to achieve exact percentage split at the request level, it is still possible to perform canary deployments by deploying -baseline and -canary workload variants next to the stable variant. The service routes requests to pods of all three workload variants as the selector-label constraints are met (KubernetesManifest will honor these when creating -baseline and -canary variants). This achieves the intended effect of routing only a portion of total requests to the canary.
- **Blue-Green strategy**: Choosing blue-green strategy with this action leads to creation of workloads suffixed with '-green'. There are three route-methods supported in the action:
*Terminolgy: An **identified** service is one that is supplied as part of the input manifest(s) and targets a workload in the supplied manifest(s).
- **Service route-method**: **Identified** services are configured to target the green deployments.
- **Ingress route-method**: Along with deployments, new services are created with '-green' suffix (for **identified** services), and the ingresses are in turn updated to target the new services.
- **SMI route-method**: A new [TrafficSplit](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md) object is created for each **identified** service. The TrafficSplit object is updated to target the new deployments. **Note** that this works only if SMI is set up in the cluster.
Traffic is routed to the new workloads only after the time provided as `version-switch-buffer` input has passed. `promote` action creates workloads and services with new configurations but without any suffix. `reject` action routes traffic back to the old workloads and deletes the '-green' workloads.
## Action inputs
@ -49,11 +59,11 @@ Following are the key capabilities of this action:
</tr>
<tr>
<td><code>strategy</code><br/>Strategy</td>
<td>(Optional) Deployment strategy to be used while applying manifest files on the cluster. Acceptable values: none/canary. none - No deployment strategy is used when deploying. canary - Canary deployment strategy is used when deploying to the cluster</td>
<td>(Optional) Deployment strategy to be used while applying manifest files on the cluster. Acceptable values: none/canary/blue-green. none - No deployment strategy is used when deploying. canary - Canary deployment strategy is used when deploying to the cluster. blue-green - Blue-Green deployment strategy is used when deploying to cluster.</td>
</tr>
<tr>
<td><code>traffic-split-method</code><br/>Traffic split method</td>
<td>(Optional) Acceptable values: pod/smi; Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/deislabs/smi-spec/blob/master/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. So the percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
<td>(Optional) Acceptable values: pod/smi; Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. So the percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
</tr>
<tr>
<td><code>percentage</code><br/>Percentage</td>
@ -62,10 +72,21 @@ Following are the key capabilities of this action:
<tr>
<td><code>baseline-and-canary-replicas</code><br/>Baseline and canary replicas</td>
<td>(Optional; Relevant only if trafficSplitMethod == smi) When trafficSplitMethod == smi, as percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action - <br>&nbsp;&nbsp;&nbsp;&nbsp;strategy: canary<br>&nbsp;&nbsp;&nbsp;&nbsp;trafficSplitMethod: smi<br>&nbsp;&nbsp;&nbsp;&nbsp;percentage: 20<br>&nbsp;&nbsp;&nbsp;&nbsp;baselineAndCanaryReplicas: 1<br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
</tr>
<tr>
<td><code>route-method</code><br/>Route Method</td>
<td>(Optional; Relevant only if strategy==blue-green) Default value: service. Acceptable values: service/ingress/smi. Traffic is routed based on this input.
<br>Service: Service selector labels are updated to target '-green' workloads.
<br>Ingress: Ingress backends are updated to target the new '-green' services which in turn target '-green' deployments.
<br>SMI: A <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> object is created for each required service to route traffic to new workloads.</td>
</tr>
<tr>
<td><code>version-switch-buffer</code><br/>Version Switch Buffer</td>
<td>(Optional; Relevant only if strategy==blue-green and action == deploy) Default value: 0. Acceptable values: 1-300. Waits for the given input in minutes before routing traffic to '-green' workloads.</td>
</tr>
<tr>
<td><code>action</code><br/>Action</td>
<td>(Required) Default value: deploy. Acceptable values: deploy/promote/reject. Promote or reject actions are used to promote or reject canary deployments. Sample YAML snippets are provided below for guidance on how to use the same.</td>
<td>(Required) Default value: deploy. Acceptable values: deploy/promote/reject. Promote or reject actions are used to promote or reject canary/blue-green deployments. Sample YAML snippets are provided below for guidance on how to use the same.</td>
</tr>
<tr>
<td><code>kubectl-version</code><br/>Kubectl version</td>
@ -112,7 +133,7 @@ Following are the key capabilities of this action:
percentage: 20
```
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
### To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
```yaml
- uses: Azure/k8s-deploy@v1
@ -147,9 +168,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
percentage: 20
baseline-and-canary-replicas: 1
```
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
### To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
```yaml
- uses: Azure/k8s-deploy@v1
with:
@ -165,6 +184,43 @@ To promote/reject the canary created by the above snippet, the following YAML sn
traffic-split-method: smi
action: reject # substitute reject if you want to reject
```
### Deployment Strategies - Blue-Green deployment with different route methods
```yaml
- uses: Azure/k8s-deploy@v1
with:
namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
imagepullsecrets: |
image-pull-secret1
image-pull-secret2
manifests: |
deployment.yaml
service.yaml
ingress.yml
strategy: blue-green
route-method: ingress # substitute with service/smi as per need
version-switch-buffer: 15
```
### **To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:**
```yaml
- uses: Azure/k8s-deploy@v1
with:
namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
imagepullsecrets: |
image-pull-secret1
image-pull-secret2
manifests: |
deployment.yaml
service.yaml
ingress-yml
strategy: blue-green
strategy: ingress # should be the same as the value when action was deploy
action: promote # substitute reject if you want to reject
```
## End to end workflows

Просмотреть файл

@ -131,7 +131,6 @@ test("blueGreenReject - routes servcies to old deployment and deletes new deploy
expect(kubeCtl.delete).toBeCalledWith(["Deployment", "testapp-green"]);
expect(readFileSpy).toBeCalledWith("manifests/bg.yaml");
expect(fileHelperMock.writeObjectsToFile).toBeCalled();
expect(kubeCtl.getResource).toBeCalledWith("Deployment", "testapp");
});
test("blueGreenReject - deletes services if old deployment does not exist", () => {
@ -149,10 +148,8 @@ test("blueGreenReject - deletes services if old deployment does not exist", () =
//Invoke and assert
expect(blueGreenHelperService.rejectBlueGreenService(kubeCtl, ['manifests/bg.yaml'])).toMatchObject({});
expect(kubeCtl.delete).toBeCalledWith(["Deployment", "testapp-green"]);
expect(kubeCtl.delete).toBeCalledWith(["Service", "testservice"]);
expect(readFileSpy).toBeCalledWith("manifests/bg.yaml");
expect(fileHelperMock.writeObjectsToFile).toBeCalled();
expect(kubeCtl.getResource).toBeCalledWith("Deployment", "testapp");
});
test("isIngressRoute() - returns true if route-method is ingress", () => {
@ -365,7 +362,6 @@ test("blueGreenRejectSMI - routes servcies to old deployment and deletes new dep
expect(kubeCtl.delete).toBeCalledWith(["Service", "testservice-stable"]);
expect(kubeCtl.delete).toBeCalledWith(["TrafficSplit", "testservice-trafficsplit"]);
expect(readFileSpy).toBeCalledWith("manifests/bg.yaml");
expect(kubeCtl.getResource).toBeCalledWith("Deployment", "testapp");
});
test("blueGreenRejectSMI - deletes service if stable deployment doesn't exist", () => {
@ -383,12 +379,10 @@ test("blueGreenRejectSMI - deletes service if stable deployment doesn't exist",
//Invoke and assert
expect(blueGreenHelperSMI.rejectBlueGreenSMI(kubeCtl, ['manifests/bg.yaml'])).toMatchObject({});
expect(kubeCtl.delete).toBeCalledWith(["Deployment", "testapp-green"]);
expect(kubeCtl.delete).toBeCalledWith(["Service", "testservice"]);
expect(kubeCtl.delete).toBeCalledWith(["Service", "testservice-green"]);
expect(kubeCtl.delete).toBeCalledWith(["Service", "testservice-stable"]);
expect(kubeCtl.delete).toBeCalledWith(["TrafficSplit", "testservice-trafficsplit"]);
expect(readFileSpy).toBeCalledWith("manifests/bg.yaml");
expect(kubeCtl.getResource).toBeCalledWith("Deployment", "testapp");
});
// other functions and branches
@ -478,7 +472,7 @@ test("blueGreenRouteIngress - routes to green services in nextlabel is green", (
kubeCtl.apply = jest.fn().mockReturnValue('');
//Invoke and assert
expect(blueGreenHelperIngress.routeBlueGreenIngress(kubeCtl, 'green', serviceEntityMap, serEntList, ingEntList));
expect(blueGreenHelperIngress.routeBlueGreenIngress(kubeCtl, 'green', serviceEntityMap, ingEntList));
expect(kubeCtl.apply).toBeCalled();
expect(fileHelperMock.writeObjectsToFile).toBeCalled();
});
@ -599,7 +593,7 @@ test("validateTrafficSplitState - throws if trafficsplit in wrong state", () =>
kubeCtl.getResource = jest.fn().mockReturnValue(JSON.parse(JSON.stringify(temp)));
//Invoke and assert
expect(blueGreenHelperSMI.validateTrafficSplitsState(kubeCtl, depEntList, serEntList)).toBeFalsy();
expect(blueGreenHelperSMI.validateTrafficSplitsState(kubeCtl, serEntList)).toBeFalsy();
});
test("validateTrafficSplitState - throws if trafficsplit in wrong state", () => {
@ -678,7 +672,7 @@ test("validateTrafficSplitState - throws if trafficsplit in wrong state", () =>
kubeCtl.getResource = jest.fn().mockReturnValue(JSON.parse(JSON.stringify(temp)));
//Invoke and assert
expect(blueGreenHelperSMI.validateTrafficSplitsState(kubeCtl, depEntList, serEntList)).toBeFalsy();
expect(blueGreenHelperSMI.validateTrafficSplitsState(kubeCtl, serEntList)).toBeFalsy();
});
test("getSuffix() - returns BLUE_GREEN_SUFFIX if BLUE_GREEN_NEW_LABEL_VALUE is given, else emrty string", () => {

Просмотреть файл

@ -91,16 +91,16 @@ function promoteBlueGreen(kubectl) {
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
core.debug('routing to new deployments');
if (blue_green_helper_2.isIngressRoute()) {
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
blue_green_helper_1.deleteWorkloadsAndServicesWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
}
else if (blue_green_helper_2.isSMIRoute()) {
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
smi_blue_green_helper_1.cleanupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
smi_blue_green_helper_1.cleanupSMI(kubectl, manifestObjects.serviceEntityList);
}
else {
service_blue_green_helper_1.routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
service_blue_green_helper_1.routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
}
});

Просмотреть файл

@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchResource = exports.isServiceSelectorSubsetOfMatchLabel = exports.getServiceSelector = exports.getDeploymentMatchLabels = exports.getSpecLabel = exports.getBlueGreenResourceName = exports.addBlueGreenLabelsAndAnnotations = exports.getNewBlueGreenObject = exports.createWorkloadsWithLabel = exports.isServiceRouted = exports.getManifestObjects = exports.getSuffix = exports.deleteObjects = exports.deleteWorkloadsAndServicesWithLabel = exports.cleanUp = exports.deleteWorkloadsWithLabel = exports.routeBlueGreen = exports.isSMIRoute = exports.isIngressRoute = exports.isBlueGreenDeploymentStrategy = exports.STABLE_SUFFIX = exports.GREEN_SUFFIX = exports.BLUE_GREEN_VERSION_LABEL = exports.NONE_LABEL_VALUE = exports.GREEN_LABEL_VALUE = exports.BLUE_GREEN_DEPLOYMENT_STRATEGY = void 0;
exports.fetchResource = exports.isServiceSelectorSubsetOfMatchLabel = exports.getServiceSelector = exports.getDeploymentMatchLabels = exports.getBlueGreenResourceName = exports.addBlueGreenLabelsAndAnnotations = exports.getNewBlueGreenObject = exports.createWorkloadsWithLabel = exports.isServiceRouted = exports.getManifestObjects = exports.getSuffix = exports.deleteObjects = exports.deleteWorkloadsAndServicesWithLabel = exports.deleteWorkloadsWithLabel = exports.routeBlueGreen = exports.isSMIRoute = exports.isIngressRoute = exports.isBlueGreenDeploymentStrategy = exports.STABLE_SUFFIX = exports.GREEN_SUFFIX = exports.BLUE_GREEN_VERSION_LABEL = exports.NONE_LABEL_VALUE = exports.GREEN_LABEL_VALUE = exports.BLUE_GREEN_DEPLOYMENT_STRATEGY = void 0;
const core = require("@actions/core");
const fs = require("fs");
const yaml = require("js-yaml");
@ -50,22 +50,22 @@ function routeBlueGreen(kubectl, inputManifestFiles) {
let bufferTime = parseInt(TaskInputParameters.versionSwitchBuffer);
//logging start of buffer time
let dateNow = new Date();
console.log('starting buffer time of ' + bufferTime + ' minute/s at ' + dateNow.toISOString() + ' UTC');
console.log(`Starting buffer time of ${bufferTime} minute(s) at ${dateNow.toISOString()}`);
// waiting
yield utility_1.sleep(bufferTime * 1000 * 60);
// logging end of buffer time
dateNow = new Date();
console.log('stopping buffer time of ' + bufferTime + ' minute/s at ' + dateNow.toISOString() + ' UTC');
console.log(`Stopping buffer time of ${bufferTime} minute(s) at ${dateNow.toISOString()}`);
const manifestObjects = getManifestObjects(inputManifestFiles);
// routing to new deployments
if (isIngressRoute()) {
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
}
else if (isSMIRoute()) {
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.serviceEntityList);
}
else {
service_blue_green_helper_1.routeBlueGreenService(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
service_blue_green_helper_1.routeBlueGreenService(kubectl, exports.GREEN_LABEL_VALUE, manifestObjects.serviceEntityList);
}
});
}
@ -90,26 +90,6 @@ function deleteWorkloadsWithLabel(kubectl, deleteLabel, deploymentEntityList) {
deleteObjects(kubectl, resourcesToDelete);
}
exports.deleteWorkloadsWithLabel = deleteWorkloadsWithLabel;
function cleanUp(kubectl, deploymentEntityList, serviceEntityList) {
// checks if services has some stable deployments to target or deletes them too
let deleteList = [];
deploymentEntityList.forEach((deploymentObject) => {
const existingDeploy = fetchResource(kubectl, deploymentObject.kind, deploymentObject.metadata.name);
if (!existingDeploy) {
serviceEntityList.forEach((serviceObject) => {
const serviceSelector = getServiceSelector(serviceObject);
const matchLabels = getDeploymentMatchLabels(deploymentObject);
if (!!serviceSelector && !!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels)) {
const resourceToDelete = { name: serviceObject.metadata.name, kind: serviceObject.kind };
deleteList.push(resourceToDelete);
}
});
}
});
// delete service not targeting a deployment
deleteObjects(kubectl, deleteList);
}
exports.cleanUp = cleanUp;
function deleteWorkloadsAndServicesWithLabel(kubectl, deleteLabel, deploymentEntityList, serviceEntityList) {
// need to delete services and deployments
const deletionEntitiesList = deploymentEntityList.concat(serviceEntityList);
@ -156,19 +136,28 @@ exports.getSuffix = getSuffix;
// other common functions
function getManifestObjects(filePaths) {
const deploymentEntityList = [];
const serviceEntityList = [];
const routedServiceEntityList = [];
const unroutedServiceEntityList = [];
const ingressEntityList = [];
const otherEntitiesList = [];
let serviceNameMap = new Map();
filePaths.forEach((filePath) => {
const fileContents = fs.readFileSync(filePath);
yaml.safeLoadAll(fileContents, function (inputObject) {
if (!!inputObject) {
const kind = inputObject.kind;
const name = inputObject.metadata.name;
if (helper.isDeploymentEntity(kind)) {
deploymentEntityList.push(inputObject);
}
else if (helper.isServiceEntity(kind)) {
serviceEntityList.push(inputObject);
if (isServiceRouted(inputObject, deploymentEntityList)) {
routedServiceEntityList.push(inputObject);
serviceNameMap.set(name, getBlueGreenResourceName(name, exports.GREEN_SUFFIX));
}
else {
unroutedServiceEntityList.push(inputObject);
}
}
else if (helper.isIngressEntity(kind)) {
ingressEntityList.push(inputObject);
@ -179,27 +168,20 @@ function getManifestObjects(filePaths) {
}
});
});
let serviceNameMap = new Map();
// find all services and add their names with blue green suffix
serviceEntityList.forEach(inputObject => {
const name = inputObject.metadata.name;
serviceNameMap.set(name, getBlueGreenResourceName(name, exports.GREEN_SUFFIX));
});
return { serviceEntityList: serviceEntityList, serviceNameMap: serviceNameMap, deploymentEntityList: deploymentEntityList, ingressEntityList: ingressEntityList, otherObjects: otherEntitiesList };
return { serviceEntityList: routedServiceEntityList, serviceNameMap: serviceNameMap, unroutedServiceEntityList: unroutedServiceEntityList, deploymentEntityList: deploymentEntityList, ingressEntityList: ingressEntityList, otherObjects: otherEntitiesList };
}
exports.getManifestObjects = getManifestObjects;
function isServiceRouted(serviceObject, deploymentEntityList) {
let shouldBeRouted = false;
const serviceSelector = getServiceSelector(serviceObject);
if (!!serviceSelector) {
deploymentEntityList.every((depObject) => {
if (deploymentEntityList.some((depObject) => {
// finding if there is a deployment in the given manifests the service targets
const matchLabels = getDeploymentMatchLabels(depObject);
if (!!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels)) {
shouldBeRouted = true;
return false;
}
});
return (!!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels));
})) {
shouldBeRouted = true;
}
}
return shouldBeRouted;
}
@ -245,38 +227,31 @@ function getBlueGreenResourceName(name, suffix) {
return `${name}${suffix}`;
}
exports.getBlueGreenResourceName = getBlueGreenResourceName;
function getSpecLabel(inputObject) {
if (!!inputObject && inputObject.spec && inputObject.spec.selector && inputObject.spec.selector.matchLabels && inputObject.spec.selector.matchLabels[exports.BLUE_GREEN_VERSION_LABEL]) {
return inputObject.spec.selector.matchLabels[exports.BLUE_GREEN_VERSION_LABEL];
}
return '';
}
exports.getSpecLabel = getSpecLabel;
function getDeploymentMatchLabels(deploymentObject) {
if (!!deploymentObject && deploymentObject.kind.toUpperCase() == constants_1.KubernetesWorkload.pod.toUpperCase() && !!deploymentObject.metadata && !!deploymentObject.metadata.labels) {
return JSON.stringify(deploymentObject.metadata.labels);
return deploymentObject.metadata.labels;
}
else if (!!deploymentObject && deploymentObject.spec && deploymentObject.spec.selector && deploymentObject.spec.selector.matchLabels) {
return JSON.stringify(deploymentObject.spec.selector.matchLabels);
return deploymentObject.spec.selector.matchLabels;
}
return '';
return null;
}
exports.getDeploymentMatchLabels = getDeploymentMatchLabels;
function getServiceSelector(serviceObject) {
if (!!serviceObject && serviceObject.spec && serviceObject.spec.selector) {
return JSON.stringify(serviceObject.spec.selector);
return serviceObject.spec.selector;
}
else
return '';
return null;
}
exports.getServiceSelector = getServiceSelector;
function isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels) {
let serviceSelectorMap = new Map();
let matchLabelsMap = new Map();
JSON.parse(serviceSelector, (key, value) => {
JSON.parse(JSON.stringify(serviceSelector), (key, value) => {
serviceSelectorMap.set(key, value);
});
JSON.parse(matchLabels, (key, value) => {
JSON.parse(JSON.stringify(matchLabels), (key, value) => {
matchLabelsMap.set(key, value);
});
let isMatch = true;

Просмотреть файл

@ -28,7 +28,7 @@ function deployBlueGreenIngress(kubectl, filePaths) {
core.debug('New blue-green object is: ' + JSON.stringify(newBlueGreenObject));
newObjectsList.push(newBlueGreenObject);
});
newObjectsList = newObjectsList.concat(manifestObjects.otherObjects);
newObjectsList = newObjectsList.concat(manifestObjects.otherObjects).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
// return results to check for manifest stability
@ -62,13 +62,13 @@ function rejectBlueGreenIngress(kubectl, filePaths) {
// get all kubernetes objects defined in manifest files
const manifestObjects = blue_green_helper_1.getManifestObjects(filePaths);
// routing ingress to stables services
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
// deleting green services and deployments
blue_green_helper_1.deleteWorkloadsAndServicesWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
});
}
exports.rejectBlueGreenIngress = rejectBlueGreenIngress;
function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, serviceEntityList, ingressEntityList) {
function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, ingressEntityList) {
let newObjectsList = [];
if (!nextLabel) {
newObjectsList = newObjectsList.concat(ingressEntityList);

Просмотреть файл

@ -19,7 +19,7 @@ function deployBlueGreenService(kubectl, filePaths) {
// create deployments with green label value
const result = blue_green_helper_1.createWorkloadsWithLabel(kubectl, manifestObjects.deploymentEntityList, blue_green_helper_2.GREEN_LABEL_VALUE);
// create other non deployment and non service entities
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.ingressEntityList);
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.ingressEntityList).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
// returning deployment details to check for rollout stability
@ -29,7 +29,7 @@ exports.deployBlueGreenService = deployBlueGreenService;
function promoteBlueGreenService(kubectl, manifestObjects) {
return __awaiter(this, void 0, void 0, function* () {
// checking if services are in the right state ie. targeting green deployments
if (!validateServicesState(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList)) {
if (!validateServicesState(kubectl, manifestObjects.serviceEntityList)) {
throw ('NotInPromoteState');
}
// creating stable deployments with new configurations
@ -44,26 +44,17 @@ function rejectBlueGreenService(kubectl, filePaths) {
// get all kubernetes objects defined in manifest files
const manifestObjects = blue_green_helper_1.getManifestObjects(filePaths);
// routing to stable objects
routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
// seeing if we should even delete the service
blue_green_helper_1.cleanUp(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
// deleting the new deployments with green suffix
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
});
}
exports.rejectBlueGreenService = rejectBlueGreenService;
function routeBlueGreenService(kubectl, nextLabel, deploymentEntityList, serviceEntityList) {
function routeBlueGreenService(kubectl, nextLabel, serviceEntityList) {
const newObjectsList = [];
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
// if service is routed, point it to given label
const newBlueGreenServiceObject = getUpdatedBlueGreenService(serviceObject, nextLabel);
newObjectsList.push(newBlueGreenServiceObject);
}
else {
// if service is not routed, just push the original service
newObjectsList.push(serviceObject);
}
const newBlueGreenServiceObject = getUpdatedBlueGreenService(serviceObject, nextLabel);
newObjectsList.push(newBlueGreenServiceObject);
});
// configures the services
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
@ -77,24 +68,22 @@ function getUpdatedBlueGreenService(inputObject, labelValue) {
blue_green_helper_1.addBlueGreenLabelsAndAnnotations(newObject, labelValue);
return newObject;
}
function validateServicesState(kubectl, deploymentEntityList, serviceEntityList) {
function validateServicesState(kubectl, serviceEntityList) {
let areServicesGreen = true;
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
// finding the existing routed service
const existingService = blue_green_helper_1.fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name);
if (!!existingService) {
let currentLabel = getServiceSpecLabel(existingService);
if (currentLabel != blue_green_helper_2.GREEN_LABEL_VALUE) {
// service should be targeting deployments with green label
areServicesGreen = false;
}
}
else {
// service targeting deployment doesn't exist
// finding the existing routed service
const existingService = blue_green_helper_1.fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name);
if (!!existingService) {
let currentLabel = getServiceSpecLabel(existingService);
if (currentLabel != blue_green_helper_2.GREEN_LABEL_VALUE) {
// service should be targeting deployments with green label
areServicesGreen = false;
}
}
else {
// service targeting deployment doesn't exist
areServicesGreen = false;
}
});
return areServicesGreen;
}

Просмотреть файл

@ -23,11 +23,11 @@ function deployBlueGreenSMI(kubectl, filePaths) {
// get all kubernetes objects defined in manifest files
const manifestObjects = blue_green_helper_1.getManifestObjects(filePaths);
// creating services and other objects
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.serviceEntityList).concat(manifestObjects.ingressEntityList);
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.serviceEntityList).concat(manifestObjects.ingressEntityList).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
// make extraservices and trafficsplit
setupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
setupSMI(kubectl, manifestObjects.serviceEntityList);
// create new deloyments
const result = blue_green_helper_1.createWorkloadsWithLabel(kubectl, manifestObjects.deploymentEntityList, blue_green_helper_2.GREEN_LABEL_VALUE);
// return results to check for manifest stability
@ -37,7 +37,7 @@ exports.deployBlueGreenSMI = deployBlueGreenSMI;
function promoteBlueGreenSMI(kubectl, manifestObjects) {
return __awaiter(this, void 0, void 0, function* () {
// checking if there is something to promote
if (!validateTrafficSplitsState(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList)) {
if (!validateTrafficSplitsState(kubectl, manifestObjects.serviceEntityList)) {
throw ('NotInPromoteStateSMI');
}
// create stable deployments with new configuration
@ -52,29 +52,25 @@ function rejectBlueGreenSMI(kubectl, filePaths) {
// get all kubernetes objects defined in manifest files
const manifestObjects = blue_green_helper_1.getManifestObjects(filePaths);
// routing trafficsplit to stable deploymetns
routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
// deciding whether to delete services or not
blue_green_helper_1.cleanUp(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
// deleting rejected new bluegreen deplyments
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
//deleting trafficsplit and extra services
cleanupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
cleanupSMI(kubectl, manifestObjects.serviceEntityList);
});
}
exports.rejectBlueGreenSMI = rejectBlueGreenSMI;
function setupSMI(kubectl, deploymentEntityList, serviceEntityList) {
function setupSMI(kubectl, serviceEntityList) {
const newObjectsList = [];
const trafficObjectList = [];
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
// create a trafficsplit for service
trafficObjectList.push(serviceObject);
// setting up the services for trafficsplit
const newStableService = getSMIServiceResource(serviceObject, blue_green_helper_2.STABLE_SUFFIX);
const newGreenService = getSMIServiceResource(serviceObject, blue_green_helper_2.GREEN_SUFFIX);
newObjectsList.push(newStableService);
newObjectsList.push(newGreenService);
}
// create a trafficsplit for service
trafficObjectList.push(serviceObject);
// setting up the services for trafficsplit
const newStableService = getSMIServiceResource(serviceObject, blue_green_helper_2.STABLE_SUFFIX);
const newGreenService = getSMIServiceResource(serviceObject, blue_green_helper_2.GREEN_SUFFIX);
newObjectsList.push(newStableService);
newObjectsList.push(newGreenService);
});
// creating services
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
@ -87,7 +83,9 @@ function setupSMI(kubectl, deploymentEntityList, serviceEntityList) {
exports.setupSMI = setupSMI;
function createTrafficSplitObject(kubectl, name, nextLabel) {
// getting smi spec api version
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
if (!trafficSplitAPIVersion) {
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
}
// deciding weights based on nextlabel
let stableWeight;
let greenWeight;
@ -137,54 +135,48 @@ function getSMIServiceResource(inputObject, suffix) {
}
}
exports.getSMIServiceResource = getSMIServiceResource;
function routeBlueGreenSMI(kubectl, nextLabel, deploymentEntityList, serviceEntityList) {
function routeBlueGreenSMI(kubectl, nextLabel, serviceEntityList) {
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
// routing trafficsplit to given label
createTrafficSplitObject(kubectl, serviceObject.metadata.name, nextLabel);
}
// routing trafficsplit to given label
createTrafficSplitObject(kubectl, serviceObject.metadata.name, nextLabel);
});
}
exports.routeBlueGreenSMI = routeBlueGreenSMI;
function validateTrafficSplitsState(kubectl, deploymentEntityList, serviceEntityList) {
function validateTrafficSplitsState(kubectl, serviceEntityList) {
let areTrafficSplitsInRightState = true;
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
const name = serviceObject.metadata.name;
let trafficSplitObject = blue_green_helper_1.fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, blue_green_helper_1.getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX));
if (!trafficSplitObject) {
// no trafficplit exits
areTrafficSplitsInRightState = false;
}
trafficSplitObject = JSON.parse(JSON.stringify(trafficSplitObject));
trafficSplitObject.spec.backends.forEach(element => {
// checking if trafficsplit in right state to deploy
if (element.service === blue_green_helper_1.getBlueGreenResourceName(name, blue_green_helper_2.GREEN_SUFFIX)) {
if (element.weight != MAX_VAL) {
// green service should have max weight
areTrafficSplitsInRightState = false;
}
}
if (element.service === blue_green_helper_1.getBlueGreenResourceName(name, blue_green_helper_2.STABLE_SUFFIX)) {
if (element.weight != MIN_VAL) {
// stable service should have 0 weight
areTrafficSplitsInRightState = false;
}
}
});
const name = serviceObject.metadata.name;
let trafficSplitObject = blue_green_helper_1.fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, blue_green_helper_1.getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX));
if (!trafficSplitObject) {
// no trafficplit exits
areTrafficSplitsInRightState = false;
}
trafficSplitObject = JSON.parse(JSON.stringify(trafficSplitObject));
trafficSplitObject.spec.backends.forEach(element => {
// checking if trafficsplit in right state to deploy
if (element.service === blue_green_helper_1.getBlueGreenResourceName(name, blue_green_helper_2.GREEN_SUFFIX)) {
if (element.weight != MAX_VAL) {
// green service should have max weight
areTrafficSplitsInRightState = false;
}
}
if (element.service === blue_green_helper_1.getBlueGreenResourceName(name, blue_green_helper_2.STABLE_SUFFIX)) {
if (element.weight != MIN_VAL) {
// stable service should have 0 weight
areTrafficSplitsInRightState = false;
}
}
});
});
return areTrafficSplitsInRightState;
}
exports.validateTrafficSplitsState = validateTrafficSplitsState;
function cleanupSMI(kubectl, deploymentEntityList, serviceEntityList) {
function cleanupSMI(kubectl, serviceEntityList) {
const deleteList = [];
serviceEntityList.forEach((serviceObject) => {
if (blue_green_helper_1.isServiceRouted(serviceObject, deploymentEntityList)) {
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
}
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind });
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
});
// deleting all objects
blue_green_helper_1.deleteObjects(kubectl, deleteList);

Просмотреть файл

@ -1,4 +1,5 @@
'use strict';
import * as core from '@actions/core';
import * as deploymentHelper from '../utilities/strategy-helpers/deployment-helper';
import * as canaryDeploymentHelper from '../utilities/strategy-helpers/canary-deployment-helper';
@ -9,7 +10,7 @@ import { getUpdatedManifestFiles } from '../utilities/manifest-utilities'
import * as KubernetesObjectUtility from '../utilities/resource-object-utility';
import * as models from '../constants';
import * as KubernetesManifestUtility from '../utilities/manifest-stability-utility';
import { getManifestObjects, deleteWorkloadsWithLabel, deleteWorkloadsAndServicesWithLabel } from '../utilities/strategy-helpers/blue-green-helper';
import { getManifestObjects, deleteWorkloadsWithLabel, deleteWorkloadsAndServicesWithLabel, BlueGreenManifests } from '../utilities/strategy-helpers/blue-green-helper';
import { isBlueGreenDeploymentStrategy, isIngressRoute, isSMIRoute, GREEN_LABEL_VALUE, NONE_LABEL_VALUE } from '../utilities/strategy-helpers/blue-green-helper';
import { routeBlueGreenService, promoteBlueGreenService } from '../utilities/strategy-helpers/service-blue-green-helper';
import { routeBlueGreenIngress, promoteBlueGreenIngress } from '../utilities/strategy-helpers/ingress-blue-green-helper';
@ -59,7 +60,7 @@ async function promoteCanary(kubectl: Kubectl) {
async function promoteBlueGreen(kubectl: Kubectl) {
// updated container images and pull secrets
let inputManifestFiles: string[] = getUpdatedManifestFiles(TaskInputParameters.manifests);
const manifestObjects = getManifestObjects(inputManifestFiles);
const manifestObjects: BlueGreenManifests = getManifestObjects(inputManifestFiles);
core.debug('deleting old deployment and making new ones');
let result;
@ -78,14 +79,14 @@ async function promoteBlueGreen(kubectl: Kubectl) {
core.debug('routing to new deployments');
if(isIngressRoute()) {
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
deleteWorkloadsAndServicesWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
} else if (isSMIRoute()) {
routeBlueGreenSMI(kubectl, NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenSMI(kubectl, NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
deleteWorkloadsWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
cleanupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
cleanupSMI(kubectl, manifestObjects.serviceEntityList);
} else {
routeBlueGreenService(kubectl, NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenService(kubectl, NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
deleteWorkloadsWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
}
}

Просмотреть файл

@ -37,27 +37,36 @@ export function isSMIRoute(): boolean {
return routeMethod && routeMethod.toUpperCase() === SMI_ROUTE;
}
export interface BlueGreenManifests {
serviceEntityList: any[],
serviceNameMap: Map<string, string>,
unroutedServiceEntityList: any[],
deploymentEntityList: any[],
ingressEntityList: any[],
otherObjects: any[]
}
export async function routeBlueGreen(kubectl: Kubectl, inputManifestFiles: string[]) {
// get buffer time
let bufferTime: number = parseInt(TaskInputParameters.versionSwitchBuffer);
//logging start of buffer time
let dateNow = new Date();
console.log('starting buffer time of '+bufferTime+' minute/s at '+dateNow.toISOString()+' UTC');
console.log(`Starting buffer time of ${bufferTime} minute(s) at ${dateNow.toISOString()}`);
// waiting
await sleep(bufferTime*1000*60);
// logging end of buffer time
dateNow = new Date();
console.log('stopping buffer time of '+bufferTime+' minute/s at '+dateNow.toISOString()+' UTC');
console.log(`Stopping buffer time of ${bufferTime} minute(s) at ${dateNow.toISOString()}`);
const manifestObjects = getManifestObjects(inputManifestFiles);
const manifestObjects: BlueGreenManifests = getManifestObjects(inputManifestFiles);
// routing to new deployments
if (isIngressRoute()) {
routeBlueGreenIngress(kubectl, GREEN_LABEL_VALUE, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
routeBlueGreenIngress(kubectl, GREEN_LABEL_VALUE, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
} else if (isSMIRoute()) {
routeBlueGreenSMI(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenSMI(kubectl, GREEN_LABEL_VALUE, manifestObjects.serviceEntityList);
} else {
routeBlueGreenService(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenService(kubectl, GREEN_LABEL_VALUE, manifestObjects.serviceEntityList);
}
}
@ -82,27 +91,6 @@ export function deleteWorkloadsWithLabel(kubectl: Kubectl, deleteLabel: string,
deleteObjects(kubectl, resourcesToDelete);
}
export function cleanUp(kubectl: Kubectl, deploymentEntityList: any[], serviceEntityList: any[]) {
// checks if services has some stable deployments to target or deletes them too
let deleteList = [];
deploymentEntityList.forEach((deploymentObject) => {
const existingDeploy = fetchResource(kubectl, deploymentObject.kind, deploymentObject.metadata.name);
if (!existingDeploy) {
serviceEntityList.forEach((serviceObject) => {
const serviceSelector: string = getServiceSelector(serviceObject);
const matchLabels: string = getDeploymentMatchLabels(deploymentObject);
if (!!serviceSelector && !!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels)) {
const resourceToDelete = { name : serviceObject.metadata.name, kind : serviceObject.kind };
deleteList.push(resourceToDelete);
}
});
}
});
// delete service not targeting a deployment
deleteObjects(kubectl, deleteList);
}
export function deleteWorkloadsAndServicesWithLabel(kubectl: Kubectl, deleteLabel: string, deploymentEntityList: any[], serviceEntityList: any[]) {
// need to delete services and deployments
const deletionEntitiesList = deploymentEntityList.concat(serviceEntityList);
@ -144,20 +132,28 @@ export function getSuffix(label: string): string {
}
// other common functions
export function getManifestObjects (filePaths: string[]): any {
export function getManifestObjects (filePaths: string[]): BlueGreenManifests {
const deploymentEntityList = [];
const serviceEntityList = [];
const routedServiceEntityList = [];
const unroutedServiceEntityList = [];
const ingressEntityList = [];
const otherEntitiesList = [];
let serviceNameMap = new Map<string, string>();
filePaths.forEach((filePath: string) => {
const fileContents = fs.readFileSync(filePath);
yaml.safeLoadAll(fileContents, function (inputObject) {
if(!!inputObject) {
const kind = inputObject.kind;
const name = inputObject.metadata.name;
if (helper.isDeploymentEntity(kind)) {
deploymentEntityList.push(inputObject);
} else if (helper.isServiceEntity(kind)) {
serviceEntityList.push(inputObject);
if (isServiceRouted(inputObject, deploymentEntityList)) {
routedServiceEntityList.push(inputObject);
serviceNameMap.set(name, getBlueGreenResourceName(name, GREEN_SUFFIX));
} else {
unroutedServiceEntityList.push(inputObject);
}
} else if (helper.isIngressEntity(kind)) {
ingressEntityList.push(inputObject);
} else {
@ -167,28 +163,20 @@ export function getManifestObjects (filePaths: string[]): any {
});
})
let serviceNameMap = new Map<string, string>();
// find all services and add their names with blue green suffix
serviceEntityList.forEach(inputObject => {
const name = inputObject.metadata.name;
serviceNameMap.set(name, getBlueGreenResourceName(name, GREEN_SUFFIX));
});
return { serviceEntityList: serviceEntityList, serviceNameMap: serviceNameMap, deploymentEntityList: deploymentEntityList, ingressEntityList: ingressEntityList, otherObjects: otherEntitiesList };
return { serviceEntityList: routedServiceEntityList, serviceNameMap: serviceNameMap, unroutedServiceEntityList: unroutedServiceEntityList, deploymentEntityList: deploymentEntityList, ingressEntityList: ingressEntityList, otherObjects: otherEntitiesList };
}
export function isServiceRouted(serviceObject: any[], deploymentEntityList: any[]): boolean {
let shouldBeRouted: boolean = false;
const serviceSelector: string = getServiceSelector(serviceObject);
const serviceSelector: any = getServiceSelector(serviceObject);
if (!!serviceSelector) {
deploymentEntityList.every((depObject) => {
if (deploymentEntityList.some((depObject) => {
// finding if there is a deployment in the given manifests the service targets
const matchLabels: string = getDeploymentMatchLabels(depObject);
if (!!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels)) {
shouldBeRouted = true;
return false;
}
});
const matchLabels: any = getDeploymentMatchLabels(depObject);
return (!!matchLabels && isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels))
})) {
shouldBeRouted = true;
}
}
return shouldBeRouted;
}
@ -240,36 +228,30 @@ export function getBlueGreenResourceName(name: string, suffix: string) {
return `${name}${suffix}`;
}
export function getSpecLabel(inputObject: any): string {
if(!!inputObject && inputObject.spec && inputObject.spec.selector && inputObject.spec.selector.matchLabels && inputObject.spec.selector.matchLabels[BLUE_GREEN_VERSION_LABEL]) {
return inputObject.spec.selector.matchLabels[BLUE_GREEN_VERSION_LABEL];
}
return '';
}
export function getDeploymentMatchLabels(deploymentObject: any): string {
export function getDeploymentMatchLabels(deploymentObject: any): any {
if (!!deploymentObject && deploymentObject.kind.toUpperCase()==KubernetesWorkload.pod.toUpperCase() && !!deploymentObject.metadata && !!deploymentObject.metadata.labels) {
return JSON.stringify(deploymentObject.metadata.labels);
return deploymentObject.metadata.labels;
} else if (!!deploymentObject && deploymentObject.spec && deploymentObject.spec.selector && deploymentObject.spec.selector.matchLabels) {
return JSON.stringify(deploymentObject.spec.selector.matchLabels);
return deploymentObject.spec.selector.matchLabels;
}
return '';
return null;
}
export function getServiceSelector(serviceObject: any): string {
export function getServiceSelector(serviceObject: any): any {
if (!!serviceObject && serviceObject.spec && serviceObject.spec.selector) {
return JSON.stringify(serviceObject.spec.selector);
} else return '';
return serviceObject.spec.selector;
} else return null;
}
export function isServiceSelectorSubsetOfMatchLabel(serviceSelector: string, matchLabels: string): boolean {
export function isServiceSelectorSubsetOfMatchLabel(serviceSelector: any, matchLabels: any): boolean {
let serviceSelectorMap = new Map();
let matchLabelsMap = new Map();
JSON.parse(serviceSelector, (key, value) => {
JSON.parse(JSON.stringify(serviceSelector), (key, value) => {
serviceSelectorMap.set(key, value);
});
JSON.parse(matchLabels, (key, value) => {
JSON.parse(JSON.stringify(matchLabels), (key, value) => {
matchLabelsMap.set(key, value);
});

Просмотреть файл

@ -3,13 +3,13 @@
import * as core from '@actions/core';
import { Kubectl } from '../../kubectl-object-model';
import * as fileHelper from '../files-helper';
import { createWorkloadsWithLabel, getManifestObjects, getNewBlueGreenObject, addBlueGreenLabelsAndAnnotations, deleteWorkloadsAndServicesWithLabel, fetchResource } from './blue-green-helper';
import { createWorkloadsWithLabel, getManifestObjects, getNewBlueGreenObject, addBlueGreenLabelsAndAnnotations, deleteWorkloadsAndServicesWithLabel, fetchResource, BlueGreenManifests } from './blue-green-helper';
import { GREEN_LABEL_VALUE, NONE_LABEL_VALUE, BLUE_GREEN_VERSION_LABEL } from './blue-green-helper';
const BACKEND = 'BACKEND';
export function deployBlueGreenIngress(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// create deployments with green label value
const result = createWorkloadsWithLabel(kubectl, manifestObjects.deploymentEntityList, GREEN_LABEL_VALUE);
@ -21,7 +21,7 @@ export function deployBlueGreenIngress(kubectl: Kubectl, filePaths: string[]) {
core.debug('New blue-green object is: ' + JSON.stringify(newBlueGreenObject));
newObjectsList.push(newBlueGreenObject);
});
newObjectsList = newObjectsList.concat(manifestObjects.otherObjects);
newObjectsList = newObjectsList.concat(manifestObjects.otherObjects).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
@ -56,16 +56,16 @@ export async function promoteBlueGreenIngress(kubectl: Kubectl, manifestObjects)
export async function rejectBlueGreenIngress(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// routing ingress to stables services
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.serviceEntityList, manifestObjects.ingressEntityList);
routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
// deleting green services and deployments
deleteWorkloadsAndServicesWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
}
export function routeBlueGreenIngress(kubectl: Kubectl, nextLabel: string, serviceNameMap: Map<string, string>, serviceEntityList: any[], ingressEntityList: any[]) {
export function routeBlueGreenIngress(kubectl: Kubectl, nextLabel: string, serviceNameMap: Map<string, string>, ingressEntityList: any[]) {
let newObjectsList = [];
if (!nextLabel) {
newObjectsList = newObjectsList.concat(ingressEntityList);

Просмотреть файл

@ -2,18 +2,18 @@
import { Kubectl } from '../../kubectl-object-model';
import * as fileHelper from '../files-helper';
import { createWorkloadsWithLabel, getManifestObjects, addBlueGreenLabelsAndAnnotations, fetchResource, deleteWorkloadsWithLabel, cleanUp, isServiceRouted } from './blue-green-helper';
import { createWorkloadsWithLabel, getManifestObjects, addBlueGreenLabelsAndAnnotations, fetchResource, deleteWorkloadsWithLabel, BlueGreenManifests } from './blue-green-helper';
import { GREEN_LABEL_VALUE, NONE_LABEL_VALUE, BLUE_GREEN_VERSION_LABEL } from './blue-green-helper';
export function deployBlueGreenService(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// create deployments with green label value
const result = createWorkloadsWithLabel(kubectl, manifestObjects.deploymentEntityList, GREEN_LABEL_VALUE);
// create other non deployment and non service entities
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.ingressEntityList);
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.ingressEntityList).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
@ -23,7 +23,7 @@ export function deployBlueGreenService(kubectl: Kubectl, filePaths: string[]) {
export async function promoteBlueGreenService(kubectl: Kubectl, manifestObjects) {
// checking if services are in the right state ie. targeting green deployments
if (!validateServicesState(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList)) {
if (!validateServicesState(kubectl, manifestObjects.serviceEntityList)) {
throw('NotInPromoteState');
}
@ -36,29 +36,20 @@ export async function promoteBlueGreenService(kubectl: Kubectl, manifestObjects)
export async function rejectBlueGreenService(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// routing to stable objects
routeBlueGreenService(kubectl, NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
// seeing if we should even delete the service
cleanUp(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenService(kubectl, NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
// deleting the new deployments with green suffix
deleteWorkloadsWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
}
export function routeBlueGreenService(kubectl: Kubectl, nextLabel: string, deploymentEntityList: any[], serviceEntityList: any[]) {
export function routeBlueGreenService(kubectl: Kubectl, nextLabel: string, serviceEntityList: any[]) {
const newObjectsList = [];
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
// if service is routed, point it to given label
const newBlueGreenServiceObject = getUpdatedBlueGreenService(serviceObject, nextLabel);
newObjectsList.push(newBlueGreenServiceObject);
} else {
// if service is not routed, just push the original service
newObjectsList.push(serviceObject);
}
const newBlueGreenServiceObject = getUpdatedBlueGreenService(serviceObject, nextLabel);
newObjectsList.push(newBlueGreenServiceObject);
});
// configures the services
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
@ -73,22 +64,20 @@ function getUpdatedBlueGreenService(inputObject: any, labelValue: string): objec
return newObject;
}
export function validateServicesState(kubectl: Kubectl, deploymentEntityList: any[], serviceEntityList: any[]): boolean {
export function validateServicesState(kubectl: Kubectl, serviceEntityList: any[]): boolean {
let areServicesGreen: boolean = true;
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
// finding the existing routed service
const existingService = fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name);
if (!!existingService) {
let currentLabel: string = getServiceSpecLabel(existingService);
if(currentLabel != GREEN_LABEL_VALUE) {
// service should be targeting deployments with green label
areServicesGreen = false;
}
} else {
// service targeting deployment doesn't exist
// finding the existing routed service
const existingService = fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name);
if (!!existingService) {
let currentLabel: string = getServiceSpecLabel(existingService);
if(currentLabel != GREEN_LABEL_VALUE) {
// service should be targeting deployments with green label
areServicesGreen = false;
}
} else {
// service targeting deployment doesn't exist
areServicesGreen = false;
}
});
return areServicesGreen;

Просмотреть файл

@ -3,7 +3,7 @@
import { Kubectl } from '../../kubectl-object-model';
import * as kubectlUtils from '../kubectl-util';
import * as fileHelper from '../files-helper';
import { createWorkloadsWithLabel, getManifestObjects, fetchResource, deleteWorkloadsWithLabel, cleanUp, getNewBlueGreenObject, getBlueGreenResourceName, isServiceRouted, deleteObjects } from './blue-green-helper';
import { createWorkloadsWithLabel, getManifestObjects, fetchResource, deleteWorkloadsWithLabel, getNewBlueGreenObject, getBlueGreenResourceName, deleteObjects, BlueGreenManifests } from './blue-green-helper';
import { GREEN_LABEL_VALUE, NONE_LABEL_VALUE, GREEN_SUFFIX, STABLE_SUFFIX } from './blue-green-helper';
let trafficSplitAPIVersion = "";
@ -14,15 +14,15 @@ const MAX_VAL = '100';
export function deployBlueGreenSMI(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// creating services and other objects
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.serviceEntityList).concat(manifestObjects.ingressEntityList);
const newObjectsList = manifestObjects.otherObjects.concat(manifestObjects.serviceEntityList).concat(manifestObjects.ingressEntityList).concat(manifestObjects.unroutedServiceEntityList);
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
kubectl.apply(manifestFiles);
// make extraservices and trafficsplit
setupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
setupSMI(kubectl, manifestObjects.serviceEntityList);
// create new deloyments
const result = createWorkloadsWithLabel(kubectl, manifestObjects.deploymentEntityList, GREEN_LABEL_VALUE);
@ -33,7 +33,7 @@ export function deployBlueGreenSMI(kubectl: Kubectl, filePaths: string[]) {
export async function promoteBlueGreenSMI(kubectl: Kubectl, manifestObjects) {
// checking if there is something to promote
if (!validateTrafficSplitsState(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList)) {
if (!validateTrafficSplitsState(kubectl, manifestObjects.serviceEntityList)) {
throw('NotInPromoteStateSMI')
}
@ -46,34 +46,29 @@ export async function promoteBlueGreenSMI(kubectl: Kubectl, manifestObjects) {
export async function rejectBlueGreenSMI(kubectl: Kubectl, filePaths: string[]) {
// get all kubernetes objects defined in manifest files
const manifestObjects = getManifestObjects(filePaths);
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths);
// routing trafficsplit to stable deploymetns
routeBlueGreenSMI(kubectl, NONE_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
// deciding whether to delete services or not
cleanUp(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
routeBlueGreenSMI(kubectl, NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
// deleting rejected new bluegreen deplyments
deleteWorkloadsWithLabel(kubectl, GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
//deleting trafficsplit and extra services
cleanupSMI(kubectl, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
cleanupSMI(kubectl, manifestObjects.serviceEntityList);
}
export function setupSMI(kubectl: Kubectl, deploymentEntityList: any[], serviceEntityList: any[]) {
export function setupSMI(kubectl: Kubectl, serviceEntityList: any[]) {
const newObjectsList = [];
const trafficObjectList = []
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
// create a trafficsplit for service
trafficObjectList.push(serviceObject);
// setting up the services for trafficsplit
const newStableService = getSMIServiceResource(serviceObject, STABLE_SUFFIX);
const newGreenService = getSMIServiceResource(serviceObject, GREEN_SUFFIX);
newObjectsList.push(newStableService);
newObjectsList.push(newGreenService);
}
// create a trafficsplit for service
trafficObjectList.push(serviceObject);
// setting up the services for trafficsplit
const newStableService = getSMIServiceResource(serviceObject, STABLE_SUFFIX);
const newGreenService = getSMIServiceResource(serviceObject, GREEN_SUFFIX);
newObjectsList.push(newStableService);
newObjectsList.push(newGreenService);
});
// creating services
@ -88,7 +83,9 @@ export function setupSMI(kubectl: Kubectl, deploymentEntityList: any[], serviceE
function createTrafficSplitObject(kubectl: Kubectl ,name: string, nextLabel: string): any {
// getting smi spec api version
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
if (!trafficSplitAPIVersion) {
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
}
// deciding weights based on nextlabel
let stableWeight: number;
@ -140,56 +137,49 @@ export function getSMIServiceResource(inputObject: any, suffix: string): object
}
}
export function routeBlueGreenSMI(kubectl: Kubectl, nextLabel: string, deploymentEntityList: any[], serviceEntityList: any[]) {
export function routeBlueGreenSMI(kubectl: Kubectl, nextLabel: string, serviceEntityList: any[]) {
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
// routing trafficsplit to given label
createTrafficSplitObject(kubectl, serviceObject.metadata.name, nextLabel);
}
// routing trafficsplit to given label
createTrafficSplitObject(kubectl, serviceObject.metadata.name, nextLabel);
});
}
export function validateTrafficSplitsState(kubectl: Kubectl, deploymentEntityList: any[], serviceEntityList: any[]): boolean {
export function validateTrafficSplitsState(kubectl: Kubectl, serviceEntityList: any[]): boolean {
let areTrafficSplitsInRightState: boolean = true;
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
const name = serviceObject.metadata.name;
let trafficSplitObject = fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX));
if (!trafficSplitObject) {
// no trafficplit exits
areTrafficSplitsInRightState = false;
}
trafficSplitObject = JSON.parse(JSON.stringify(trafficSplitObject));
trafficSplitObject.spec.backends.forEach(element => {
// checking if trafficsplit in right state to deploy
if (element.service === getBlueGreenResourceName(name, GREEN_SUFFIX)) {
if (element.weight != MAX_VAL) {
// green service should have max weight
areTrafficSplitsInRightState = false;
}
}
if (element.service === getBlueGreenResourceName(name, STABLE_SUFFIX)) {
if (element.weight != MIN_VAL) {
// stable service should have 0 weight
areTrafficSplitsInRightState = false;
}
}
});
const name = serviceObject.metadata.name;
let trafficSplitObject = fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX));
if (!trafficSplitObject) {
// no trafficplit exits
areTrafficSplitsInRightState = false;
}
});
trafficSplitObject = JSON.parse(JSON.stringify(trafficSplitObject));
trafficSplitObject.spec.backends.forEach(element => {
// checking if trafficsplit in right state to deploy
if (element.service === getBlueGreenResourceName(name, GREEN_SUFFIX)) {
if (element.weight != MAX_VAL) {
// green service should have max weight
areTrafficSplitsInRightState = false;
}
}
if (element.service === getBlueGreenResourceName(name, STABLE_SUFFIX)) {
if (element.weight != MIN_VAL) {
// stable service should have 0 weight
areTrafficSplitsInRightState = false;
}
}
});
});
return areTrafficSplitsInRightState;
}
export function cleanupSMI(kubectl: Kubectl, deploymentEntityList: any[], serviceEntityList: any[]) {
export function cleanupSMI(kubectl: Kubectl, serviceEntityList: any[]) {
const deleteList = [];
serviceEntityList.forEach((serviceObject) => {
if (isServiceRouted(serviceObject, deploymentEntityList)) {
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, GREEN_SUFFIX), kind: serviceObject.kind});
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, STABLE_SUFFIX), kind: serviceObject.kind});
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT});
}
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, GREEN_SUFFIX), kind: serviceObject.kind});
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, STABLE_SUFFIX), kind: serviceObject.kind});
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT});
});
// deleting all objects