From f09ea44e2fed51f6ddd2888f23504c2baa966ffd Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Wed, 11 Sep 2019 18:22:57 +0530 Subject: [PATCH 1/7] service wait for ip --- .../src/models/constants.ts | 4 +- .../src/utils/DeploymentHelper.ts | 52 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/Tasks/KubernetesManifestV0/src/models/constants.ts b/Tasks/KubernetesManifestV0/src/models/constants.ts index edb0fa071f2f..2206cf50592a 100644 --- a/Tasks/KubernetesManifestV0/src/models/constants.ts +++ b/Tasks/KubernetesManifestV0/src/models/constants.ts @@ -11,9 +11,11 @@ export class KubernetesWorkload { public static daemonSet: string = 'DaemonSet'; public static job: string = 'job'; public static cronjob: string = 'cronjob'; + public static service: string = "service"; + public static ingress: string = "ingress"; } -export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; +export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'service', 'ingress']; export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset']; diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index 7fb212b85970..b124615045fa 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -75,6 +75,19 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): tl.warning(tl.loc('CouldNotDeterminePodStatus', JSON.stringify(ex))); } } + if(isEqual(resource.type, constants.KubernetesWorkload.service, StringComparer.OrdinalIgnoreCase)) { + try { + const spec = getServiceSpec(kubectl, resource.name); + if(isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase)) { + await waitForServiceExternalIpAssignment(kubectl, resource.name); + } + } catch (ex) { + tl.warning(tl.loc('CouldNotDetermineServiceStatus', JSON.stringify(ex))); + } + } + if(isEqual(resource.type, constants.KubernetesWorkload.ingress, StringComparer.OrdinalIgnoreCase)) { + getIngressResource(kubectl,resource.name); + } } utils.checkForErrors(rolloutStatusResults); } @@ -203,6 +216,45 @@ function isPodReady(podStatus: any): boolean { return allContainersAreReady; } +async function waitForServiceExternalIpAssignment(kubectl: Kubectl, serviceName: string): Promise { + const sleepTimeout = 10 * 1000; // 10 seconds + const iterations = 30; // 30 * 10 seconds timeout = 5 minutes max timeout + + for (let i = 0; i < iterations; i++) { + await sleep(sleepTimeout); + tl.debug(`Polling for service Ip assignmesnt: ${serviceName}`); + let status = getServiceStatus(kubectl, serviceName); + console.log("Service status " + JSON.stringify(status)); + if(status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0 ) { + console.log(`service/${serviceName} External Ip: ${status.loadBalancer.ingress[0].ip}`); + return; + } + } + tl.warning(`wait for service/${serviceName} external ip assignement timedout`); +} + +function getServiceSpec(kubectl: Kubectl, serviceName) { + const serviceResult = kubectl.getResource('service', serviceName); + utils.checkForErrors([serviceResult]); + const serviceSpec = JSON.parse(serviceResult.stdout).spec; + tl.debug(`Service Spec: ${JSON.stringify(serviceSpec)}`); + return serviceSpec; +} + + +function getServiceStatus(kubectl: Kubectl, serviceName: string): any { + const serviceResult = kubectl.getResource('service', serviceName); + utils.checkForErrors([serviceResult]); + const serviceStatus = JSON.parse(serviceResult.stdout).status; + tl.debug(`Service Status: ${JSON.stringify(serviceStatus)}`); + return serviceStatus; +} + +function getIngressResource(kubectl: Kubectl, ingressName: string) { + const ingressResult = kubectl.getResource('ingress', ingressName); + utils.checkForErrors([ingressResult]); +} + function sleep(timeout: number) { return new Promise(resolve => setTimeout(resolve, timeout)); } \ No newline at end of file From 24dbd773b33d9f775da00857a3cbeab5afc464b3 Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Wed, 11 Sep 2019 18:26:50 +0530 Subject: [PATCH 2/7] updated task.json --- .../Strings/resources.resjson/en-US/resources.resjson | 3 ++- Tasks/KubernetesManifestV0/task.json | 5 +++-- Tasks/KubernetesManifestV0/task.loc.json | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson index 69dae446fe55..3a5ab421518b 100644 --- a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson @@ -74,5 +74,6 @@ "loc.messages.InvalidPromotetActionDeploymentStrategy": "Promote action works only with strategy: canary", "loc.messages.AllContainersNotInReadyState": "All the containers are not in a ready state.", "loc.messages.CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", - "loc.messages.KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features." + "loc.messages.KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", + "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s" } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/task.json b/Tasks/KubernetesManifestV0/task.json index 85b691f22272..376b95915461 100644 --- a/Tasks/KubernetesManifestV0/task.json +++ b/Tasks/KubernetesManifestV0/task.json @@ -14,7 +14,7 @@ "version": { "Major": 0, "Minor": 156, - "Patch": 0 + "Patch": 1 }, "demands": [], "groups": [], @@ -333,6 +333,7 @@ "InvalidPromotetActionDeploymentStrategy": "Promote action works only with strategy: canary", "AllContainersNotInReadyState": "All the containers are not in a ready state.", "CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", - "KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features." + "KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", + "CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s" } } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/task.loc.json b/Tasks/KubernetesManifestV0/task.loc.json index a358e5061135..bbd08b2cd0ee 100644 --- a/Tasks/KubernetesManifestV0/task.loc.json +++ b/Tasks/KubernetesManifestV0/task.loc.json @@ -14,7 +14,7 @@ "version": { "Major": 0, "Minor": 156, - "Patch": 0 + "Patch": 1 }, "demands": [], "groups": [], @@ -333,6 +333,7 @@ "InvalidPromotetActionDeploymentStrategy": "ms-resource:loc.messages.InvalidPromotetActionDeploymentStrategy", "AllContainersNotInReadyState": "ms-resource:loc.messages.AllContainersNotInReadyState", "CouldNotDeterminePodStatus": "ms-resource:loc.messages.CouldNotDeterminePodStatus", - "KubectlShouldBeUpgraded": "ms-resource:loc.messages.KubectlShouldBeUpgraded" + "KubectlShouldBeUpgraded": "ms-resource:loc.messages.KubectlShouldBeUpgraded", + "CouldNotDetermineServiceStatus": "ms-resource:loc.messages.CouldNotDetermineServiceStatus" } } \ No newline at end of file From 1b08614b4fed85088d168a99b1e5d8193f2f480f Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Thu, 12 Sep 2019 11:55:30 +0530 Subject: [PATCH 3/7] code refactoring --- .../src/utils/DeploymentHelper.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index b124615045fa..47416af0b9cb 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -77,8 +77,10 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): } if(isEqual(resource.type, constants.KubernetesWorkload.service, StringComparer.OrdinalIgnoreCase)) { try { - const spec = getServiceSpec(kubectl, resource.name); - if(isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase)) { + const service = getService(kubectl, resource.name); + const spec = service.spec; + const status = service.status; + if(isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase) && !isLoadBalancerIpAssigned(status)) { await waitForServiceExternalIpAssignment(kubectl, resource.name); } } catch (ex) { @@ -216,38 +218,33 @@ function isPodReady(podStatus: any): boolean { return allContainersAreReady; } +function getService(kubectl: Kubectl, serviceName) { + const serviceResult = kubectl.getResource('service', serviceName); + utils.checkForErrors([serviceResult]); + return JSON.parse(serviceResult.stdout) +} + async function waitForServiceExternalIpAssignment(kubectl: Kubectl, serviceName: string): Promise { const sleepTimeout = 10 * 1000; // 10 seconds const iterations = 30; // 30 * 10 seconds timeout = 5 minutes max timeout for (let i = 0; i < iterations; i++) { await sleep(sleepTimeout); - tl.debug(`Polling for service Ip assignmesnt: ${serviceName}`); - let status = getServiceStatus(kubectl, serviceName); - console.log("Service status " + JSON.stringify(status)); - if(status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0 ) { - console.log(`service/${serviceName} External Ip: ${status.loadBalancer.ingress[0].ip}`); + tl.debug(`Waiting for service ${serviceName} Ip assignment`); + let status = getService(kubectl, serviceName).status; + tl.debug(`Service Status: ${JSON.stringify(status)}`); + if(isLoadBalancerIpAssigned(status) ) { return; } } tl.warning(`wait for service/${serviceName} external ip assignement timedout`); } -function getServiceSpec(kubectl: Kubectl, serviceName) { - const serviceResult = kubectl.getResource('service', serviceName); - utils.checkForErrors([serviceResult]); - const serviceSpec = JSON.parse(serviceResult.stdout).spec; - tl.debug(`Service Spec: ${JSON.stringify(serviceSpec)}`); - return serviceSpec; -} - - -function getServiceStatus(kubectl: Kubectl, serviceName: string): any { - const serviceResult = kubectl.getResource('service', serviceName); - utils.checkForErrors([serviceResult]); - const serviceStatus = JSON.parse(serviceResult.stdout).status; - tl.debug(`Service Status: ${JSON.stringify(serviceStatus)}`); - return serviceStatus; +function isLoadBalancerIpAssigned(status: any) { + if(status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0 ) { + return true; + } + return false; } function getIngressResource(kubectl: Kubectl, ingressName: string) { From 6cb8e28f451b8d279190380d62e88512e26ac21c Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Fri, 13 Sep 2019 15:32:29 +0530 Subject: [PATCH 4/7] addressed review comments --- .../src/models/constants.ts | 9 ++-- .../src/utils/DeploymentHelper.ts | 44 ++++++++++--------- Tasks/KubernetesManifestV0/task.json | 4 +- Tasks/KubernetesManifestV0/task.loc.json | 4 +- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/Tasks/KubernetesManifestV0/src/models/constants.ts b/Tasks/KubernetesManifestV0/src/models/constants.ts index 2206cf50592a..6c1dc4328b9e 100644 --- a/Tasks/KubernetesManifestV0/src/models/constants.ts +++ b/Tasks/KubernetesManifestV0/src/models/constants.ts @@ -11,11 +11,14 @@ export class KubernetesWorkload { public static daemonSet: string = 'DaemonSet'; public static job: string = 'job'; public static cronjob: string = 'cronjob'; - public static service: string = "service"; - public static ingress: string = "ingress"; } -export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'service', 'ingress']; +export class DiscoveryAndLoadBalancerResource { + public static service: string = 'service'; + public static ingress: string = 'ingress'; +} + +export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset']; diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index 47416af0b9cb..dca1f70b509d 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -30,9 +30,13 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy)); // check manifest stability - const resourceTypes: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes); + const resourceTypes: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([constants.DiscoveryAndLoadBalancerResource.service]) ); await checkManifestStability(kubectl, resourceTypes); + // print ingress resources + const ingressResources: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, [constants.DiscoveryAndLoadBalancerResource.ingress]); + getIngressResources(kubectl, ingressResources); + // annotate resources annotateResources(deployedManifestFiles, kubectl, resourceTypes); } @@ -63,7 +67,7 @@ function deployManifests(files: string[], kubectl: Kubectl, isCanaryDeploymentSt async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): Promise { const rolloutStatusResults = []; const numberOfResources = resources.length; - for (let i = 0; i< numberOfResources; i++) { + for (let i = 0; i < numberOfResources; i++) { const resource = resources[i]; if (models.workloadTypesWithRolloutStatus.indexOf(resource.type.toLowerCase()) >= 0) { rolloutStatusResults.push(kubectl.checkRolloutStatus(resource.type, resource.name)); @@ -75,21 +79,18 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): tl.warning(tl.loc('CouldNotDeterminePodStatus', JSON.stringify(ex))); } } - if(isEqual(resource.type, constants.KubernetesWorkload.service, StringComparer.OrdinalIgnoreCase)) { + if (isEqual(resource.type, constants.DiscoveryAndLoadBalancerResource.service, StringComparer.OrdinalIgnoreCase)) { try { const service = getService(kubectl, resource.name); const spec = service.spec; const status = service.status; - if(isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase) && !isLoadBalancerIpAssigned(status)) { - await waitForServiceExternalIpAssignment(kubectl, resource.name); + if (isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase) && !isLoadBalancerIPAssigned(status)) { + await waitForServiceExternalIPAssignment(kubectl, resource.name); } } catch (ex) { tl.warning(tl.loc('CouldNotDetermineServiceStatus', JSON.stringify(ex))); } } - if(isEqual(resource.type, constants.KubernetesWorkload.ingress, StringComparer.OrdinalIgnoreCase)) { - getIngressResource(kubectl,resource.name); - } } utils.checkForErrors(rolloutStatusResults); } @@ -219,37 +220,38 @@ function isPodReady(podStatus: any): boolean { } function getService(kubectl: Kubectl, serviceName) { - const serviceResult = kubectl.getResource('service', serviceName); + const serviceResult = kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.service, serviceName); utils.checkForErrors([serviceResult]); return JSON.parse(serviceResult.stdout) } -async function waitForServiceExternalIpAssignment(kubectl: Kubectl, serviceName: string): Promise { +async function waitForServiceExternalIPAssignment(kubectl: Kubectl, serviceName: string): Promise { const sleepTimeout = 10 * 1000; // 10 seconds - const iterations = 30; // 30 * 10 seconds timeout = 5 minutes max timeout + const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout for (let i = 0; i < iterations; i++) { await sleep(sleepTimeout); - tl.debug(`Waiting for service ${serviceName} Ip assignment`); + tl.debug(`Waiting for service ${serviceName} IP assignment`); let status = getService(kubectl, serviceName).status; - tl.debug(`Service Status: ${JSON.stringify(status)}`); - if(isLoadBalancerIpAssigned(status) ) { - return; + if (isLoadBalancerIPAssigned(status)) { + return; } } - tl.warning(`wait for service/${serviceName} external ip assignement timedout`); + tl.warning(`wait for service/${serviceName} external IP assignment timedout`); } -function isLoadBalancerIpAssigned(status: any) { - if(status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0 ) { +function isLoadBalancerIPAssigned(status: any) { + if (status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0) { return true; } return false; } -function getIngressResource(kubectl: Kubectl, ingressName: string) { - const ingressResult = kubectl.getResource('ingress', ingressName); - utils.checkForErrors([ingressResult]); +function getIngressResources(kubectl: Kubectl, ingressResources: Resource[]) { + const numberOfResources = ingressResources.length; + for (let i = 0; i < numberOfResources; i++) { + kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.ingress, ingressResources[i].name); + } } function sleep(timeout: number) { diff --git a/Tasks/KubernetesManifestV0/task.json b/Tasks/KubernetesManifestV0/task.json index 376b95915461..270e8a715fe2 100644 --- a/Tasks/KubernetesManifestV0/task.json +++ b/Tasks/KubernetesManifestV0/task.json @@ -13,8 +13,8 @@ "author": "Microsoft Corporation", "version": { "Major": 0, - "Minor": 156, - "Patch": 1 + "Minor": 159, + "Patch": 0 }, "demands": [], "groups": [], diff --git a/Tasks/KubernetesManifestV0/task.loc.json b/Tasks/KubernetesManifestV0/task.loc.json index bbd08b2cd0ee..b49e59934137 100644 --- a/Tasks/KubernetesManifestV0/task.loc.json +++ b/Tasks/KubernetesManifestV0/task.loc.json @@ -13,8 +13,8 @@ "author": "Microsoft Corporation", "version": { "Major": 0, - "Minor": 156, - "Patch": 1 + "Minor": 159, + "Patch": 0 }, "demands": [], "groups": [], From bda8cd72ec0396f45dadd9ff3cc55ab593bcf332 Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Mon, 16 Sep 2019 15:16:18 +0530 Subject: [PATCH 5/7] addressing review comments --- .../Strings/resources.resjson/en-US/resources.resjson | 4 +++- Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts | 6 +++--- Tasks/KubernetesManifestV0/task.json | 4 +++- Tasks/KubernetesManifestV0/task.loc.json | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson index 3a5ab421518b..7c041a99bb83 100644 --- a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson @@ -75,5 +75,7 @@ "loc.messages.AllContainersNotInReadyState": "All the containers are not in a ready state.", "loc.messages.CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", "loc.messages.KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", - "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s" + "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", + "loc.messages.waitForServiceIpAssignment": "Waiting for service %s external IP assignment", + "loc.messages.waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timedout" } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index dca1f70b509d..0235e7243145 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -30,7 +30,7 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy)); // check manifest stability - const resourceTypes: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([constants.DiscoveryAndLoadBalancerResource.service]) ); + const resourceTypes: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([constants.DiscoveryAndLoadBalancerResource.service])); await checkManifestStability(kubectl, resourceTypes); // print ingress resources @@ -231,13 +231,13 @@ async function waitForServiceExternalIPAssignment(kubectl: Kubectl, serviceName: for (let i = 0; i < iterations; i++) { await sleep(sleepTimeout); - tl.debug(`Waiting for service ${serviceName} IP assignment`); + console.log(tl.loc('waitForServiceIpAssignment', serviceName)); let status = getService(kubectl, serviceName).status; if (isLoadBalancerIPAssigned(status)) { return; } } - tl.warning(`wait for service/${serviceName} external IP assignment timedout`); + tl.warning(tl.loc('waitForServiceIpAssignmentTimedOut', serviceName)); } function isLoadBalancerIPAssigned(status: any) { diff --git a/Tasks/KubernetesManifestV0/task.json b/Tasks/KubernetesManifestV0/task.json index 270e8a715fe2..c0d33a5c2283 100644 --- a/Tasks/KubernetesManifestV0/task.json +++ b/Tasks/KubernetesManifestV0/task.json @@ -334,6 +334,8 @@ "AllContainersNotInReadyState": "All the containers are not in a ready state.", "CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", "KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", - "CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s" + "CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", + "waitForServiceIpAssignment": "Waiting for service %s external IP assignment", + "waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timedout" } } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/task.loc.json b/Tasks/KubernetesManifestV0/task.loc.json index b49e59934137..b87ade82b7e4 100644 --- a/Tasks/KubernetesManifestV0/task.loc.json +++ b/Tasks/KubernetesManifestV0/task.loc.json @@ -334,6 +334,8 @@ "AllContainersNotInReadyState": "ms-resource:loc.messages.AllContainersNotInReadyState", "CouldNotDeterminePodStatus": "ms-resource:loc.messages.CouldNotDeterminePodStatus", "KubectlShouldBeUpgraded": "ms-resource:loc.messages.KubectlShouldBeUpgraded", - "CouldNotDetermineServiceStatus": "ms-resource:loc.messages.CouldNotDetermineServiceStatus" + "CouldNotDetermineServiceStatus": "ms-resource:loc.messages.CouldNotDetermineServiceStatus", + "waitForServiceIpAssignment": "ms-resource:loc.messages.waitForServiceIpAssignment", + "waitForServiceIpAssignmentTimedOut": "ms-resource:loc.messages.waitForServiceIpAssignmentTimedOut" } } \ No newline at end of file From 44bba709ee01888ac28a9a6e5f8f9c3a20fef845 Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Mon, 16 Sep 2019 17:28:27 +0530 Subject: [PATCH 6/7] some more review comments --- .../Strings/resources.resjson/en-US/resources.resjson | 2 +- Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts | 9 ++++----- Tasks/KubernetesManifestV0/task.json | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson index 7c041a99bb83..daa369052d74 100644 --- a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson @@ -77,5 +77,5 @@ "loc.messages.KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", "loc.messages.waitForServiceIpAssignment": "Waiting for service %s external IP assignment", - "loc.messages.waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timedout" + "loc.messages.waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out" } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index 8609556243d1..2227b15ee82e 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -290,8 +290,8 @@ async function waitForServiceExternalIPAssignment(kubectl: Kubectl, serviceName: const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout for (let i = 0; i < iterations; i++) { - await sleep(sleepTimeout); console.log(tl.loc('waitForServiceIpAssignment', serviceName)); + await sleep(sleepTimeout); let status = getService(kubectl, serviceName).status; if (isLoadBalancerIPAssigned(status)) { return; @@ -308,10 +308,9 @@ function isLoadBalancerIPAssigned(status: any) { } function getIngressResources(kubectl: Kubectl, ingressResources: Resource[]) { - const numberOfResources = ingressResources.length; - for (let i = 0; i < numberOfResources; i++) { - kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.ingress, ingressResources[i].name); - } + ingressResources.forEach(ingressResource => { + kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name); + }); } function sleep(timeout: number) { diff --git a/Tasks/KubernetesManifestV0/task.json b/Tasks/KubernetesManifestV0/task.json index c0d33a5c2283..4605a48f7369 100644 --- a/Tasks/KubernetesManifestV0/task.json +++ b/Tasks/KubernetesManifestV0/task.json @@ -336,6 +336,6 @@ "KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", "CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", "waitForServiceIpAssignment": "Waiting for service %s external IP assignment", - "waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timedout" + "waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out" } } \ No newline at end of file From 76df95410e86cd6774477d5dd829a6888d395ee6 Mon Sep 17 00:00:00 2001 From: Anurag Chauhan Date: Wed, 18 Sep 2019 14:54:55 +0530 Subject: [PATCH 7/7] added test for service IP --- .../resources.resjson/en-US/resources.resjson | 5 ++-- Tasks/KubernetesManifestV0/Tests/L0.ts | 1 + Tasks/KubernetesManifestV0/Tests/TestSetup.ts | 7 ++++++ .../Tests/manifests/deployment.yaml | 20 +++++++++++++++- .../src/models/constants.ts | 6 +++++ .../src/utils/DeploymentHelper.ts | 23 +++++++++---------- Tasks/KubernetesManifestV0/task.json | 5 ++-- Tasks/KubernetesManifestV0/task.loc.json | 3 ++- 8 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson index daa369052d74..e7d545466248 100644 --- a/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/KubernetesManifestV0/Strings/resources.resjson/en-US/resources.resjson @@ -75,7 +75,8 @@ "loc.messages.AllContainersNotInReadyState": "All the containers are not in a ready state.", "loc.messages.CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", "loc.messages.KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", - "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", + "loc.messages.CouldNotDetermineServiceStatus": "Could not determine the service %s status due to the error: %s", "loc.messages.waitForServiceIpAssignment": "Waiting for service %s external IP assignment", - "loc.messages.waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out" + "loc.messages.waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out", + "loc.messages.ServiceExternalIP": "service %s external IP is %s" } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/Tests/L0.ts b/Tasks/KubernetesManifestV0/Tests/L0.ts index a58d10cd25a6..16a1e04ac015 100644 --- a/Tasks/KubernetesManifestV0/Tests/L0.ts +++ b/Tasks/KubernetesManifestV0/Tests/L0.ts @@ -45,6 +45,7 @@ describe('Kubernetes Manifests Suite', function () { process.env[shared.TestEnvVars.imagePullSecrets] = 'test-key1\ntest-key2'; tr.run(); assert(tr.succeeded, 'task should have succeeded'); + assert(tr.stdout.indexOf('nginx-service 104.211.243.77') != -1, 'nginx-service external IP is 104.211.243.77') done(); }); diff --git a/Tasks/KubernetesManifestV0/Tests/TestSetup.ts b/Tasks/KubernetesManifestV0/Tests/TestSetup.ts index 508720f75aa9..b9ec77116d0d 100644 --- a/Tasks/KubernetesManifestV0/Tests/TestSetup.ts +++ b/Tasks/KubernetesManifestV0/Tests/TestSetup.ts @@ -247,6 +247,11 @@ a.exec[`${kubectlPath} scale ${process.env[shared.TestEnvVars.kind]}/${process.e stdout: 'created secret' } +a.exec[`${kubectlPath} get service/nginx-service -o json --namespace testnamespace`] = { + 'code': 0, + 'stdout': '{\r\n "apiVersion": "v1",\r\n "kind": "Service",\r\n "metadata": {\r\n "annotations": {\r\n "azure-pipelines/jobName": "Agent phase",\r\n "azure-pipelines/org": "https://codedev.ms/anchauh/",\r\n "azure-pipelines/pipeline": "aksCd-153 - 64 - CD",\r\n "azure-pipelines/pipelineId": "40",\r\n "azure-pipelines/project": "nginx",\r\n "azure-pipelines/run": "41",\r\n "azure-pipelines/runuri": "https://codedev.ms/anchauh/nginx/_releaseProgress?releaseId=41",\r\n "kubectl.kubernetes.io/last-applied-configuration": "{\\"apiVersion\\":\\"v1\\",\\"kind\\":\\"Service\\",\\"metadata\\":{\\"annotations\\":{},\\"labels\\":{\\"app\\":\\"nginx\\"},\\"name\\":\\"nginx-service\\",\\"namespace\\":\\"testnamespace\\"},\\"spec\\":{\\"ports\\":[{\\"name\\":\\"http\\",\\"port\\":80,\\"protocol\\":\\"TCP\\",\\"targetPort\\":\\"http\\"}],\\"selector\\":{\\"app\\":\\"nginx\\"},\\"type\\":\\"LoadBalancer\\"}}\\n"\r\n },\r\n "creationTimestamp": "2019-09-11T10:09:09Z",\r\n "labels": {\r\n "app": "nginx"\r\n },\r\n "name": "nginx-service",\r\n "namespace": "testnamespace",\r\n "resourceVersion": "8754335",\r\n "selfLink": "/api/v1/namespaces/testnamespace/services/nginx-service",\r\n "uid": "31f02713-d47c-11e9-9448-16b93c17a2b4"\r\n },\r\n "spec": {\r\n "clusterIP": "10.0.157.189",\r\n "externalTrafficPolicy": "Cluster",\r\n "ports": [\r\n {\r\n "name": "http",\r\n "nodePort": 32112,\r\n "port": 80,\r\n "protocol": "TCP",\r\n "targetPort": "http"\r\n }\r\n ],\r\n "selector": {\r\n "app": "nginx"\r\n },\r\n "sessionAffinity": "***",\r\n "type": "LoadBalancer"\r\n },\r\n "status": {\r\n "loadBalancer": {\r\n "ingress": [\r\n {\r\n "ip": "104.211.243.77"\r\n }\r\n ]\r\n }\r\n }\r\n }' +} + const pipelineAnnotations: string = [ `azure-pipelines/run=${buildNumber}`, `azure-pipelines/pipeline="${definitionName}"`, @@ -282,6 +287,7 @@ if (process.env[shared.TestEnvVars.arguments]) { }; } + tr.setAnswers(a); tr.registerMock('azure-pipelines-task-lib/toolrunner', require('azure-pipelines-task-lib/mock-toolrunner')); @@ -324,6 +330,7 @@ tr.registerMock('../utils/FileHelper', { }); if (newFilePaths.length === 0) { + console.log(shared.ManifestFilesPath); newFilePaths.push(shared.ManifestFilesPath); } return newFilePaths; diff --git a/Tasks/KubernetesManifestV0/Tests/manifests/deployment.yaml b/Tasks/KubernetesManifestV0/Tests/manifests/deployment.yaml index 41df31909c65..4a24b4e9f65e 100644 --- a/Tasks/KubernetesManifestV0/Tests/manifests/deployment.yaml +++ b/Tasks/KubernetesManifestV0/Tests/manifests/deployment.yaml @@ -20,4 +20,22 @@ spec: ports: - containerPort: 80 imagePullSecrets: - - name: key1 \ No newline at end of file + - name: key1 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: nginx-service + labels: + app: nginx +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app: nginx \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/src/models/constants.ts b/Tasks/KubernetesManifestV0/src/models/constants.ts index 6c1dc4328b9e..6b25c1816c38 100644 --- a/Tasks/KubernetesManifestV0/src/models/constants.ts +++ b/Tasks/KubernetesManifestV0/src/models/constants.ts @@ -18,6 +18,12 @@ export class DiscoveryAndLoadBalancerResource { public static ingress: string = 'ingress'; } +export class ServiceTypes { + public static loadBalancer: string = 'LoadBalancer'; + public static nodePort: string = 'NodePort'; + public static clusterIP: string = 'ClusterIP' +} + export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset']; diff --git a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts index 2227b15ee82e..8236072e57bc 100644 --- a/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts +++ b/Tasks/KubernetesManifestV0/src/utils/DeploymentHelper.ts @@ -39,7 +39,9 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl // print ingress resources const ingressResources: Resource[] = KubernetesObjectUtility.getResources(deployedManifestFiles, [constants.DiscoveryAndLoadBalancerResource.ingress]); - getIngressResources(kubectl, ingressResources); + ingressResources.forEach(ingressResource => { + kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name); + }); // annotate resources const allPods = JSON.parse((kubectl.getAllPods()).stdout); @@ -101,11 +103,14 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): const service = getService(kubectl, resource.name); const spec = service.spec; const status = service.status; - if (isEqual(spec.type, "LoadBalancer", StringComparer.OrdinalIgnoreCase) && !isLoadBalancerIPAssigned(status)) { - await waitForServiceExternalIPAssignment(kubectl, resource.name); + if (isEqual(spec.type, constants.ServiceTypes.loadBalancer, StringComparer.OrdinalIgnoreCase)) { + if(!isLoadBalancerIPAssigned(status)) { + await waitForServiceExternalIPAssignment(kubectl, resource.name); + } + console.log(tl.loc('ServiceExternalIP', resource.name, status.loadBalancer.ingress[0].ip)); } } catch (ex) { - tl.warning(tl.loc('CouldNotDetermineServiceStatus', JSON.stringify(ex))); + tl.warning(tl.loc('CouldNotDetermineServiceStatus', resource.name, JSON.stringify(ex))); } } } @@ -282,7 +287,7 @@ function isPodReady(podStatus: any): boolean { function getService(kubectl: Kubectl, serviceName) { const serviceResult = kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.service, serviceName); utils.checkForErrors([serviceResult]); - return JSON.parse(serviceResult.stdout) + return JSON.parse(serviceResult.stdout); } async function waitForServiceExternalIPAssignment(kubectl: Kubectl, serviceName: string): Promise { @@ -301,18 +306,12 @@ async function waitForServiceExternalIPAssignment(kubectl: Kubectl, serviceName: } function isLoadBalancerIPAssigned(status: any) { - if (status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0) { + if (status && status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0) { return true; } return false; } -function getIngressResources(kubectl: Kubectl, ingressResources: Resource[]) { - ingressResources.forEach(ingressResource => { - kubectl.getResource(constants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name); - }); -} - function sleep(timeout: number) { return new Promise(resolve => setTimeout(resolve, timeout)); } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/task.json b/Tasks/KubernetesManifestV0/task.json index 4605a48f7369..d4039e1575e0 100644 --- a/Tasks/KubernetesManifestV0/task.json +++ b/Tasks/KubernetesManifestV0/task.json @@ -334,8 +334,9 @@ "AllContainersNotInReadyState": "All the containers are not in a ready state.", "CouldNotDeterminePodStatus": "Could not determine the pod's status due to the error: %s", "KubectlShouldBeUpgraded": "kubectl client version equal to v1.14 or higher is required to use kustomize features.", - "CouldNotDetermineServiceStatus": "Could not determine the service's status due to the error: %s", + "CouldNotDetermineServiceStatus": "Could not determine the service %s status due to the error: %s", "waitForServiceIpAssignment": "Waiting for service %s external IP assignment", - "waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out" + "waitForServiceIpAssignmentTimedOut": "Wait for service %s external IP assignment timed out", + "ServiceExternalIP": "service %s external IP is %s" } } \ No newline at end of file diff --git a/Tasks/KubernetesManifestV0/task.loc.json b/Tasks/KubernetesManifestV0/task.loc.json index b87ade82b7e4..01a76009c738 100644 --- a/Tasks/KubernetesManifestV0/task.loc.json +++ b/Tasks/KubernetesManifestV0/task.loc.json @@ -336,6 +336,7 @@ "KubectlShouldBeUpgraded": "ms-resource:loc.messages.KubectlShouldBeUpgraded", "CouldNotDetermineServiceStatus": "ms-resource:loc.messages.CouldNotDetermineServiceStatus", "waitForServiceIpAssignment": "ms-resource:loc.messages.waitForServiceIpAssignment", - "waitForServiceIpAssignmentTimedOut": "ms-resource:loc.messages.waitForServiceIpAssignmentTimedOut" + "waitForServiceIpAssignmentTimedOut": "ms-resource:loc.messages.waitForServiceIpAssignmentTimedOut", + "ServiceExternalIP": "ms-resource:loc.messages.ServiceExternalIP" } } \ No newline at end of file