Skip to content

Commit 97463ee

Browse files
committed
TIR - remove dynamic pod data and surface helm status
If helm is used for deployment, the components deployed by helm are no longer listed as toplevel elements in the TIR. Instead the helm release is represented by its status so that users will see tables under: Deployment Resource: <helm-release-name>-deploymentMean Deployment Resource: <helm-release-name>-deploymentStatus The DeploymentStatus has rows surfacing the releaseName, releaseRevision, namespace, deployStatus, deployDescription, lastDeployed and resources. Here an example from the resources: "Deployment: backend-helm-monorepo-chart-component-a, backend- helm-monorepo-chart-component-b, Service: backend-helm-monorepo- chart" Non-Helm deployments are surfaced as before. Further change notes: - To avoid CPS issues in Jenkins all types were using their own file and the Immutable annotation was avoided. - Type HelmStatusData captures helm status with high fidelity whereas HelmStatusSimpleData captures shallower information in particular omitting the details of resources except their names and kinds. This approach simplifies the json Helm status json parsing code while keeping the consuming code relatively lean. In the deployment artifact the simplified helmStatus is kept under key 'helmStatus' in the deploymentMean information. Which is also written to `ods-deployments.json` by DeploymentDescriptor. - PodData fields capturing dynamic data where removed, except for the podName which has a prefix useful for correlating dynamic data with pods. At the moment this is still needed for both tailor and helm deployments. The code which finds this named pods did require retry logic even for helm installs using --atomic.
1 parent 343ffc7 commit 97463ee

13 files changed

Lines changed: 712 additions & 147 deletions

