Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e1b726b
image stream not supported by EKS
s2oBCN Jul 31, 2025
94f048f
EKSLoginStage
s2oBCN Jul 31, 2025
4dad41c
EKSLoginStage
s2oBCN Jul 31, 2025
428ed42
config Options
s2oBCN Jul 31, 2025
78282f8
RolloutOpenShiftDeploymentOptions
s2oBCN Jul 31, 2025
0aea4b1
prepare for imageCopy
s2oBCN Jul 31, 2025
af1926c
this.
s2oBCN Jul 31, 2025
e46695e
AWS_ACCESS_KEY_ID
s2oBCN Jul 31, 2025
487ce86
eks deployment
s2oBCN Aug 4, 2025
693f2ea
ImageRepositoryEKS
s2oBCN Aug 4, 2025
f3b3745
EKSLoginStage
s2oBCN Aug 4, 2025
ba3f338
EKSService
s2oBCN Aug 4, 2025
7a71a93
copyImage
s2oBCN Aug 4, 2025
65df229
org.ods.services.EKSService
s2oBCN Aug 4, 2025
bd8aeb7
services
s2oBCN Aug 4, 2025
57703af
retagImages
s2oBCN Aug 4, 2025
1dbcf05
public
s2oBCN Aug 4, 2025
6fbad4b
NonCPS
s2oBCN Aug 4, 2025
ddd5a17
DeploymentConfig
s2oBCN Aug 4, 2025
bbe904d
deploymentConfig
s2oBCN Aug 4, 2025
f5cc778
DeploymentConfig
s2oBCN Aug 4, 2025
49dad0c
envPath
s2oBCN Aug 4, 2025
ef89a36
awsEnvironmentVars
s2oBCN Aug 4, 2025
1e211d5
ImageRepositoryEKS
s2oBCN Aug 4, 2025
b726eac
copyImage
s2oBCN Aug 4, 2025
6c17c47
skopeo copy
s2oBCN Aug 4, 2025
82e0e61
account
s2oBCN Aug 4, 2025
b7587ee
aws eks update-kubeconfig
s2oBCN Aug 28, 2025
65a4a0e
gaia-newdev
s2oBCN Aug 28, 2025
0d14312
show eks
s2oBCN Aug 28, 2025
ed3093d
retagImages
s2oBCN Aug 28, 2025
815c165
createRepository and namespace
s2oBCN Aug 29, 2025
9c74659
retagImages
s2oBCN Aug 30, 2025
82cbc50
executeCommand
s2oBCN Aug 30, 2025
345e8a3
Add namespace override support in helmUpgrade method
s2oBCN Oct 31, 2025
1891a25
Enhance deploy method to support namespace override for image retaggi…
s2oBCN Nov 3, 2025
305af1d
Refactor getRolloutData method to accept targetProject parameter for …
s2oBCN Nov 3, 2025
d3b913d
Fix parsePodJson method to handle 'Running' status in addition to 'ru…
s2oBCN Dec 5, 2025
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
17 changes: 0 additions & 17 deletions src/org/ods/component/AbstractDeploymentStrategy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,6 @@ abstract class AbstractDeploymentStrategy implements IDeploymentStrategy {
originalVersions
}

protected findOrCreateImageStream(String targetProject, String image) {
try {
openShift.findOrCreateImageStream(targetProject, image)
} catch (Exception ex) {
steps.error "Could not find/create ImageStream ${image} in ${targetProject}. Error was: ${ex}"
}
}

protected void retagImages(String targetProject, Set<String> images) {
images.each { image ->
findOrCreateImageStream(targetProject, image)
openShift.importImageTagFromProject(
targetProject, image, context.cdProject, options.imageTag, options.imageTag
)
}
}

