diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..4210158b --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,33 @@ +**Is this a request for help?**: + +--- + +**Is this a BUG REPORT or FEATURE REQUEST?** (choose one): + + + +**Environment (plugin version, maven version, OS, ...)**: + + +**What happened**: + + +**What you expected to happen**: + + +**How to reproduce it** (as minimally and precisely as possible): + + +**Anything else we need to know**: \ No newline at end of file diff --git a/README.md b/README.md index 3195bc84..7839aad9 100644 --- a/README.md +++ b/README.md @@ -18,60 +18,89 @@ Currently (October 2017) there is no simple Maven plugin to package existing HEL # How? -The plugin downloads HELM in a specific version and runs the tool in the background. +By default the plugin downloads HELM in a specific version. Next to that it is possible to specify a local HELM binary. +In both cases HELM will be executed in the background. Add following dependency to your pom.xml: -``` +```xml com.kiwigrid helm-maven-plugin - 3.4 + 4.0 ``` -Configure plugin with explicit credentials: +## Configuration Examples + +### Usage with Downloaded Binary +```xml + + + ... + + com.kiwigrid + helm-maven-plugin + 4.0 + + ${project.basedir} + ${project.version} + + https://storage.googleapis.com/kubernetes-helm/helm-v2.12.3-linux-amd64.tar.gz + ${project.basedir}/target/helm/home + + + ... + + +``` + +### Usage with Local Binary +```xml + + + ... + + com.kiwigrid + helm-maven-plugin + 4.0 + + ${project.basedir} + ${project.version} + + true + /usr/local/bin + + + ... + + ``` -... - - https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz - -... + +### Configure Plugin to Use Credentials from settings.xml for Upload +```xml ... com.kiwigrid helm-maven-plugin - 3.4 + 4.0 ${project.basedir} ${project.version} + stable-repo https://repo.example.com/artifactory/helm-stable - - ARTIFACTORY - foo - bar snapshot-repo - https://my.chart.museum/api/charts + https://my.chart.museum:8080/api/charts CHARTMUSEUM - ${helm.download.url} + https://storage.googleapis.com/kubernetes-helm/helm-v2.12.3-linux-amd64.tar.gz ${project.basedir}/target/helm/home - false - - ${project.basedir}/excluded - - - - incubator - https://kubernetes-charts-incubator.storage.googleapis.com - - ... @@ -79,35 +108,47 @@ Configure plugin with explicit credentials: ``` -Configure plugin using credentials from settings.xml: -``` -... - - https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz - -... +### More Complex Example +```xml ... com.kiwigrid helm-maven-plugin - 3.4 + 4.0 ${project.basedir} ${project.version} stable-repo https://repo.example.com/artifactory/helm-stable + + ARTIFACTORY + foo + bar snapshot-repo - https://my.chart.museum:8080/api/charts + https://my.chart.museum/api/charts CHARTMUSEUM - ${helm.download.url} + https://storage.googleapis.com/kubernetes-helm/helm-v2.12.3-linux-amd64.tar.gz ${project.basedir}/target/helm/home + + false + + + ${project.basedir}/excluded + + + + + kiwigrid + https://kiwigrid.github.io + + ... @@ -122,7 +163,7 @@ Configure plugin using credentials from settings.xml: - Recursive chart detection (subcharts) - Helm does not need to be installed - Upload to [ChartMuseum](https://github.com/kubernetes-helm/chartmuseum) or [Artifactory](https://jfrog.com/artifactory/) -- Repository names are interpreted as server ids to retrieve basic authentication from server list in settings.xml. +- Repository names are interpreted as server IDs to retrieve basic authentication from server list in settings.xml. # Usage @@ -144,8 +185,8 @@ Parameter | Type | User Property | Required | Description `` | string | helm.appVersion | false | The version of the app. This needn't be SemVer. `` | string | helm.downloadUrl | false | URL to download helm `` | list of strings | helm.excludes | false | list of chart directories to exclude +`` | boolean | helm.useLocalHelmBinary | false | Controls whether a local binary should be used instead of downloading it. If set to `true` path has to be set with property `executableDirectory` `` | string | helm.executableDirectory | false | directory of your helm installation (default: `${project.build.directory}/helm`) -`` | string | helm.executable | false | path to your helm executable (default: `${project.build.directory}/helm/linux-amd64/helm`) `` | string | helm.outputDirectory | false | chart output directory (default: `${project.build.directory}/helm/repo`) `` | string | helm.homeDirectory | false | path to helm home directory; useful for concurrent Jenkins builds! (default: `~/.helm`) `` | list of [HelmRepository](./src/main/java/com/kiwigrid/helm/maven/plugin/HelmRepository.java) | helm.extraRepos | false | adds extra repositories while init @@ -161,7 +202,7 @@ Parameter | Type | User Property | Required | Description `` | boolean | helm.upload.skip | false | skip upload goal `` | string | helm.security | false | path to your [settings-security.xml](https://maven.apache.org/guides/mini/guide-encryption.html) (default: `~/.m2/settings-security.xml`) -## Packaging with the helm lifecycle +## Packaging with the Helm Lifecycle To keep your pom files small you can use 'helm' packaging. This binds `helm:init` to the initialize phase, `helm:lint` to the test phase, `helm:package` to the package phase and `helm:upload` to the deploy phase. diff --git a/pom.xml b/pom.xml index d46a4f44..e7798323 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,12 @@ com.kiwigrid helm-maven-plugin - 3.5-SNAPSHOT + 4.0-SNAPSHOT ${project.groupId}:${project.artifactId} A plugin for executing HELM (https://docs.helm.sh). - HELM itself will be downloaded during build. + HELM itself will be downloaded during build or local binary can be provided. https://github.com/kiwigrid/helm-maven-plugin diff --git a/src/main/java/com/kiwigrid/helm/maven/plugin/AbstractHelmMojo.java b/src/main/java/com/kiwigrid/helm/maven/plugin/AbstractHelmMojo.java index 55c78769..bbdf9786 100644 --- a/src/main/java/com/kiwigrid/helm/maven/plugin/AbstractHelmMojo.java +++ b/src/main/java/com/kiwigrid/helm/maven/plugin/AbstractHelmMojo.java @@ -35,15 +35,12 @@ public abstract class AbstractHelmMojo extends AbstractMojo { @Component(role = org.sonatype.plexus.components.sec.dispatcher.SecDispatcher.class, hint = "default") private SecDispatcher securityDispatcher; + @Parameter(property = "helm.useLocalHelmBinary", defaultValue = "false") + private boolean useLocalHelmBinary; + @Parameter(property = "helm.executableDirectory", defaultValue = "${project.build.directory}/helm") private String helmExecutableDirectory; - /** - * If no executeable is set this plugin tries to determine helm executeable based on operation system. - */ - @Parameter(property = "helm.executable") - private String helmExecuteable; - @Parameter(property = "helm.outputDirectory", defaultValue = "${project.build.directory}/helm/repo") private String outputDirectory; @@ -87,12 +84,10 @@ public abstract class AbstractHelmMojo extends AbstractMojo { private Settings settings; Path getHelmExecuteablePath() throws MojoExecutionException { - if (helmExecuteable == null) { - helmExecuteable = SystemUtils.IS_OS_WINDOWS ? "helm.exe" : "helm"; - } - Path path = Paths.get(helmExecutableDirectory, helmExecuteable).toAbsolutePath(); + String helmExecutable = SystemUtils.IS_OS_WINDOWS ? "helm.exe" : "helm"; + Path path = Paths.get(helmExecutableDirectory, helmExecutable).toAbsolutePath(); if (!path.toFile().exists()) { - throw new MojoExecutionException("Helm executeable at " + path + " not found."); + throw new MojoExecutionException("Helm executable at " + path + " not found."); } return path; } @@ -248,14 +243,6 @@ protected SecDispatcher getSecDispatcher() { return securityDispatcher; } - public String getHelmExecuteable() { - return helmExecuteable; - } - - public void setHelmExecuteable(String helmExecuteable) { - this.helmExecuteable = helmExecuteable; - } - public String getOutputDirectory() { return outputDirectory; } @@ -355,4 +342,12 @@ public void setHelmSecurity(String helmSecurity) { public Settings getSettings() { return settings; } + + public boolean isUseLocalHelmBinary() { + return useLocalHelmBinary; + } + + public void setUseLocalHelmBinary(boolean useLocalHelmBinary) { + this.useLocalHelmBinary = useLocalHelmBinary; + } } diff --git a/src/main/java/com/kiwigrid/helm/maven/plugin/InitMojo.java b/src/main/java/com/kiwigrid/helm/maven/plugin/InitMojo.java index 93af7e1a..d670547a 100644 --- a/src/main/java/com/kiwigrid/helm/maven/plugin/InitMojo.java +++ b/src/main/java/com/kiwigrid/helm/maven/plugin/InitMojo.java @@ -35,6 +35,33 @@ public void execute() getLog().info("Creating output directory..."); callCli("mkdir -p " + getOutputDirectory(), "Unable to create output directory at " + getOutputDirectory(), false); + + if(isUseLocalHelmBinary()) { + verifyLocalHelmBinary(); + getLog().info("Using local HELM binary ["+ getHelmExecutableDirectory() +"]"); + } else { + downloadAndUnpackHelm(); + } + + if (getHelmExtraRepos() != null) { + for (HelmRepository repository : getHelmExtraRepos()) { + getLog().info("Adding repo " + repository); + PasswordAuthentication auth = getAuthentication(repository); + callCli(getHelmExecutableDirectory() + + File.separator + + "helm repo add " + + repository.getName() + + " " + + repository.getUrl() + + (StringUtils.isNotEmpty(getHelmHomeDirectory()) ? " --home=" + getHelmHomeDirectory() : "") + + (auth != null ? " --username=" + auth.getUserName() + " --password=" + String.valueOf(auth.getPassword()) : ""), + "Unable add repo", + false); + } + } + } + + protected void downloadAndUnpackHelm() throws MojoExecutionException { getLog().info("Downloading Helm..."); callCli("wget -qO " + getHelmExecutableDirectory() @@ -56,23 +83,10 @@ public void execute() + (StringUtils.isNotEmpty(getHelmHomeDirectory()) ? " --home=" + getHelmHomeDirectory() : ""), "Unable to call helm init", false); + } - if (getHelmExtraRepos() != null) { - for (HelmRepository repository : getHelmExtraRepos()) { - getLog().info("Adding repo " + repository); - PasswordAuthentication auth = getAuthentication(repository); - callCli(getHelmExecutableDirectory() - + File.separator - + "helm repo add " - + repository.getName() - + " " - + repository.getUrl() - + (StringUtils.isNotEmpty(getHelmHomeDirectory()) ? " --home=" + getHelmHomeDirectory() : "") - + (auth != null ? " --username=" + auth.getUserName() + " --password=" + String.valueOf(auth.getPassword()) : ""), - "Unable add repo", - false); - } - } + private void verifyLocalHelmBinary() throws MojoExecutionException { + callCli(getHelmExecuteablePath() + " version --client", "Unable to verify local HELM binary", false); } public boolean isSkipRefresh() { diff --git a/src/test/java/com/kiwigrid/helm/maven/plugin/InitMojoTest.java b/src/test/java/com/kiwigrid/helm/maven/plugin/InitMojoTest.java index 64732e54..863892f3 100644 --- a/src/test/java/com/kiwigrid/helm/maven/plugin/InitMojoTest.java +++ b/src/test/java/com/kiwigrid/helm/maven/plugin/InitMojoTest.java @@ -1,5 +1,7 @@ package com.kiwigrid.helm.maven.plugin; +import java.io.File; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -9,6 +11,7 @@ import com.kiwigrid.helm.maven.plugin.junit.MojoExtension; import com.kiwigrid.helm.maven.plugin.junit.MojoProperty; import com.kiwigrid.helm.maven.plugin.junit.SystemPropertyExtension; +import org.apache.maven.plugin.MojoExecutionException; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -29,26 +32,23 @@ public class InitMojoTest { @DisplayName("Init helm with different download urls.") @ParameterizedTest @ValueSource(strings = { "darwin", "linux", "windows" }) - public void initMojoHappyPath(String os, InitMojo mojo) throws Exception { + public void initMojoHappyPathWhenDownloadHelm(String os, InitMojo mojo) throws Exception { // prepare execution - doNothing().when(mojo).callCli(contains("helm "), anyString(), anyBoolean()); mojo.setHelmDownloadUrl("https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-" + os + "-amd64.tar.gz"); // run init - mojo.execute(); // check helm file - Path helm = Paths.get(mojo.getHelmExecutableDirectory(), "windows".equals(os) ? "helm.exe" : "helm") .toAbsolutePath(); assertTrue(Files.exists(helm), "Helm executable not found at: " + helm); } @Test - public void verifyDefaultInitCommand(InitMojo mojo) throws Exception { + public void verifyDefaultInitCommandWhenDownloadingHelm(InitMojo mojo) throws Exception { // prepare execution ArgumentCaptor helmCommandCaptor = ArgumentCaptor.forClass(String.class); @@ -89,4 +89,25 @@ public void initMojoSkipRefreshIfConfigured(InitMojo mojo) throws Exception { String helmInitCommand = helmCommands.get(0); assertTrue(helmInitCommand.contains("--skip-refresh"), "Option 'skip-refresh' expected"); } + + @Test + public void verifyLocalHelmBinaryUsage(InitMojo mojo) throws MojoExecutionException { + + final URL resource = this.getClass().getResource("helm.tar.gz"); + final String helmExecutableDir = new File(resource.getFile()).getParent(); + mojo.callCli("tar -xf " + + helmExecutableDir + + File.separator + // flatten directory structure using --strip to get helm executeable on basedir, see https://www.systutorials.com/docs/linux/man/1-tar/#lbAS + + "helm.tar.gz --strip=1 --directory=" + + helmExecutableDir, "Unable to unpack helm to " + helmExecutableDir, false); + + + // configure mojo + mojo.setUseLocalHelmBinary(true); + mojo.setHelmExecutableDirectory(helmExecutableDir); + + // execute + mojo.execute(); + } } \ No newline at end of file diff --git a/src/test/resources/com/kiwigrid/helm/maven/plugin/helm.tar.gz b/src/test/resources/com/kiwigrid/helm/maven/plugin/helm.tar.gz new file mode 100644 index 00000000..39f5b22f Binary files /dev/null and b/src/test/resources/com/kiwigrid/helm/maven/plugin/helm.tar.gz differ