Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/tir rm dyn data add helm status #1143

Open
wants to merge 40 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
97463ee
TIR - remove dynamic pod data and surface helm status
henrjk Jul 1, 2024
449dd9e
Tests were previously not pushed.
henrjk Jul 1, 2024
5a0a021
Update CHANGELOG
henrjk Jul 2, 2024
0e23c03
Report HelmStatus first in TIR under release-deploymentStatus
henrjk Jul 2, 2024
555745c
TIR: render empty helm/tailor values as None or similar
henrjk Jul 4, 2024
31f4590
Introduce JsonLogUtil and exception tweaks
henrjk Jul 23, 2024
8ed9042
createTir keep helmStatus separate in template data
henrjk Jul 23, 2024
690e369
running into a cps issue
henrjk Jul 23, 2024
e8ee01a
non helm deployment fix
henrjk Jul 23, 2024
f6fbcb6
non helm deployment wording changes
henrjk Jul 24, 2024
4fe044f
Remove detailed helm status parsing and rename same fields.
henrjk Jul 24, 2024
df43fe3
Helm status data to copy resourcesByKind
henrjk Jul 24, 2024
0570dfe
Smaller tweaks
henrjk Jul 24, 2024
9287243
Add @NonCPS in HelmStatusSimpleData
henrjk Jul 24, 2024
bd54a5f
createTir keep helmStatus separate as deployment in template data
henrjk Jul 24, 2024
e9c6eb9
createTir empty map for deployment appears to evaluate as truthy in t…
henrjk Jul 25, 2024
7db11b4
human format for helm status info
henrjk Jul 26, 2024
0c29dab
remove newline embedding experiment - none worked
henrjk Jul 26, 2024
c762b3b
Adjust changelog for new PR
henrjk Jul 26, 2024
51a99f6
chore: TIR HTML formatting and helm tables reformatted
anteloro-boeh Oct 14, 2024
73129a7
fix: CodeNarc rules compliance
anteloro-boeh Oct 14, 2024
673adb8
fix: TIR spec tests failing due to change in mean and status structures
anteloro-boeh Oct 14, 2024
d85eb63
Merge branch 'master' into feature/tir-rm-dyn-data-add-helm-status
anteloro-boeh Oct 14, 2024
b6e4c34
chore: merge with master
anteloro-boeh Oct 14, 2024
c93da50
chore: merge with master
anteloro-boeh Oct 15, 2024
ec961de
feature: TIR removed Name, Instance and Version rows from Deployment …
anteloro-boeh Oct 15, 2024
eefbfe5
chore: added unit tests for html formatter, and fixed PR reported issues
Oct 18, 2024
97ea254
Merge branch 'master' into feature/tir-rm-dyn-data-add-helm-status
anteloro-boeh Oct 18, 2024
9eda979
fix: fixed bad merge on CHANGELOG
Oct 18, 2024
aab5444
Improve the organization of the data for the TIR
jafarre-bi Oct 21, 2024
9160bf2
chore: deployment strategies unit tests, test data anonymization, som…
anteloro-boeh Nov 15, 2024
8e54e54
chore: merge with master
anteloro-boeh Nov 15, 2024
b8c64c6
chore: fixed CodeNarc reported blocking issues
anteloro-boeh Nov 18, 2024
ff3d38e
chore: fixed more CodeNarc reported blocking issues
anteloro-boeh Nov 18, 2024
d7959e0
fix: wrong per-env config files in helm upgrade commands
anteloro-boeh Nov 21, 2024
dea6ab4
chore: manual comparison with master branch in order to find regressions
anteloro-boeh Nov 22, 2024
2ecabf7
chore: merge with master
anteloro-boeh Nov 22, 2024
939bed5
fix: incorrect namespaces for test and prod on TIR table
anteloro-boeh Nov 22, 2024
cf250b5
chore: removed unneeded FIXME and TODO, wrap log.debug()
anteloro-boeh Nov 28, 2024
19447f3
chore: CodeNarc reported issue, reduced cyclomatic complexity to fix it
anteloro-boeh Nov 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Add preserve-digests cli option to skopeo copy command in CopyImageStage ([#1166](https://github.com/opendevstack/ods-jenkins-shared-library/issues/1166))
* Allow registry/image:tag sources in CopyImageStage instead of directly falling back to internal registry ([#1177](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1177))
* Simplify successor management since now issue links are no longer inherited ([#1116](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1116))
* TIR - remove dynamic pod data, surface helm status and helm report tables reformatting ([#1143](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1143))

### Fixed
* Fix Tailor deployment drifts for D, Q envs ([#1055](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1055))
Expand Down
3 changes: 3 additions & 0 deletions src/org/ods/component/AbstractDeploymentStrategy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ abstract class AbstractDeploymentStrategy implements IDeploymentStrategy {
@Override
abstract Map<String, List<PodData>> deploy()

// Fetches original kubernetes revisions of deployment resources.
//
// returns a two level map with keys resourceKind -> resourceName -> revision (int)
protected Map<String, Map<String, Integer>> fetchOriginalVersions(Map<String, List<String>> deploymentResources) {
def originalVersions = [:]
deploymentResources.each { resourceKind, resourceNames ->
Expand Down
113 changes: 63 additions & 50 deletions src/org/ods/component/HelmDeploymentStrategy.groovy
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
package org.ods.component

import groovy.json.JsonOutput
import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import org.ods.services.JenkinsService
import org.ods.services.OpenShiftService
import org.ods.util.HelmStatus
import org.ods.util.ILogger
import org.ods.util.PipelineSteps
import org.ods.util.IPipelineSteps
import org.ods.util.PodData

class HelmDeploymentStrategy extends AbstractDeploymentStrategy {

// Constructor arguments
private final Script script
private final IContext context
private final OpenShiftService openShift
private final JenkinsService jenkins
private final ILogger logger

private IPipelineSteps steps
// assigned in constructor
private def steps
private final RolloutOpenShiftDeploymentOptions options

@SuppressWarnings(['AbcMetric', 'CyclomaticComplexity', 'ParameterCount'])
HelmDeploymentStrategy(
def script,
IPipelineSteps steps,
IContext context,
Map<String, Object> config,
OpenShiftService openShift,
JenkinsService jenkins,
ILogger logger
) {

if (!config.selector) {
config.selector = context.selector
}
Expand Down Expand Up @@ -72,10 +69,9 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
if (!config.helmPrivateKeyCredentialsId) {
config.helmPrivateKeyCredentialsId = "${context.cdProject}-helm-private-key"
}
this.script = script
this.context = context
this.logger = logger
this.steps = new PipelineSteps(script)
this.steps = steps

this.options = new RolloutOpenShiftDeploymentOptions(config)
this.openShift = openShift
Expand All @@ -92,23 +88,19 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
// Tag images which have been built in this pipeline from cd project into target project
retagImages(context.targetProject, getBuiltImages())

logger.info "Rolling out ${context.componentId} with HELM, selector: ${options.selector}"
logger.info("Rolling out ${context.componentId} with HELM, selector: ${options.selector}")
helmUpgrade(context.targetProject)

def deploymentResources = openShift.getResourcesForComponent(
context.targetProject, DEPLOYMENT_KINDS, options.selector
)
logger.info("${this.class.name} -- DEPLOYMENT RESOURCES")
logger.info(
JsonOutput.prettyPrint(
JsonOutput.toJson(deploymentResources)))
HelmStatus helmStatus = openShift.helmStatus(context.targetProject, options.helmReleaseName)
if (logger.debugMode) {
def helmStatusMap = helmStatus.toMap()
logger.debug("${this.class.name} -- HELM STATUS: ${helmStatusMap}")
}

// // FIXME: pauseRollouts is non trivial to determine!
// // we assume that Helm does "Deployment" that should work for most
// // cases since they don't have triggers.
// metadataSvc.updateMetadata(false, deploymentResources)
def rolloutData = getRolloutData(deploymentResources) ?: [:]
logger.info(JsonOutput.prettyPrint(JsonOutput.toJson(rolloutData)))
def rolloutData = getRolloutData(helmStatus)
return rolloutData
}

Expand All @@ -124,7 +116,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
options.helmValues['componentId'] = context.componentId

// we persist the original ones set from outside - here we just add ours
Map mergedHelmValues = [:]
Map<String, String> mergedHelmValues = [:]
mergedHelmValues << options.helmValues

// we add the global ones - this allows usage in subcharts
Expand All @@ -141,13 +133,14 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {

// deal with dynamic value files - which are env dependent
def mergedHelmValuesFiles = []
mergedHelmValuesFiles.addAll(options.helmValuesFiles)

options.helmEnvBasedValuesFiles.each { envValueFile ->
mergedHelmValuesFiles << envValueFile.replace('.env',
".${context.environment}")
def envConfigFiles = options.helmEnvBasedValuesFiles.collect {filenamePattern ->
filenamePattern.replace('.env.', ".${context.environment}.")
}

mergedHelmValuesFiles.addAll(options.helmValuesFiles)
mergedHelmValuesFiles.addAll(envConfigFiles)

openShift.helmUpgrade(
targetProject,
options.helmReleaseName,
Expand All @@ -168,41 +161,61 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
// ]
@TypeChecked(TypeCheckingMode.SKIP)
private Map<String, List<PodData>> getRolloutData(
Map<String, List<String>> deploymentResources) {
HelmStatus helmStatus
) {
// TODO This is still needed for several reasons, still pending refactoring in order to
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
// remove the need for pod data
Map<String, List<PodData>> rolloutData = [:]

def rolloutData = [:]
deploymentResources.each { resourceKind, resourceNames ->
resourceNames.each { resourceName ->
def podData = []
Map<String, List<String>> deploymentKinds = helmStatus.resourcesByKind
.findAll { kind, res -> kind in DEPLOYMENT_KINDS }

deploymentKinds.each { kind, names ->
names.each { name ->
context.addDeploymentToArtifactURIs("${name}-deploymentMean",
[
type: 'helm',
selector: options.selector,
namespace: context.targetProject,
chartDir: options.chartDir,
helmReleaseName: options.helmReleaseName,
helmEnvBasedValuesFiles: options.helmEnvBasedValuesFiles,
helmValuesFiles: options.helmValuesFiles,
helmValues: options.helmValues,
helmDefaultFlags: options.helmDefaultFlags,
helmAdditionalFlags: options.helmAdditionalFlags,
helmStatus: helmStatus.toMap(),
]
)
def podDataContext = [
"targetProject=${context.targetProject}",
"selector=${options.selector}",
"name=${name}",
]
def msgPodsNotFound = "Could not find 'running' pod(s) for '${podDataContext.join(', ')}'"
List<PodData> podData = null
for (def i = 0; i < options.deployTimeoutRetries; i++) {
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
podData = openShift.checkForPodData(context.targetProject, options.selector, resourceName)
if (!podData.isEmpty()) {
podData = openShift.checkForPodData(context.targetProject, options.selector, name)
anteloro-boeh marked this conversation as resolved.
Show resolved Hide resolved
if (podData) {
break
}
steps.echo("Could not find 'running' pod(s) with label '${options.selector}' - waiting")
steps.echo("${msgPodsNotFound} - waiting")
steps.sleep(12)
}
context.addDeploymentToArtifactURIs("${resourceName}-deploymentMean",
[
'type': 'helm',
'selector': options.selector,
'chartDir': options.chartDir,
'helmReleaseName': options.helmReleaseName,
'helmEnvBasedValuesFiles': options.helmEnvBasedValuesFiles,
'helmValuesFiles': options.helmValuesFiles,
'helmValues': options.helmValues,
'helmDefaultFlags': options.helmDefaultFlags,
'helmAdditionalFlags': options.helmAdditionalFlags,
])
rolloutData["${resourceKind}/${resourceName}"] = podData
if (!podData) {
throw new RuntimeException(msgPodsNotFound)
}
logger.debug("Helm podData for ${podDataContext.join(', ')}: ${podData}")
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved

rolloutData["${kind}/${name}"] = podData
// TODO: Once the orchestration pipeline can deal with multiple replicas,
// update this to store multiple pod artifacts.
// update this to store multiple pod artifacts.
// TODO: Potential conflict if resourceName is duplicated between
// Deployment and DeploymentConfig resource.
context.addDeploymentToArtifactURIs(resourceName, podData[0]?.toMap())
// Deployment and DeploymentConfig resource.
context.addDeploymentToArtifactURIs(name, podData[0]?.toMap())
}
}
rolloutData
return rolloutData
}

}
16 changes: 9 additions & 7 deletions src/org/ods/component/RolloutOpenShiftDeploymentStage.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package org.ods.component

import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import org.ods.services.OpenShiftService
import org.ods.services.JenkinsService
import org.ods.services.OpenShiftService
import org.ods.util.ILogger
import org.ods.util.PipelineSteps

@SuppressWarnings('ParameterCount')
@TypeChecked
Expand Down Expand Up @@ -110,26 +111,27 @@ class RolloutOpenShiftDeploymentStage extends Stage {
// We have to move everything here,
// otherwise Jenkins will complain
// about: "hudson.remoting.ProxyException: CpsCallableInvocation{methodName=fileExists, ..."
def isHelmDeployment = steps.fileExists(options.chartDir + '/Chart.yaml')
def isHelmDeployment = this.steps.fileExists(options.chartDir + '/Chart.yaml')
logger.info("isHelmDeployment: ${isHelmDeployment}")
def isTailorDeployment = steps.fileExists(options.openshiftDir)
def isTailorDeployment = this.steps.fileExists(options.openshiftDir)

if (isTailorDeployment && isHelmDeployment) {
steps.error("Must be either a Tailor based deployment or a Helm based deployment")
this.steps.error("Must be either a Tailor based deployment or a Helm based deployment")
throw new IllegalStateException("Must be either a Tailor based deployment or a Helm based deployment")
}

// Use tailorDeployment in the following cases:
// (1) We have an openshiftDir
// (2) We do not have an openshiftDir but neither do we have an indication that it is Helm
def steps = new PipelineSteps(script)
if (isTailorDeployment || (!isHelmDeployment && !isTailorDeployment)) {
deploymentStrategy = new TailorDeploymentStrategy(script, context, config, openShift, jenkins, logger)
deploymentStrategy = new TailorDeploymentStrategy(steps, context, config, openShift, jenkins, logger)
String resourcePath = 'org/ods/component/RolloutOpenShiftDeploymentStage.deprecate-tailor.GString.txt'
def msg =steps.libraryResource(resourcePath)
def msg = this.steps.libraryResource(resourcePath)
logger.warn(msg)
}
if (isHelmDeployment) {
deploymentStrategy = new HelmDeploymentStrategy(script, context, config, openShift, jenkins, logger)
deploymentStrategy = new HelmDeploymentStrategy(steps, context, config, openShift, jenkins, logger)
}
logger.info("deploymentStrategy: ${deploymentStrategy} -- ${deploymentStrategy.class.name}")
return deploymentStrategy.deploy()
Expand Down
10 changes: 4 additions & 6 deletions src/org/ods/component/TailorDeploymentStrategy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,24 @@ import groovy.transform.TypeCheckingMode
import org.ods.services.JenkinsService
import org.ods.services.OpenShiftService
import org.ods.util.ILogger
import org.ods.util.PipelineSteps
import org.ods.util.IPipelineSteps
import org.ods.util.PodData

class TailorDeploymentStrategy extends AbstractDeploymentStrategy {

// Constructor arguments
private final Script script
private final IContext context
private final OpenShiftService openShift
private final JenkinsService jenkins
private final ILogger logger
private final IPipelineSteps steps

// assigned in constructor
private def steps
private final RolloutOpenShiftDeploymentOptions options

@SuppressWarnings(['AbcMetric', 'CyclomaticComplexity', 'ParameterCount'])
TailorDeploymentStrategy(
def script,
IPipelineSteps steps,
IContext context,
Map<String, Object> config,
OpenShiftService openShift,
Expand Down Expand Up @@ -67,10 +66,9 @@ class TailorDeploymentStrategy extends AbstractDeploymentStrategy {
if (!config.containsKey('tailorParams')) {
config.tailorParams = []
}
this.script = script
this.context = context
this.logger = logger
this.steps = new PipelineSteps(script)
this.steps = steps

this.options = new RolloutOpenShiftDeploymentOptions(config)
this.openShift = openShift
Expand Down
25 changes: 21 additions & 4 deletions src/org/ods/orchestration/phases/DeployOdsComponent.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.ods.services.GitService
import org.ods.orchestration.util.DeploymentDescriptor
import org.ods.orchestration.util.MROPipelineUtil
import org.ods.orchestration.util.Project
import org.ods.util.PodData

// Deploy ODS comnponent (code or service) to 'qa' or 'prod'.
@TypeChecked
Expand Down Expand Up @@ -39,7 +40,11 @@ class DeployOdsComponent {

DeploymentDescriptor deploymentDescriptor
steps.dir(openShiftDir) {
// FIXME This is not correct, when deploying on test and prod envs, the deployment descriptor
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
// read from file contains deploymentMean.namespace=xxx-dev, which doesn't match the target namespace,
// which should end in either -test or -prod
deploymentDescriptor = DeploymentDescriptor.readFromFile(steps)
logger.debug("DeploymentDescriptor '${openShiftDir}': ${deploymentDescriptor.deployments}")
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
}
if (!repo.data.openshift.deployments) {
repo.data.openshift.deployments = [:]
Expand All @@ -54,24 +59,31 @@ class DeployOdsComponent {
Map deploymentMean = deployment.deploymentMean
logger.debug("Helm Config for ${deploymentName} -> ${deploymentMean}")
deploymentMean['repoId'] = repo.id
deploymentMean['namespace'] = project.targetProject

applyTemplates(openShiftDir, deploymentMean)

def retries = project.environmentConfig?.openshiftRolloutTimeoutRetries ?: 10
def podData = null
def podDataContext = [
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
"targetProject=${project.targetProject}",
"selector=${deploymentMean.selector}",
"name=${deploymentName}",
]
def msgPodsNotFound = "Could not find 'running' pod(s) for '${podDataContext.join(', ')}'"
List<PodData> podData = null
for (def i = 0; i < retries; i++) {
podData = os.checkForPodData(project.targetProject, deploymentMean.selector, deploymentName)
if (podData) {
break
}
steps.echo("Could not find 'running' pod(s) with label '${deploymentMean.selector}' - waiting")
steps.echo("${msgPodsNotFound} - waiting")
steps.sleep(12)
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
}

if (!podData) {
throw new RuntimeException("Could not find 'running' pod(s) with label " +
"'${deploymentMean.selector}'")
throw new RuntimeException(msgPodsNotFound)
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
}
logger.debug("Helm podData for '${podDataContext.join(', ')}': ${podData}")

// TODO: Once the orchestration pipeline can deal with multiple replicas,
// update this to deal with multiple pods.
Expand Down Expand Up @@ -227,6 +239,11 @@ class DeployOdsComponent {
deploymentMean.helmDefaultFlags,
deploymentMean.helmAdditionalFlags,
true)

def helmStatus = os.helmStatus(project.targetProject, deploymentMean.helmReleaseName)
jafarre-bi marked this conversation as resolved.
Show resolved Hide resolved
def helmStatusMap = helmStatus.toMap()
deploymentMean.helmStatus = helmStatusMap
logger.debug("${this.class.name} -- HELM STATUS: ${helmStatusMap}")
}
}
jenkins.maybeWithPrivateKeyCredentials(secretName) { String pkeyFile ->
Expand Down
Loading