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/credentials for registry #19

Merged
merged 10 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.idea
*.iml
maven-wrapper.jar
target/
target/
.classpath
.factorypath
.project
.settings/
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,28 @@ All of these have set default images, but you can change them if you wish to.
def gitopsConfig = [
buildImages: [
// These are used to run helm and kubectl commands in the core logic
helm: 'ghcr.io/cloudogu/helm:3.5.4-1',
kubectl: 'lachlanevenson/k8s-kubectl:v1.19.3',
//
helm: [
image: 'ghcr.io/cloudogu/helm:3.5.4-1'
credentialsId: 'myCredentials' (optional - only needed if image is in a private repository. CredentialsId is getting pulled from Jenkins credentials)
],
kubectl: [
image: 'lachlanevenson/k8s-kubectl:v1.19.3'
credentialsId: 'myCredentials' (optional - only needed if image is in a private repository. CredentialsId is getting pulled from Jenkins credentials)
],
// These are used for each specific validator via an imageRef property inside the validators config. See [Validators] for examples.
kubeval: 'ghcr.io/cloudogu/helm:3.5.4-1',
helmKubeval: 'ghcr.io/cloudogu/helm:3.5.4-1',
yamllint: 'cytopia/yamllint:1.25-0.7'
kubeval: [
image: 'ghcr.io/cloudogu/helm:3.5.4-1'
credentialsId: 'myCredentials' (optional - only needed if image is in a private repository. CredentialsId is getting pulled from Jenkins credentials)
],
helmKubeval: [
image: 'ghcr.io/cloudogu/helm:3.5.4-1'
credentialsId: 'myCredentials' (optional - only needed if image is in a private repository. CredentialsId is getting pulled from Jenkins credentials)
],
yamllint: [
image: 'cytopia/yamllint:1.25-0.7'
credentialsId: 'myCredentials' (optional - only needed if image is in a private repository. CredentialsId is getting pulled from Jenkins credentials)
]
]
]
```
Expand Down
7 changes: 3 additions & 4 deletions src/com/cloudogu/gitopsbuildlib/deployment/Deployment.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.cloudogu.gitopsbuildlib.docker.DockerWrapper

abstract class Deployment {

protected static String getKubectlImage() { 'lachlanevenson/k8s-kubectl:v1.19.3' }
protected String extraResourcesFolder = ""

static String getConfigDir() { '.config' }
Expand Down Expand Up @@ -55,7 +54,7 @@ abstract class Deployment {

String createConfigMap(String key, String filePath, String name, String namespace) {
String configMap = ""
withDockerImage(kubectlImage) {
withDockerImage(gitopsConfig.buildImages.kubectl) {
String kubeScript = "KUBECONFIG=${writeKubeConfig()} kubectl create configmap ${name} " +
"--from-file=${key}=${filePath} " +
"--dry-run=client -o yaml -n ${namespace}"
Expand All @@ -65,8 +64,8 @@ abstract class Deployment {
return configMap
}

void withDockerImage(String image, Closure body) {
dockerWrapper.withDockerImage(image, body)
void withDockerImage(def imageConfig, Closure body) {
dockerWrapper.withDockerImage(imageConfig, body)
}

// Dummy kubeConfig, so we can use `kubectl --dry-run=client`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,32 @@ class ArgoCDRelease extends HelmRelease {

String helmRelease = ""
if (helmConfig.repoType == 'GIT') {
helmRelease = createResourcesFromGitRepo(gitopsConfig, application, mergedValuesFile)
helmRelease = createResourcesFromGitRepo(gitopsConfig, application, namespace, mergedValuesFile)
} else if (helmConfig.repoType == 'HELM') {
helmRelease = createResourcesFromHelmRepo(gitopsConfig, application, mergedValuesFile)
helmRelease = createResourcesFromHelmRepo(gitopsConfig, application, namespace, mergedValuesFile)
}
return helmRelease
}

private String createResourcesFromGitRepo(Map gitopsConfig, String application, String mergedValuesFile) {
private String createResourcesFromGitRepo(Map gitopsConfig, String application, String namespace, String mergedValuesFile) {
Map helmConfig = gitopsConfig.deployments.helm

def chartPath = ''
if (helmConfig.containsKey('chartPath')) {
chartPath = helmConfig.chartPath
}

return createHelmRelease(chartPath as String, application, gitopsConfig.buildImages.helm, mergedValuesFile)
return createHelmRelease(chartPath as String, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
}

private String createResourcesFromHelmRepo(Map gitopsConfig, String application, String mergedValuesFile) {
return createHelmRelease(gitopsConfig.deployments.helm.chartName, application, gitopsConfig.buildImages.helm, mergedValuesFile)
private String createResourcesFromHelmRepo(Map gitopsConfig, String application, String namespace, String mergedValuesFile) {
return createHelmRelease(gitopsConfig.deployments.helm.chartName, application, namespace, gitopsConfig.buildImages.helm, mergedValuesFile)
}

private String createHelmRelease(def chartPath, String application, def helmImage, String mergedValuesFile) {
private String createHelmRelease(def chartPath, String application, String namespace, def helmImageConfig, String mergedValuesFile) {
String helmRelease = ""
dockerWrapper.withDockerImage(helmImage) {
String templateScript = "helm template ${application} ${script.env.WORKSPACE}/.helmChartTempDir/chart/${chartPath} -f ${mergedValuesFile}"
dockerWrapper.withDockerImage(helmImageConfig) {
String templateScript = "helm template ${application} ${script.env.WORKSPACE}/.helmChartTempDir/chart/${chartPath} -n ${namespace} -f ${mergedValuesFile}"
helmRelease = script.sh returnStdout: true, script: templateScript
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ class GitRepo extends RepoType {
} else {
git = script.cesBuildLib.Git.new(script)
}

git url: helmConfig.repoUrl, branch: 'main', changelog: false, poll: false


if (helmConfig.containsKey('mainBranch') && helmConfig.mainBranch) {
git url: helmConfig.repoUrl, branch: helmConfig.mainBranch, changelog: false, poll: false
} else {
git url: helmConfig.repoUrl, branch: 'main', changelog: false, poll: false
}

if(helmConfig.containsKey('version') && helmConfig.version) {
git.fetch()
git.checkout(helmConfig.version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ abstract class RepoType {

abstract void prepareRepo(Map gitopsConfig, String helmChartTempDir, String chartRootDir)

void withDockerImage(String image, Closure body) {
dockerWrapper.withDockerImage(image, body)
void withDockerImage(def imageConfig, Closure body) {
dockerWrapper.withDockerImage(imageConfig, body)
}
}
18 changes: 17 additions & 1 deletion src/com/cloudogu/gitopsbuildlib/docker/DockerWrapper.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,23 @@ class DockerWrapper {
this.script = script
}

void withDockerImage(String image, Closure body) {
void withDockerImage(def imageConfig, Closure body) {
if(imageConfig.containsKey('credentialsId') && imageConfig.credentialsId) {
def registryUrl = getRegistryUrlFromImage(imageConfig.image)
script.docker.withRegistry("https://${registryUrl}", imageConfig.credentialsId) {
runDockerImage(imageConfig.image, body)
}
} else {
runDockerImage(imageConfig.image, body)
}
}

private String getRegistryUrlFromImage(String image) {
int i = image.lastIndexOf('/')
return image.substring(0, i)
}

private void runDockerImage(String image, Closure body) {
script.docker.image(image).inside(
// Allow accessing WORKSPACE even when we are in a child dir (using "dir() {}")
"${script.pwd().equals(script.env.WORKSPACE) ? '' : "-v ${script.env.WORKSPACE}:${script.env.WORKSPACE} "}" +
Expand Down
10 changes: 5 additions & 5 deletions src/com/cloudogu/gitopsbuildlib/validation/Validator.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ abstract class Validator {
void validate(boolean enabled, GitopsTool gitopsTool, SourceType sourceType, String targetDirectory, Map validatorConfig, Map gitopsConfig) {
if (enabled && getSupportedGitopsTools().contains(gitopsTool) && getSupportedSourceTypes().contains(sourceType)) {
script.echo "Starting validator ${this.getClass().getSimpleName()} for ${gitopsTool.name()} in ${sourceType.name()} resources"
withDockerImage(getImage(gitopsConfig, validatorConfig)) {
withDockerImage(getImageConfig(gitopsConfig, validatorConfig)) {
validate(targetDirectory, validatorConfig, gitopsConfig)
}
} else {
Expand All @@ -30,13 +30,13 @@ abstract class Validator {
abstract SourceType[] getSupportedSourceTypes()
abstract GitopsTool[] getSupportedGitopsTools()

protected void withDockerImage(String image, Closure body) {
dockerWrapper.withDockerImage(image, body)
protected void withDockerImage(def imageConfig, Closure body) {
dockerWrapper.withDockerImage(imageConfig, body)
}

protected String getImage(Map gitopsConfig, Map validatorConfig) {
protected getImageConfig(Map gitopsConfig, Map validatorConfig) {
if (validatorConfig.containsKey('image')) {
return validatorConfig.image
return [ image: validatorConfig.image ]
} else if (validatorConfig.containsKey('imageRef') && gitopsConfig.buildImages.containsKey(validatorConfig.imageRef)) {
return gitopsConfig.buildImages[validatorConfig.imageRef]
} else {
Expand Down
10 changes: 10 additions & 0 deletions test/com/cloudogu/gitopsbuildlib/DockerMock.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import static org.mockito.Mockito.when
class DockerMock {

List<String> actualInsideArgs = new LinkedList<>()
List<String> actualRegistryArgs = new LinkedList<>()
List<String> actualImages = new LinkedList<>()

Docker createMock() {
Expand All @@ -24,6 +25,15 @@ class DockerMock {
}
})

when(dockerMock.withRegistry(anyString(), anyString(), any())).thenAnswer(new Answer<Object>() {
@Override
Object answer(InvocationOnMock invocationOnMock) throws Throwable {
actualRegistryArgs += invocationOnMock.getArgument(0)
actualRegistryArgs += invocationOnMock.getArgument(1)
Closure closure = invocationOnMock.getArgument(2)
closure.call()
}
})
when(imageMock.mountJenkinsUser()).thenReturn(imageMock)
when(imageMock.mountJenkinsUser(anyBoolean())).thenReturn(imageMock)
when(imageMock.mountDockerSocket()).thenReturn(imageMock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class DeploymentTest {
sourcePath: 'k8s',
plain: [:]
],
buildImages: [
kubectl: [
image: "http://my-private-registry.com/repo/kubectlImage",
credentialsId: "credentials"
]
],
validators: [
yamllint: [
validator: new Yamllint(scriptMock.mock),
Expand Down Expand Up @@ -70,6 +76,9 @@ class DeploymentTest {

deploymentUnderTest.createFileConfigmaps('staging')

assertThat(scriptMock.dockerMock.actualRegistryArgs[0]).isEqualTo('https://http://my-private-registry.com/repo')
assertThat(scriptMock.dockerMock.actualRegistryArgs[1]).isEqualTo('credentials')

assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:KUBECONFIG=pwd/.kube/config kubectl create configmap index --from-file=index.html=workspace/k8s/../index.html --dry-run=client -o yaml -n fluxv1-staging]')

assertThat(scriptMock.actualWriteFileArgs[0]).isEqualTo('''[file:pwd/.kube/config, text:apiVersion: v1
Expand Down
8 changes: 6 additions & 2 deletions test/com/cloudogu/gitopsbuildlib/deployment/HelmTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ class HelmTest {
]
],
buildImages: [
helm: 'helmImage',
kubectl: 'kubectlImage'
helm: [
image: 'helmImage',
],
kubectl: [
image: 'kubectlImage'
]
],
deployments: deployment,
validators: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ class ArgoCDReleaseTest {
]
],
buildImages: [
helm: 'helmImg'
helm: [
image: 'helmImg'
]
]
],
'namespace',
'this/is/a/valuesfile')

assertThat(scriptMock.dockerMock.actualImages[0]).isEqualTo('helmImg')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/path -f this/is/a/valuesfile]')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/path -n namespace -f this/is/a/valuesfile]')
}

@Test
Expand All @@ -47,14 +49,16 @@ class ArgoCDReleaseTest {
]
],
buildImages: [
helm: 'helmImg'
helm: [
image: 'helmImg'
]
]
],
'namespace',
'this/is/a/valuesfile')

assertThat(scriptMock.dockerMock.actualImages[0]).isEqualTo('helmImg')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/ -f this/is/a/valuesfile]')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/ -n namespace -f this/is/a/valuesfile]')
}

@Test
Expand All @@ -70,13 +74,15 @@ class ArgoCDReleaseTest {
]
],
buildImages: [
helm: 'helmImg'
helm: [
image: 'helmImg'
]
]
],
'namespace',
'this/is/a/valuesfile')

assertThat(scriptMock.dockerMock.actualImages[0]).isEqualTo('helmImg')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/chartName -f this/is/a/valuesfile]')
assertThat(scriptMock.actualShArgs[0]).isEqualTo('[returnStdout:true, script:helm template app workspace/.helmChartTempDir/chart/chartName -n namespace -f this/is/a/valuesfile]')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ class GitRepoTest {
void 'merges values successfully'() {
gitRepo.prepareRepo([
buildImages: [
helm: 'helmImage'
helm: [
image: 'helmImage'
]
],
deployments: [
helm: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class HelmRepoTest {
void 'merges values successfully'() {
helmRepo.prepareRepo([
buildImages: [
helm: 'helmImage'
helm: [
image: 'helmImage'
]
],
deployments: [
helm: [
Expand Down
Loading