Skip to content

Commit bc86674

Browse files
authored
Merge pull request #30 from cloudogu/feature/local-helm-repo
Feature/local helm repo
2 parents 9cd5d0f + f5ee471 commit bc86674

File tree

13 files changed

+174
-46
lines changed

13 files changed

+174
-46
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.4.0](https://github.com/cloudogu/gitops-build-lib/releases/tag/0.4.0) - 2023-03-21
11+
12+
### Added
13+
14+
- Helm Repo Type `LOCAL`
15+
1016
## [0.3.0](https://github.com/cloudogu/gitops-build-lib/releases/tag/0.3.0) - 2023-03-21
1117

1218
### Added

README.md

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ Or if you want to chat with us about gitops in general, visit us [here](https://
3333
- [Deployment](#deployment)
3434
- [Plain k8s deployment](#plain-k8s-deployment)
3535
- [Helm deployment](#helm-deployment)
36+
- [Helm Chart from Helm Repository](#helm-chart-from-helm-repository)
37+
- [Helm Chart from Git Repository](#helm-chart-from-git-repository)
38+
- [Local Helm Chart ](#local-helm-chart)
3639
- [Conventions for helm deployment](#conventions-for-helm-deployment)
3740
- [`helm template` with ArgoCD application](#helm-template-with-argocd-application)
3841
- [Folder Structure in destination gitops repository](#folder-structure-in-destination-gitops-repository)
@@ -563,8 +566,16 @@ In plain pipelines, the library creates the deployment resources by updating the
563566
---
564567

565568
### Helm deployment
566-
Besides plain k8s resources you can also use helm charts to generate the resources. You can choose between two types of
567-
helm-repository-types. First of all there is the `repoType: HELM`, which is used to load tgz from helm-repositories.
569+
Besides plain k8s resources you can also use helm charts to generate the resources. You can choose between these types
570+
of helm-repository-types:
571+
572+
* Helm Repository
573+
* Git Repository
574+
* Local
575+
576+
577+
#### Helm Chart from Helm Repository
578+
The `repoType: HELM`, which is used to load tgz from helm-repositories.
568579

569580
```groovy
570581
def gitopsConfig = [
@@ -581,7 +592,8 @@ def gitopsConfig = [
581592
]
582593
```
583594

584-
Then there is `repoType: GIT` - which can be used to load charts from a specific Git-Repository.
595+
#### Helm Chart from Git Repository
596+
The `repoType: GIT` can be used to load charts from a specific Git-Repository.
585597

586598
```groovy
587599
def gitopsConfig = [
@@ -605,6 +617,23 @@ git client checkout the default branch given within the HEADs meta information.
605617
`main` branch. Make sure the git-repository has a main-branch, else the deployment step will fail. After a successful clone
606618
it checks out the given version as expected.
607619

620+
#### Local Helm Chart
621+
The `repoType: LOCAL` can be used if you store the helm repository locally, i.e. in the same Git Repository where your Jenkins Job runs from.
622+
A common pattern used in this context would be the "Umbrella pattern".
623+
624+
```groovy
625+
def gitopsConfig = [
626+
deployments: [
627+
helm : [
628+
repoType : 'LOCAL',
629+
chartPath: 'helm/chart' /* Must be relative to jenkins WORKSPACE */,
630+
updateValues: [[fieldPath: "image.name", newValue: imageName]]
631+
]
632+
]
633+
]
634+
```
635+
636+
608637
#### Conventions for helm deployment
609638
- Application name is used as the release-name in Flux (not for argo, argo creates plain resources using `helm template`)
610639
> Helm Release name = `${application}`

src/com/cloudogu/gitopsbuildlib/deployment/helm/Helm.groovy

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,19 @@ import com.cloudogu.gitopsbuildlib.deployment.SourceType
55
import com.cloudogu.gitopsbuildlib.deployment.helm.helmrelease.ArgoCDRelease
66
import com.cloudogu.gitopsbuildlib.deployment.helm.helmrelease.FluxV1Release
77
import com.cloudogu.gitopsbuildlib.deployment.helm.helmrelease.HelmRelease
8-
import com.cloudogu.gitopsbuildlib.deployment.helm.repotype.GitRepo
9-
import com.cloudogu.gitopsbuildlib.deployment.helm.repotype.HelmRepo
108
import com.cloudogu.gitopsbuildlib.deployment.helm.repotype.RepoType
119

1210
class Helm extends Deployment {
1311

14-
protected RepoType chartRepo
1512
protected HelmRelease helmRelease
1613

17-
private String helmChartTempDir = ".helmChartTempDir"
18-
private String chartRootDir = "chart"
14+
public static final String HELM_CHART_TEMP_DIR = ".helmChartTempDir"
15+
public static final String CHART_ROOT_DIR = "chart"
1916

2017
Helm(def script, def gitopsConfig) {
2118
super(script, gitopsConfig)
2219
this.extraResourcesFolder = "extraResources"
23-
if (gitopsConfig.deployments.helm.repoType == 'GIT') {
24-
chartRepo = new GitRepo(script)
25-
} else if (gitopsConfig.deployments.helm.repoType == 'HELM') {
26-
chartRepo = new HelmRepo(script)
27-
}
20+
2821
if(gitopsConfig.gitopsTool == 'FLUX') {
2922
helmRelease = new FluxV1Release(script)
3023
} else if(gitopsConfig.gitopsTool == 'ARGO') {
@@ -37,7 +30,8 @@ class Helm extends Deployment {
3730
def sourcePath = gitopsConfig.deployments.sourcePath
3831
def destinationPath = getDestinationFolder(getFolderStructureStrategy(), stage)
3932

40-
chartRepo.prepareRepo(gitopsConfig, helmChartTempDir, chartRootDir)
33+
RepoType repoType = RepoType.create(gitopsConfig.deployments.helm.repoType, script)
34+
repoType.prepareRepo(gitopsConfig, HELM_CHART_TEMP_DIR, CHART_ROOT_DIR)
4135

4236
// writing the merged-values.yaml via writeYaml into a file has the advantage, that it gets formatted as valid yaml
4337
// This makes it easier to read in and indent for the inline use in the helmRelease.
@@ -48,17 +42,17 @@ class Helm extends Deployment {
4842
if (script.fileExists("${script.env.WORKSPACE}/${sourcePath}/values-shared.yaml")) {
4943
valueFiles.add("${script.env.WORKSPACE}/${sourcePath}/values-shared.yaml")
5044
}
51-
script.writeFile file: "${script.env.WORKSPACE}/${helmChartTempDir}/mergedValues.yaml", text: mergeValuesFiles(gitopsConfig, valueFiles as String[])
45+
script.writeFile file: "${script.env.WORKSPACE}/${HELM_CHART_TEMP_DIR}/mergedValues.yaml", text: mergeValuesFiles(gitopsConfig, valueFiles as String[], repoType)
5246

53-
updateYamlValue("${script.env.WORKSPACE}/${helmChartTempDir}/mergedValues.yaml", gitopsConfig)
47+
updateYamlValue("${script.env.WORKSPACE}/${HELM_CHART_TEMP_DIR}/mergedValues.yaml", gitopsConfig)
5448

55-
script.writeFile file: "${destinationPath}/applicationRelease.yaml", text: helmRelease.create(gitopsConfig, getNamespace(stage), "${script.env.WORKSPACE}/${helmChartTempDir}/mergedValues.yaml")
49+
script.writeFile file: "${destinationPath}/applicationRelease.yaml", text: helmRelease.create(gitopsConfig, getNamespace(stage), "${script.env.WORKSPACE}/${HELM_CHART_TEMP_DIR}/mergedValues.yaml")
5650
}
5751

5852
@Override
5953
def postValidation(String stage) {
6054
// clean the helm chart folder since the validation on this helm chart is done
61-
script.sh "rm -rf ${script.env.WORKSPACE}/${helmChartTempDir} || true"
55+
script.sh "rm -rf ${script.env.WORKSPACE}/${HELM_CHART_TEMP_DIR} || true"
6256
}
6357

6458
@Override
@@ -67,7 +61,7 @@ class Helm extends Deployment {
6761

6862
gitopsConfig.validators.each { validator ->
6963
validator.value.validator.validate(validator.value.enabled, getGitopsTool(), SourceType.PLAIN, "${destinationPath}", validator.value.config, gitopsConfig)
70-
validator.value.validator.validate(validator.value.enabled, getGitopsTool(), SourceType.HELM, "${script.env.WORKSPACE}/${helmChartTempDir}",validator.value.config, gitopsConfig)
64+
validator.value.validator.validate(validator.value.enabled, getGitopsTool(), SourceType.HELM, "${script.env.WORKSPACE}/${HELM_CHART_TEMP_DIR}",validator.value.config, gitopsConfig)
7165
}
7266
}
7367

@@ -89,20 +83,14 @@ class Helm extends Deployment {
8983
script.writeYaml file: yamlFilePath, data: data, overwrite: true
9084
}
9185

92-
private String mergeValuesFiles(Map gitopsConfig, String[] valuesFiles) {
93-
def helmConfig = gitopsConfig.deployments.helm
86+
private String mergeValuesFiles(Map gitopsConfig, String[] valuesFiles, RepoType repoType) {
9487

9588
String mergedValuesFile = ""
9689

97-
def chartDir = ''
98-
if (helmConfig.containsKey('chartPath') && helmConfig.chartPath) {
99-
chartDir = helmConfig.chartPath
100-
} else if ( helmConfig.containsKey('chartName')) {
101-
chartDir = helmConfig.chartName
102-
}
90+
def chartPath = repoType.getChartPath(gitopsConfig, HELM_CHART_TEMP_DIR, CHART_ROOT_DIR)
10391

10492
withDockerImage(gitopsConfig.buildImages.helm) {
105-
String helmScript = "helm values ${script.env.WORKSPACE}/${helmChartTempDir}/${chartRootDir}/${chartDir} ${valuesFilesWithParameter(valuesFiles)}"
93+
String helmScript = "helm values ${chartPath} ${valuesFilesWithParameter(valuesFiles)}"
10694
mergedValuesFile = script.sh returnStdout: true, script: helmScript
10795
}
10896
return mergedValuesFile

src/com/cloudogu/gitopsbuildlib/deployment/helm/helmrelease/ArgoCDRelease.groovy

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.cloudogu.gitopsbuildlib.deployment.helm.helmrelease
22

3+
import com.cloudogu.gitopsbuildlib.deployment.helm.Helm
4+
import com.cloudogu.gitopsbuildlib.deployment.helm.repotype.RepoType
35
import com.cloudogu.gitopsbuildlib.docker.DockerWrapper
46

57
class ArgoCDRelease extends HelmRelease {
@@ -16,34 +18,33 @@ class ArgoCDRelease extends HelmRelease {
1618
Map helmConfig = gitopsConfig.deployments.helm
1719
String application = gitopsConfig.application
1820

19-
String helmRelease = ""
2021
if (helmConfig.repoType == 'GIT') {
21-
helmRelease = createResourcesFromGitRepo(gitopsConfig, application, namespace, mergedValuesFile)
22+
return createResourcesFromGitRepo(gitopsConfig, application, namespace, mergedValuesFile)
2223
} else if (helmConfig.repoType == 'HELM') {
23-
helmRelease = createResourcesFromHelmRepo(gitopsConfig, application, namespace, mergedValuesFile)
24+
return createResourcesFromHelmRepo(gitopsConfig, application, namespace, mergedValuesFile)
25+
} else if (helmConfig.repoType == 'LOCAL') {
26+
return createResourcesFromLocalRepo(gitopsConfig, application, namespace, mergedValuesFile)
2427
}
25-
return helmRelease
28+
return null // Validated in deployViaGitOps.groovy
2629
}
2730

2831
private String createResourcesFromGitRepo(Map gitopsConfig, String application, String namespace, String mergedValuesFile) {
29-
Map helmConfig = gitopsConfig.deployments.helm
30-
31-
def chartPath = ''
32-
if (helmConfig.containsKey('chartPath')) {
33-
chartPath = helmConfig.chartPath
34-
}
35-
36-
return createHelmRelease(gitopsConfig, chartPath as String, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
32+
return createHelmRelease(gitopsConfig, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
3733
}
3834

3935
private String createResourcesFromHelmRepo(Map gitopsConfig, String application, String namespace, String mergedValuesFile) {
40-
return createHelmRelease(gitopsConfig, gitopsConfig.deployments.helm.chartName, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
36+
return createHelmRelease(gitopsConfig, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
37+
}
38+
39+
private String createResourcesFromLocalRepo(Map gitopsConfig, String application, String namespace, String mergedValuesFile) {
40+
return createHelmRelease(gitopsConfig, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
4141
}
4242

43-
private String createHelmRelease(Map gitopsConfig, def chartPath, String application, String namespace, def helmImageConfig, String mergedValuesFile) {
43+
private String createHelmRelease(Map gitopsConfig, String application, String namespace, def helmImageConfig, String mergedValuesFile) {
4444
String helmRelease = ""
45+
def chartPath = RepoType.create(gitopsConfig.deployments.helm.repoType, script).getChartPath(gitopsConfig, Helm.HELM_CHART_TEMP_DIR, Helm.CHART_ROOT_DIR)
4546
dockerWrapper.withDockerImage(helmImageConfig) {
46-
String templateScript = "helm template ${application} ${script.env.WORKSPACE}/.helmChartTempDir/chart/${chartPath} -n ${namespace} --kube-version ${gitopsConfig.k8sVersion} -f ${mergedValuesFile}"
47+
String templateScript = "helm template ${application} ${chartPath} -n ${namespace} --kube-version ${gitopsConfig.k8sVersion} -f ${mergedValuesFile}"
4748
helmRelease = script.sh returnStdout: true, script: templateScript
4849
}
4950

src/com/cloudogu/gitopsbuildlib/deployment/helm/helmrelease/FluxV1Release.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ ${values}
5252
return gitRepoChart(helmConfig)
5353
} else if (helmConfig.repoType == 'HELM') {
5454
return helmRepoChart(helmConfig)
55+
} else if (helmConfig.repoType == 'LOCAL') {
56+
return script.error("Helm repoType LOCAL not supported for fluxv1")
5557
}
58+
return null // Validated in deployViaGitOps.groovy
5659
}
5760
}

src/com/cloudogu/gitopsbuildlib/deployment/helm/repotype/GitRepo.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ class GitRepo extends RepoType {
66
super(script)
77
}
88

9+
@Override
10+
String getChartPath(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
11+
def helmConfig = gitopsConfig.deployments.helm
12+
def chartPath = ''
13+
if (helmConfig.containsKey('chartPath') && helmConfig.chartPath) {
14+
chartPath = helmConfig.chartPath
15+
}
16+
17+
return "${script.env.WORKSPACE}/${helmChartTempDir}/${chartRootDir}/${chartPath}"
18+
}
19+
920
@Override
1021
void prepareRepo(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
1122
def helmConfig = gitopsConfig.deployments.helm

src/com/cloudogu/gitopsbuildlib/deployment/helm/repotype/HelmRepo.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ class HelmRepo extends RepoType {
66
super(script)
77
}
88

9+
@Override
10+
String getChartPath(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
11+
return "${script.env.WORKSPACE}/${helmChartTempDir}/${chartRootDir}/${gitopsConfig.deployments.helm.chartName}"
12+
}
13+
914
@Override
1015
void prepareRepo(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
1116
def helmConfig = gitopsConfig.deployments.helm
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.cloudogu.gitopsbuildlib.deployment.helm.repotype
2+
3+
class LocalRepo extends RepoType {
4+
5+
LocalRepo(def script) {
6+
super(script)
7+
}
8+
9+
@Override
10+
void prepareRepo(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
11+
// Nothing to prepare for a local chart
12+
}
13+
14+
@Override
15+
String getChartPath(Map gitopsConfig, String helmChartTempDir, String chartRootDir) {
16+
def chartPath = gitopsConfig.deployments.helm.chartPath
17+
// Use absolute paths, so e.g. helm values works in .configRepoTempDir
18+
if (!chartRootDir.contains(script.env.WORKSPACE)) {
19+
chartPath = "${script.env.WORKSPACE}/${chartPath}"
20+
}
21+
return "${chartPath}"
22+
}
23+
}

src/com/cloudogu/gitopsbuildlib/deployment/helm/repotype/RepoType.groovy

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,21 @@ abstract class RepoType {
1313
}
1414

1515
abstract void prepareRepo(Map gitopsConfig, String helmChartTempDir, String chartRootDir)
16-
16+
abstract String getChartPath(Map gitopsConfig, String helmChartTempDir, String chartRootDir)
17+
1718
void withDockerImage(def imageConfig, Closure body) {
1819
dockerWrapper.withDockerImage(imageConfig, body)
1920
}
21+
22+
static RepoType create(String potentialRepoType, def script) {
23+
RepoType repoType = null
24+
if (potentialRepoType == 'GIT') {
25+
repoType = new GitRepo(script)
26+
} else if (potentialRepoType == 'HELM') {
27+
repoType = new HelmRepo(script)
28+
} else if (potentialRepoType == 'LOCAL') {
29+
repoType = new LocalRepo(script)
30+
}
31+
return repoType
32+
}
2033
}

test/com/cloudogu/gitopsbuildlib/ScriptMock.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class ScriptMock {
1212

1313
List<String> actualShArgs = new LinkedList<>()
1414
List<String> actualEchoArgs = new LinkedList<>()
15+
List<String> actualErrorArgs = new LinkedList<>()
1516
List<String> actualReadYamlArgs = new LinkedList<>()
1617
List<String> actualGitArgs = new LinkedList<>()
1718
List<String> actualDir = new LinkedList<>()
@@ -49,6 +50,7 @@ to:
4950
pwd : { 'pwd' },
5051
sh : { args -> actualShArgs += args.toString() },
5152
echo : { args -> actualEchoArgs += args.toString() },
53+
error : { args -> actualErrorArgs += args.toString() },
5254
readYaml: { args -> actualReadYamlArgs += args.toString(); return new YamlSlurper().parseText(configYaml) },
5355
writeYaml: { args -> actualWriteYamlArgs += args.toString() },
5456
readFile : { args -> actualReadFileArgs += args.toString(); return configYaml},

0 commit comments

Comments
 (0)