@TypeChecked(TypeCheckingMode.SKIP)
protected Set<String> getBuiltImages() {
context.buildArtifactURIs.builds.keySet().findAll { it ->
Expand Down
78 changes: 78 additions & 0 deletions src/org/ods/component/DeploymentConfig.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

package org.ods.component

class DeploymentConfig {

public void updateCommonConfig(IContext context, Map<String, Object> config) {
if (!config.selector) {
config.selector = context.selector
}
if (!config.imageTag) {
config.imageTag = context.shortGitCommit
}
if (!config.deployTimeoutMinutes) {
config.deployTimeoutMinutes = context.openshiftRolloutTimeout ?: 15
}
if (!config.deployTimeoutRetries) {
config.deployTimeoutRetries = context.openshiftRolloutTimeoutRetries ?: 5
}
}

public void updateHelmConfig(IContext context, Map<String, Object> config){
if (!config.chartDir) {
config.chartDir = 'chart'
}
if (!config.containsKey('helmReleaseName')) {
config.helmReleaseName = context.componentId
}
if (!config.containsKey('helmValues')) {
config.helmValues = [:]
}
if (!config.containsKey('helmValuesFiles')) {
config.helmValuesFiles = [ 'values.yaml' ]
}
if (!config.containsKey('helmEnvBasedValuesFiles')) {
config.helmEnvBasedValuesFiles = []
}
if (!config.containsKey('helmDefaultFlags')) {
config.helmDefaultFlags = ['--install', '--atomic']
}
if (!config.containsKey('helmAdditionalFlags')) {
config.helmAdditionalFlags = []
}
if (!config.containsKey('helmDiff')) {
config.helmDiff = true
}
if (!config.helmPrivateKeyCredentialsId) {
config.helmPrivateKeyCredentialsId = "${context.cdProject}-helm-private-key"
}
}

@NonCPS
public static void updateTailorConfig(IContext context, Map<String, Object> config) {
if (!config.openshiftDir) {
config.openshiftDir = 'openshift'
}
if (!config.tailorPrivateKeyCredentialsId) {
config.tailorPrivateKeyCredentialsId = "${context.cdProject}-tailor-private-key"
}
if (!config.tailorSelector) {
config.tailorSelector = config.selector
}
if (!config.containsKey('tailorVerify')) {
config.tailorVerify = true
}
if (!config.containsKey('tailorExclude')) {
config.tailorExclude = 'bc,is'
}
if (!config.containsKey('tailorParamFile')) {
config.tailorParamFile = '' // none apart the automatic param file
}
if (!config.containsKey('tailorPreserve')) {
config.tailorPreserve = [] // do not preserve any paths in live config
}
if (!config.containsKey('tailorParams')) {
config.tailorParams = []
}
}
}
46 changes: 27 additions & 19 deletions src/org/ods/component/HelmDeploymentStrategy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
private final JenkinsService jenkins
private final ILogger logger
private final IPipelineSteps steps
private final IImageRepository imageRepository

// assigned in constructor
private final RolloutOpenShiftDeploymentOptions options

Expand All @@ -28,8 +30,13 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
Map<String, Object> config,
OpenShiftService openShift,
JenkinsService jenkins,
IImageRepository imageRepository,
ILogger logger
) {
// TODO
// DeploymentConfig deploymentConfig = new DeploymentConfig()
// deploymentConfig.updateCommonConfig(context, config)
// deploymentConfig.updateHelmConfig(context, config)
if (!config.selector) {
config.selector = context.selector
}
Expand All @@ -42,7 +49,6 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
if (!config.deployTimeoutRetries) {
config.deployTimeoutRetries = context.openshiftRolloutTimeoutRetries ?: 5
}
// Helm options
if (!config.chartDir) {
config.chartDir = 'chart'
}
Expand All @@ -53,7 +59,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
config.helmValues = [:]
}
if (!config.containsKey('helmValuesFiles')) {
config.helmValuesFiles = ['values.yaml']
config.helmValuesFiles = [ 'values.yaml' ]
}
if (!config.containsKey('helmEnvBasedValuesFiles')) {
config.helmEnvBasedValuesFiles = []
Expand All @@ -70,28 +76,31 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
if (!config.helmPrivateKeyCredentialsId) {
config.helmPrivateKeyCredentialsId = "${context.cdProject}-helm-private-key"
}

this.context = context
this.logger = logger
this.steps = steps

this.options = new RolloutOpenShiftDeploymentOptions(config)
this.openShift = openShift
this.jenkins = jenkins
this.imageRepository = imageRepository
}

@Override
Map<String, List<PodData>> deploy() {
if (!context.environment) {
logger.warn 'Skipping because of empty (target) environment ...'
return [:]
Map<String, List<PodData>> deploy() {
// Maybe we need to deploy to another namespace (ie we want to deploy a monitoring stack into a specific namespace)
def targetProject = context.targetProject
if (options.helmValues['namespaceOverride']) {
targetProject = options.helmValues['namespaceOverride']
logger.info("Override namespace deployment to ${targetProject} ")
}

// Tag images which have been built in this pipeline from cd project into target project
retagImages(context.targetProject, getBuiltImages())
logger.info("Retagging images for ${targetProject} ")
imageRepository.retagImages(targetProject, getBuiltImages(), options.imageTag, options.imageTag)

logger.info("Rolling out ${context.componentId} with HELM, selector: ${options.selector}")
helmUpgrade(context.targetProject)
HelmStatus helmStatus = openShift.helmStatus(context.targetProject, options.helmReleaseName)
helmUpgrade(targetProject)
HelmStatus helmStatus = openShift.helmStatus(targetProject, options.helmReleaseName)
if (logger.debugMode) {
def helmStatusMap = helmStatus.toMap()
logger.debug("${this.class.name} -- HELM STATUS: ${helmStatusMap}")
Expand All @@ -101,17 +110,16 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
// // 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(helmStatus)
def rolloutData = getRolloutData(helmStatus, targetProject)
return rolloutData
}

private void helmUpgrade(String targetProject) {
private void helmUpgrade(String targetProject) {
steps.dir(options.chartDir) {
jenkins.maybeWithPrivateKeyCredentials(options.helmPrivateKeyCredentialsId) { String pkeyFile ->
if (pkeyFile) {
steps.sh(script: "gpg --import ${pkeyFile}", label: 'Import private key into keyring')
}

}
// we add two things persistent - as these NEVER change (and are env independent)
options.helmValues['registry'] = context.clusterRegistryAddress
options.helmValues['componentId'] = context.componentId
Expand Down Expand Up @@ -162,7 +170,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
// ]
@TypeChecked(TypeCheckingMode.SKIP)
private Map<String, List<PodData>> getRolloutData(
HelmStatus helmStatus
HelmStatus helmStatus, String targetProject
) {
Map<String, List<PodData>> rolloutData = [:]

Expand All @@ -175,7 +183,7 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
[
type: 'helm',
selector: options.selector,
namespace: context.targetProject,
namespace: targetProject,
chartDir: options.chartDir,
helmReleaseName: options.helmReleaseName,
helmEnvBasedValuesFiles: options.helmEnvBasedValuesFiles,
Expand All @@ -187,14 +195,14 @@ class HelmDeploymentStrategy extends AbstractDeploymentStrategy {
]
)
def podDataContext = [
"targetProject=${context.targetProject}",
"targetProject=${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++) {
podData = openShift.checkForPodData(context.targetProject, options.selector, name)
podData = openShift.checkForPodData(targetProject, options.selector, name)
if (podData) {
break
}
Expand Down
7 changes: 7 additions & 0 deletions src/org/ods/component/IImageRepository.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ods.component

interface IImageRepository {

void retagImages(String targetProject, Set<String> images, String sourceTag, String targetTag)

}
56 changes: 56 additions & 0 deletions src/org/ods/component/ImageRepositoryEKS.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.ods.component

import org.ods.services.EKSService
import org.ods.util.IPipelineSteps
import org.ods.component.IContext

class ImageRepositoryEKS implements IImageRepository {

// Constructor arguments
private final IPipelineSteps steps
private final IContext context
private final EKSService eks

private final String ocToken
private final String awsPassword

@SuppressWarnings(['AbcMetric', 'CyclomaticComplexity', 'ParameterCount'])
ImageRepositoryEKS(
IPipelineSteps steps,
IContext context,
EKSService eks,
String ocToken,
String awsPassword
) {
this.steps = steps
this.context = context
this.eks = eks
this.ocToken = ocToken
this.awsPassword = awsPassword
}

public void retagImages(String targetProject, Set<String> images, String sourceTag, String targetTag) {
images.each { image ->
eks.createRepository(image)
copyImage(image, context, sourceTag, targetTag)
}
}

private int copyImage(image, context, sourceTag, targetTag) {
String ocCredentials="jenkins:$ocToken"
String awsCredentials="AWS:$awsPassword"
String dockerSource="docker://${context.config.dockerRegistry}/${context.cdProject}/${image}:${sourceTag}"
String awsTarget="docker://${eks.getECRRegistry()}/${image}:${targetTag}"

return steps.sh(
script: """
skopeo copy \
--src-tls-verify=false --src-creds "${ocCredentials}"\
--dest-tls-verify=false --dest-creds "${awsCredentials}"\
$dockerSource $awsTarget
""",
returnStatus: true,
label: "Copy image to awsTarget ${awsTarget}"
) as int
}
}
40 changes: 40 additions & 0 deletions src/org/ods/component/ImageRepositoryOpenshift.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.ods.component

import org.ods.services.OpenShiftService
import org.ods.util.IPipelineSteps

class ImageRepositoryOpenshift implements IImageRepository {

// Constructor arguments
private final IPipelineSteps steps
private final IContext context
private final OpenShiftService openShift

@SuppressWarnings(['AbcMetric', 'CyclomaticComplexity', 'ParameterCount'])
ImageRepositoryOpenshift(
IPipelineSteps steps,
IContext context,
OpenShiftService openShift
) {
this.steps = steps
this.context = context
this.openShift = openShift
}

public void retagImages(String targetProject, Set<String> images, String sourceTag, String targetTag) {
images.each { image ->
findOrCreateImageStream(targetProject, image)
openShift.importImageTagFromProject(
targetProject, image, context.cdProject, sourceTag, targetTag
)
}
}

private findOrCreateImageStream(String targetProject, String image) {
try {
openShift.findOrCreateImageStream(targetProject, image)
} catch (Exception ex) {
steps.error "Could not find/create ImageStream ${image} in ${targetProject}. Error was: ${ex}"
}
}
}
Loading
Loading