src/org/ods/component/AbstractDeploymentStrategy.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ abstract class AbstractDeploymentStrategy implements IDeploymentStrategy {
1414
@Override
1515
abstract Map<String, List<PodData>> deploy()
1616

17+
// Fetches original kubernetes revisions of deployment resources.
18+
//
19+
// returns a two level map with keys resourceKind -> resourceName -> revision (int)
1720
protected Map<String, Map<String, Integer>> fetchOriginalVersions(Map<String, List<String>> deploymentResources) {
1821
def originalVersions = [:]
1922
deploymentResources.each { resourceKind, resourceNames ->

src/org/ods/component/HelmDeploymentStrategy.groovy

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import groovy.transform.TypeChecked
55
import groovy.transform.TypeCheckingMode
66
import org.ods.services.JenkinsService
77
import org.ods.services.OpenShiftService
8+
import org.ods.util.HelmStatusSimpleData
89
import org.ods.util.ILogger
910
import org.ods.util.PipelineSteps
1011
import org.ods.util.PodData
@@ -94,10 +95,16 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
9495

9596
logger.info "Rolling out ${context.componentId} with HELM, selector: ${options.selector}"
9697
helmUpgrade(context.targetProject)
98+
HelmStatusSimpleData helmStatus = openShift.helmStatus(context.targetProject, options.helmReleaseName)
99+
def helmStatusMap = helmStatus.toMap()
100+
def deploymentResources = getDeploymentResources(helmStatus)
97101

98-
def deploymentResources = openShift.getResourcesForComponent(
99-
context.targetProject, DEPLOYMENT_KINDS, options.selector
100-
)
102+
logger.info("${this.class.name} -- HELM STATUS")
103+
logger.info(
104+
JsonOutput.prettyPrint(
105+
JsonOutput.toJson(helmStatusMap)))
106+
107+
// not sure if we need both HELM STATUS and DEPLOYMENT RESOURCES"
101108
logger.info("${this.class.name} -- DEPLOYMENT RESOURCES")
102109
logger.info(
103110
JsonOutput.prettyPrint(
@@ -107,11 +114,12 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
107114
// // we assume that Helm does "Deployment" that should work for most
108115
// // cases since they don't have triggers.
109116
// metadataSvc.updateMetadata(false, deploymentResources)
110-
def rolloutData = getRolloutData(deploymentResources) ?: [:]
117+
def rolloutData = getRolloutData(helmStatus)
111118
logger.info(JsonOutput.prettyPrint(JsonOutput.toJson(rolloutData)))
112119
return rolloutData
113120
}
114121

122+
@SuppressWarnings(['UnnecessaryCast']) // otherwise IDE marked up helmUpgrade call
115123
private void helmUpgrade(String targetProject) {
116124
steps.dir(options.chartDir) {
117125
jenkins.maybeWithPrivateKeyCredentials(options.helmPrivateKeyCredentialsId) { String pkeyFile ->
@@ -124,7 +132,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
124132
options.helmValues['componentId'] = context.componentId
125133

126134
// we persist the original ones set from outside - here we just add ours
127-
Map mergedHelmValues = [:]
135+
Map mergedHelmValues = [:] as Map<String, String>
128136
mergedHelmValues << options.helmValues
129137

130138
// we add the global ones - this allows usage in subcharts
@@ -140,7 +148,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
140148
mergedHelmValues['global.imageTag'] = options.imageTag
141149

142150
// deal with dynamic value files - which are env dependent
143-
def mergedHelmValuesFiles = []
151+
def mergedHelmValuesFiles = [] as List<String>
144152
mergedHelmValuesFiles.addAll(options.helmValuesFiles)
145153

146154
options.helmEnvBasedValuesFiles.each { envValueFile ->
@@ -161,46 +169,66 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
161169
}
162170
}
163171

172+
private Map<String, List<String>> getDeploymentResources(HelmStatusSimpleData helmStatus) {
173+
helmStatus.getResourcesByKind(DEPLOYMENT_KINDS)
174+
}
175+
164176
// rollout returns a map like this:
165177
// [
166178
// 'DeploymentConfig/foo': [[podName: 'foo-a', ...], [podName: 'foo-b', ...]],
167179
// 'Deployment/bar': [[podName: 'bar-a', ...]]
168180
// ]
169181
@TypeChecked(TypeCheckingMode.SKIP)
170182
private Map<String, List<PodData>> getRolloutData(
171-
Map<String, List<String>> deploymentResources) {
172-
183+
HelmStatusSimpleData helmStatus
184+
) {
173185
def rolloutData = [:]
174-
deploymentResources.each { resourceKind, resourceNames ->
175-
resourceNames.each { resourceName ->
176-
def podData = []
177-
for (def i = 0; i < options.deployTimeoutRetries; i++) {
178-
podData = openShift.checkForPodData(context.targetProject, options.selector, resourceName)
179-
if (!podData.isEmpty()) {
180-
break
181-
}
182-
steps.echo("Could not find 'running' pod(s) with label '${options.selector}' - waiting")
183-
steps.sleep(12)
186+
helmStatus.resources.each { resource ->
187+
if (! (resource.kind in DEPLOYMENT_KINDS)) {
188+
return // continues with next
189+
}
190+
context.addDeploymentToArtifactURIs("${resource.name}-deploymentMean",
191+
[
192+
'type': 'helm',
193+
'selector': options.selector,
194+
'chartDir': options.chartDir,
195+
'helmReleaseName': options.helmReleaseName,
196+
'helmEnvBasedValuesFiles': options.helmEnvBasedValuesFiles,
197+
'helmValuesFiles': options.helmValuesFiles,
198+
'helmValues': options.helmValues,
199+
'helmDefaultFlags': options.helmDefaultFlags,
200+
'helmAdditionalFlags': options.helmAdditionalFlags,
201+
'helmStatus': helmStatus.toMap(),
202+
]
203+
)
204+
def podDataContext = [
205+
"targetProject=${context.targetProject}",
206+
"selector=${options.selector}",
207+
"name=${resource.name}",
208+
]
209+
def msgPodsNotFound = "Could not find 'running' pod(s) for '${podDataContext.join(', ')}'"
210+
List<PodData> podData = null
211+
for (def i = 0; i < options.deployTimeoutRetries; i++) {
212+
podData = openShift.checkForPodData(context.targetProject, options.selector, resource.name)
213+
if (podData) {
214+
break
184215
}
185-
context.addDeploymentToArtifactURIs("${resourceName}-deploymentMean",
186-
[
187-
'type': 'helm',
188-
'selector': options.selector,
189-
'chartDir': options.chartDir,
190-
'helmReleaseName': options.helmReleaseName,
191-
'helmEnvBasedValuesFiles': options.helmEnvBasedValuesFiles,
192-
'helmValuesFiles': options.helmValuesFiles,
193-
'helmValues': options.helmValues,
194-
'helmDefaultFlags': options.helmDefaultFlags,
195-
'helmAdditionalFlags': options.helmAdditionalFlags
196-
])
197-
rolloutData["${resourceKind}/${resourceName}"] = podData
198-
// TODO: Once the orchestration pipeline can deal with multiple replicas,
199-
// update this to store multiple pod artifacts.
200-
// TODO: Potential conflict if resourceName is duplicated between
201-
// Deployment and DeploymentConfig resource.
202-
context.addDeploymentToArtifactURIs(resourceName, podData[0]?.toMap())
216+
steps.echo("${msgPodsNotFound} - waiting")
217+
steps.sleep(12)
218+
}
219+
if (!podData) {
220+
throw new RuntimeException(msgPodsNotFound)
203221
}
222+
223+
logger.debug("Helm podData for ${podDataContext.join(', ')}: " +
224+
"${JsonOutput.prettyPrint(JsonOutput.toJson(podData))}")
225+
226+
rolloutData["${resource.kind}/${resource.name}"] = podData
227+
// TODO: Once the orchestration pipeline can deal with multiple replicas,
228+
// update this to store multiple pod artifacts.
229+
// TODO: Potential conflict if resourceName is duplicated between
230+
// Deployment and DeploymentConfig resource.
231+
context.addDeploymentToArtifactURIs(resource.name, podData[0]?.toMap())
204232
}
205233
rolloutData
206234
}

src/org/ods/orchestration/phases/DeployOdsComponent.groovy

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.ods.orchestration.phases
22

3+
import groovy.json.JsonOutput
34
import groovy.transform.TypeChecked
45
import groovy.transform.TypeCheckingMode
56
import org.ods.util.IPipelineSteps
@@ -11,6 +12,7 @@ import org.ods.services.GitService
1112
import org.ods.orchestration.util.DeploymentDescriptor
1213
import org.ods.orchestration.util.MROPipelineUtil
1314
import org.ods.orchestration.util.Project
15+
import org.ods.util.PodData
1416

1517
// Deploy ODS comnponent (code or service) to 'qa' or 'prod'.
1618
@TypeChecked
@@ -58,21 +60,29 @@ class DeployOdsComponent {
5860
applyTemplates(openShiftDir, deploymentMean)
5961

6062
def retries = project.environmentConfig?.openshiftRolloutTimeoutRetries ?: 10
61-
def podData = null
63+
def podDataContext = [
64+
"targetProject=${project.targetProject}",
65+
"selector=${deploymentMean.selector}",
66+
"name=${deploymentName}",
67+
]
68+
def msgPodsNotFound = "Could not find 'running' pod(s) for '${podDataContext.join(', ')}'"
69+
List<PodData> podData = null
6270
for (def i = 0; i < retries; i++) {
6371
podData = os.checkForPodData(project.targetProject, deploymentMean.selector, deploymentName)
6472
if (podData) {
6573
break
6674
}
67-
steps.echo("Could not find 'running' pod(s) with label '${deploymentMean.selector}' - waiting")
75+
steps.echo("${msgPodsNotFound} - waiting")
6876
steps.sleep(12)
6977
}
7078

7179
if (!podData) {
72-
throw new RuntimeException("Could not find 'running' pod(s) with label " +
73-
"'${deploymentMean.selector}'")
80+
throw new RuntimeException(msgPodsNotFound)
7481
}
7582

83+
logger.info("Helm podData for '${podDataContext.join(', ')}': " +
84+
"${JsonOutput.prettyPrint(JsonOutput.toJson(podData))}")
85+
7686
// TODO: Once the orchestration pipeline can deal with multiple replicas,
7787
// update this to deal with multiple pods.
7888
def pod = podData[0].toMap()
@@ -227,6 +237,15 @@ class DeployOdsComponent {
227237
deploymentMean.helmDefaultFlags,
228238
deploymentMean.helmAdditionalFlags,
229239
true)
240+
241+
def helmStatus = os. helmStatus(project.targetProject, deploymentMean.helmReleaseName)
242+
def helmStatusMap = helmStatus.toMap()
243+
deploymentMean.helmStatus = helmStatusMap
244+
logger.info("${this.class.name} -- HELM STATUS")
245+
logger.info(
246+
JsonOutput.prettyPrint(
247+
JsonOutput.toJson(helmStatusMap)))
248+
230249
}
231250
}
232251
jenkins.maybeWithPrivateKeyCredentials(secretName) { String pkeyFile ->

0 commit comments

Comments
 (0)