diff --git a/.github/workflows/integration-tests-with-dockerio.yml b/.github/workflows/integration-tests-with-dockerio.yml index dac831d13..405f3bb33 100644 --- a/.github/workflows/integration-tests-with-dockerio.yml +++ b/.github/workflows/integration-tests-with-dockerio.yml @@ -21,7 +21,6 @@ on: push: branches: - main - pull_request: jobs: cache: @@ -69,7 +68,7 @@ jobs: java-version: '8' distribution: 'adopt' - name: Dockerhub login - if: ${{secrets.DOCKERIO_USERNAME != null && secerts.DOCKERIO_PASSWORD != null }} + if: ${{ secrets.DOCKERIO_USERNAME != null && secrets.DOCKERIO_PASSWORD != null }} uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERIO_USERNAME }} @@ -126,7 +125,7 @@ jobs: java-version: '8' distribution: 'adopt' - name: Dockerhub login - if: ${{secrets.DOCKERIO_USERNAME != null && secerts.DOCKERIO_PASSWORD != null }} + if: ${{ secrets.DOCKERIO_USERNAME != null && secrets.DOCKERIO_PASSWORD != null }} uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERIO_USERNAME }} @@ -147,3 +146,93 @@ jobs: with: name: ci-manifests path: manifests-ocp-jvm${{ matrix.java }}.zip + + tekton: + name: Tekton Build + needs: cache + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + kubernetes: [ v1.22.2 ] + tekton: [ v0.39.0 ] + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Get Date + id: get-date + run: | + echo "::set-output name=date::$(/bin/date -u "+%Y-%m")" + shell: bash + - name: Cache .m2 registry + uses: actions/cache@v2.1.6 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ steps.get-date.outputs.date }} + restore-keys: ${{ runner.os }}-maven- + - name: Setup Minikube-Kubernetes + uses: manusa/actions-setup-minikube@v2.7.1 + with: + minikube version: v1.23.2 + kubernetes version: ${{ matrix.kubernetes }} + github token: ${{ secrets.GITHUB_TOKEN }} + driver: 'docker' + start args: '--force' + - name: Setup Java 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Dockerhub login + if: ${{ secrets.DOCKERIO_USERNAME != null && secrets.DOCKERIO_PASSWORD != null }} + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERIO_USERNAME }} + password: ${{ secrets.DOCKERIO_PASSWORD }} + - name: Install Tekton + run: | + kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/${{ matrix.tekton }}/release.yaml + - name: Build + run: | + ./mvnw -B clean install -Dformat.skip=true -DskipTests + - name: Run Tekton Integration Tests + run: | + kubectl create namespace tekton + kubectl config set-context --current --namespace=tekton + cd examples/spring-boot-with-tekton-example + # Build example + mvn clean install -DskipTests -Ddekorate.docker.registry=docker.io -Ddekorate.docker.group=dekorateio -Ddekorate.tekton.use-local-docker-config-json=true -Ddekorate.tekton.projectBuilderArguments=clean,install,-Pwith-examples,-DskipTests,-Dformat.skip=true,-pl,examples/spring-boot-with-tekton-example,-am + # Install Tekton Task manifests + kubectl apply -f target/classes/META-INF/dekorate/tekton-task.yml + # Trigger Task workflow + kubectl apply -f target/classes/META-INF/dekorate/tekton-task-run.yml + # Wait to finish + kubectl wait --for=condition=Succeeded --timeout=800s TaskRun/spring-boot-with-tekton-example-run-now + kubectl wait deployment spring-boot-with-tekton-example --for condition=Available=True --timeout=180s + # Verify application + RESULT=$(kubectl exec $(kubectl get pod -l app.kubernetes.io/name=spring-boot-with-tekton-example -o name) -- wget -qO- http://localhost:9090) + if [[ "$RESULT" = *"Hello world"* ]] + then + exit 0 + fi + echo "Application is not working. Result was: $RESULT" + exit 1 + - name: Print logs at failures + if: failure() + run: | + kubectl config set-context --current --namespace=tekton + echo "kubectl get pods: " + kubectl get pods + echo "kubectl describe TaskRun/spring-boot-with-tekton-example-run-now: " + kubectl describe TaskRun/spring-boot-with-tekton-example-run-now + echo "kubectl logs spring-boot-with-tekton-example-run-now-pod --all-containers --max-log-requests 10: " + kubectl logs spring-boot-with-tekton-example-run-now-pod --all-containers --max-log-requests 10 + - name: Zip Artifacts + if: failure() + run: | + zip -R manifests-tekton-jvm${{ matrix.java }}.zip 'classes/META-INF/dekorate/*' + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ci-manifests + path: manifests-tekton-jvm${{ matrix.java }}.zip diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 8a9b7a911..0011bc037 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -101,7 +101,6 @@ jobs: with: name: ci-manifests path: manifests-k8s-jvm${{ matrix.java }}.zip - openshift: name: Openshift Build needs: cache @@ -151,3 +150,82 @@ jobs: with: name: ci-manifests path: manifests-ocp-jvm${{ matrix.java }}.zip + tekton: + name: Tekton Build + needs: cache + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + tekton: [ v0.39.0 ] + steps: + - name: Checkout + uses: actions/checkout@v2.3.4 + - name: Get Date + id: get-date + run: | + echo "::set-output name=date::$(/bin/date -u "+%Y-%m")" + shell: bash + - name: Cache .m2 registry + uses: actions/cache@v2.1.6 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ steps.get-date.outputs.date }} + restore-keys: ${{ runner.os }}-maven- + - name: Kubernetes KinD Cluster + uses: container-tools/kind-action@v1 + with: + version: v0.18.0 + registry: true + - name: Setup Java 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Tekton + run: | + kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/${{ matrix.tekton }}/release.yaml || true + - name: Build + run: | + ./mvnw -B clean install -Dformat.skip=true -DskipTests + - name: Run Tekton Integration Tests + run: | + kubectl create namespace tekton + kubectl config set-context --current --namespace=tekton + cd examples/spring-boot-with-tekton-example + # Build example + mvn clean install -DskipTests -Ddekorate.docker.registry=$KIND_REGISTRY -Ddekorate.tekton.imageRegistryInsecure=true -Ddekorate.tekton.projectBuilderArguments=clean,install,-Pwith-examples,-DskipTests,-Dformat.skip=true,-pl,examples/spring-boot-with-tekton-example,-am + # Install Tekton Task manifests + kubectl apply -f target/classes/META-INF/dekorate/tekton-task.yml + # Trigger Task workflow + kubectl apply -f target/classes/META-INF/dekorate/tekton-task-run.yml + # Wait to finish + kubectl wait --for=condition=Succeeded --timeout=800s TaskRun/spring-boot-with-tekton-example-run-now + kubectl wait deployment spring-boot-with-tekton-example --for condition=Available=True --timeout=180s + # Verify application + RESULT=$(kubectl exec $(kubectl get pod -l app.kubernetes.io/name=spring-boot-with-tekton-example -o name) -- wget -qO- http://localhost:9090) + if [[ "$RESULT" = *"Hello world"* ]] + then + exit 0 + fi + echo "Application is not working. Result was: $RESULT" + exit 1 + - name: Print logs at failures + if: failure() + run: | + kubectl config set-context --current --namespace=tekton + echo "kubectl get pods: " + kubectl get pods + echo "kubectl describe TaskRun/spring-boot-with-tekton-example-run-now: " + kubectl describe TaskRun/spring-boot-with-tekton-example-run-now + echo "kubectl logs spring-boot-with-tekton-example-run-now-pod --all-containers --max-log-requests 10: " + kubectl logs spring-boot-with-tekton-example-run-now-pod --all-containers --max-log-requests 10 + - name: Zip Artifacts + if: failure() + run: | + zip -R manifests-tekton-jvm${{ matrix.java }}.zip 'classes/META-INF/dekorate/*' + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: ci-manifests + path: manifests-tekton-jvm${{ matrix.java }}.zip diff --git a/annotations/knative-annotations/src/main/java/io/dekorate/knative/config/DefaultKnativeConfigGenerator.java b/annotations/knative-annotations/src/main/java/io/dekorate/knative/config/DefaultKnativeConfigGenerator.java index 0b5d3c547..8ad99c934 100644 --- a/annotations/knative-annotations/src/main/java/io/dekorate/knative/config/DefaultKnativeConfigGenerator.java +++ b/annotations/knative-annotations/src/main/java/io/dekorate/knative/config/DefaultKnativeConfigGenerator.java @@ -19,6 +19,7 @@ import io.dekorate.WithProject; import io.dekorate.config.DefaultConfiguration; import io.dekorate.kubernetes.configurator.ApplyDeployToApplicationConfiguration; +import io.dekorate.kubernetes.configurator.ApplyImagePullSecretConfiguration; import io.dekorate.project.ApplyProjectInfo; public class DefaultKnativeConfigGenerator implements KnativeConfigGenerator, WithProject { @@ -28,6 +29,7 @@ public class DefaultKnativeConfigGenerator implements KnativeConfigGenerator, Wi public DefaultKnativeConfigGenerator(ConfigurationRegistry configurationRegistry) { this.configurationRegistry = configurationRegistry; on(new DefaultConfiguration(KnativeConfig.newKnativeConfigBuilderFromDefaults())); + this.configurationRegistry.add(new ApplyImagePullSecretConfiguration()); this.configurationRegistry.add(new ApplyProjectInfo(getProject())); this.configurationRegistry.add(new ApplyDeployToApplicationConfiguration()); } diff --git a/annotations/kubernetes-annotations/src/main/java/io/dekorate/kubernetes/config/DefaultKubernetesConfigGenerator.java b/annotations/kubernetes-annotations/src/main/java/io/dekorate/kubernetes/config/DefaultKubernetesConfigGenerator.java index 23113dc84..2936ef8da 100644 --- a/annotations/kubernetes-annotations/src/main/java/io/dekorate/kubernetes/config/DefaultKubernetesConfigGenerator.java +++ b/annotations/kubernetes-annotations/src/main/java/io/dekorate/kubernetes/config/DefaultKubernetesConfigGenerator.java @@ -18,6 +18,7 @@ import io.dekorate.ConfigurationRegistry; import io.dekorate.config.DefaultConfiguration; import io.dekorate.kubernetes.configurator.ApplyDeployToApplicationConfiguration; +import io.dekorate.kubernetes.configurator.ApplyImagePullSecretConfiguration; import io.dekorate.kubernetes.configurator.PopulateWebPort; import io.dekorate.project.ApplyProjectInfo; @@ -28,6 +29,7 @@ public class DefaultKubernetesConfigGenerator implements KubernetesConfigGenerat public DefaultKubernetesConfigGenerator(ConfigurationRegistry configurationRegistry) { this.configurationRegistry = configurationRegistry; this.configurationRegistry.add(new ApplyProjectInfo(getProject())); + this.configurationRegistry.add(new ApplyImagePullSecretConfiguration()); this.configurationRegistry.add(new PopulateWebPort()); this.configurationRegistry.add(new ApplyDeployToApplicationConfiguration()); add(new DefaultConfiguration(KubernetesConfig.newKubernetesConfigBuilderFromDefaults())); diff --git a/annotations/openshift-annotations/src/main/java/io/dekorate/openshift/config/DefaultOpenshiftConfigGenerator.java b/annotations/openshift-annotations/src/main/java/io/dekorate/openshift/config/DefaultOpenshiftConfigGenerator.java index 6c6526e4b..5dd4945a3 100644 --- a/annotations/openshift-annotations/src/main/java/io/dekorate/openshift/config/DefaultOpenshiftConfigGenerator.java +++ b/annotations/openshift-annotations/src/main/java/io/dekorate/openshift/config/DefaultOpenshiftConfigGenerator.java @@ -19,6 +19,7 @@ import io.dekorate.WithProject; import io.dekorate.config.DefaultConfiguration; import io.dekorate.kubernetes.configurator.ApplyDeployToApplicationConfiguration; +import io.dekorate.kubernetes.configurator.ApplyImagePullSecretConfiguration; import io.dekorate.kubernetes.configurator.PopulateWebPort; import io.dekorate.project.ApplyProjectInfo; @@ -30,6 +31,7 @@ public DefaultOpenshiftConfigGenerator(ConfigurationRegistry configurationRegist this.configurationRegistry = configurationRegistry; on(new DefaultConfiguration(OpenshiftConfig.newOpenshiftConfigBuilderFromDefaults())); + this.configurationRegistry.add(new ApplyImagePullSecretConfiguration()); this.configurationRegistry.add(new ApplyProjectInfo(getProject())); this.configurationRegistry.add(new PopulateWebPort()); this.configurationRegistry.add(new ApplyDeployToApplicationConfiguration()); diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/annotation/TektonApplication.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/annotation/TektonApplication.java index 0d70f049b..bc127b05b 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/annotation/TektonApplication.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/annotation/TektonApplication.java @@ -207,6 +207,13 @@ */ String[] imagePushArguments() default {}; + /* + * If to use an unsecure registry or not. + * + * @return The container image will be configured to work with an insecure registry. + */ + boolean imageRegistryInsecure() default false; + /** * The relative path to the Dockerfile. * diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDeployStepDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDeployStepDecorator.java index d7c3c1ee0..000e7f50b 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDeployStepDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDeployStepDecorator.java @@ -17,6 +17,8 @@ package io.dekorate.tekton.decorator; +import java.nio.file.Path; + import io.dekorate.kubernetes.decorator.Decorator; import io.fabric8.tekton.pipeline.v1beta1.Step; import io.fabric8.tekton.pipeline.v1beta1.StepBuilder; @@ -29,18 +31,18 @@ public class AddDeployStepDecorator extends NamedTaskDecorator implements StepDe private static final String PATH_TO_YML_PARAM_NAME = "pathToYml"; private final String stepName; - private final String projectName; + private final Path relativePath; private final String deployerImage; - public AddDeployStepDecorator(String taskName, String stepName, String projectName, String deployerImage) { + public AddDeployStepDecorator(String taskName, String stepName, Path relativePath, String deployerImage) { super(taskName); this.stepName = stepName; - this.projectName = projectName; + this.relativePath = relativePath; this.deployerImage = deployerImage; } - public AddDeployStepDecorator(String taskName, String projectName, String deployerImage) { - this(taskName, STEP_NAME, projectName, deployerImage); + public AddDeployStepDecorator(String taskName, Path relativePath, String deployerImage) { + this(taskName, STEP_NAME, relativePath, deployerImage); } @Override @@ -50,7 +52,8 @@ public void andThenVisit(TaskSpecFluent taskSpec) { public Step createDeployStep() { return new StepBuilder().withName(stepName).withImage(deployerImage).withCommand(DEPLOY_CMD) - .withArgs(new String[] { "apply", "-f", param(PATH_TO_YML_PARAM_NAME) }).withWorkingDir(sourcePath(projectName)) + .withArgs(new String[] { "apply", "-f", param(PATH_TO_YML_PARAM_NAME) }) + .withWorkingDir(sourcePath(relativePath.toString())) .build(); } diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeTaskDecorator.java similarity index 77% rename from annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeDecorator.java rename to annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeTaskDecorator.java index 5ffef04b7..f36876e07 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddDockerSocketVolumeTaskDecorator.java @@ -19,13 +19,13 @@ import io.dekorate.tekton.step.ImageStep; -public class AddDockerSocketVolumeDecorator extends AddHostPathVolumeDecorator { +public class AddDockerSocketVolumeTaskDecorator extends AddHostPathVolumeTaskDecorator { - public AddDockerSocketVolumeDecorator(String taskName) { + public AddDockerSocketVolumeTaskDecorator(String taskName) { super(taskName, ImageStep.DOCKER_SOCKET_NAME, ImageStep.DOCKER_SOCKET_PATH, ImageStep.DOCKER_SOCKET_TYPE); } - public AddDockerSocketVolumeDecorator(String taskName, String name, String path, String type) { + public AddDockerSocketVolumeTaskDecorator(String taskName, String name, String path, String type) { super(taskName, name, path, type); } } diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeTaskDecorator.java similarity index 91% rename from annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeDecorator.java rename to annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeTaskDecorator.java index 8c5ca55db..a33d2847b 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddHostPathVolumeTaskDecorator.java @@ -23,13 +23,13 @@ import io.fabric8.tekton.pipeline.v1beta1.TaskSpecFluent; @Description("Add a persistent host path volume to the specified task.") -public class AddHostPathVolumeDecorator extends NamedTaskDecorator { +public class AddHostPathVolumeTaskDecorator extends NamedTaskDecorator { private final String name; private final String path; private final String type; - public AddHostPathVolumeDecorator(String taskName, String name, String path, String type) { + public AddHostPathVolumeTaskDecorator(String taskName, String name, String path, String type) { super(taskName); this.name = name; this.path = path; diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddImageBuildStepDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddImageBuildStepDecorator.java index 9d34899c9..578caf886 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddImageBuildStepDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddImageBuildStepDecorator.java @@ -17,6 +17,8 @@ package io.dekorate.tekton.decorator; +import java.nio.file.Path; + import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.tekton.step.ImageBuildStep; import io.dekorate.utils.Strings; @@ -33,24 +35,24 @@ public class AddImageBuildStepDecorator extends NamedTaskDecorator implements St private static final String DOCKER_CONFIG_DEFAULT = "/tekton/home/.docker"; private final String stepName; - private final String projectName; + private final Path relativePath; private final String image; private final String command; private final String[] args; - public AddImageBuildStepDecorator(String taskName, String projectName) { - this(taskName, ImageBuildStep.ID, projectName); + public AddImageBuildStepDecorator(String taskName, Path relativePath) { + this(taskName, ImageBuildStep.ID, relativePath); } - public AddImageBuildStepDecorator(String taskName, String stepName, String projectName) { - this(taskName, stepName, projectName, BUILDER_IMAGE_REF, BUILDER_COMMAND_REF, BUILDER_ARGS_REF); + public AddImageBuildStepDecorator(String taskName, String stepName, Path relativePath) { + this(taskName, stepName, relativePath, BUILDER_IMAGE_REF, BUILDER_COMMAND_REF, BUILDER_ARGS_REF); } - public AddImageBuildStepDecorator(String taskName, String stepName, String projectName, String image, String command, + public AddImageBuildStepDecorator(String taskName, String stepName, Path relativePath, String image, String command, String... args) { super(taskName); this.stepName = stepName; - this.projectName = projectName; + this.relativePath = relativePath; this.image = Strings.isNotNullOrEmpty(image) ? image : BUILDER_IMAGE_REF; this.command = Strings.isNotNullOrEmpty(command) ? command : BUILDER_COMMAND_REF; this.args = args != null && args.length != 0 ? args : new String[] { BUILDER_ARGS_REF }; @@ -64,7 +66,7 @@ public void andThenVisit(TaskSpecFluent taskSpec) { .addToEnv(new EnvVarBuilder().withName(DOCKER_CONFIG).withValue(DOCKER_CONFIG_DEFAULT).build()) .withCommand(command) .withArgs(args) - .withWorkingDir(sourcePath(projectName)) + .withWorkingDir(sourcePath()) .endStep(); } diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddKanikoImageBuildStepDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddKanikoImageBuildStepDecorator.java index 9cff0358f..8e063137c 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddKanikoImageBuildStepDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddKanikoImageBuildStepDecorator.java @@ -17,6 +17,8 @@ package io.dekorate.tekton.decorator; +import static io.dekorate.tekton.step.KanikoBuildStep.FORCE_ARG; + import io.dekorate.kubernetes.decorator.Decorator; import io.dekorate.utils.Strings; import io.fabric8.kubernetes.api.model.EnvVarBuilder; @@ -61,7 +63,7 @@ public AddKanikoImageBuildStepDecorator(String taskName, String stepName, String this.image = Strings.isNotNullOrEmpty(image) ? image : BUILDER_IMAGE_REF; this.command = Strings.isNotNullOrEmpty(command) ? command : KANIKO_CMD; this.args = args != null && args.length != 0 ? args - : new String[] { DOCKERFILE_ARG, CONTEXT_ARG, IMAGE_DESTINATION_ARG, VERBOSITY_DEBUG }; + : new String[] { FORCE_ARG, DOCKERFILE_ARG, CONTEXT_ARG, IMAGE_DESTINATION_ARG, VERBOSITY_DEBUG }; } @Override diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddProjectBuildStepDecorator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddProjectBuildStepDecorator.java index 8cb304eaf..babb56de1 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddProjectBuildStepDecorator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/decorator/AddProjectBuildStepDecorator.java @@ -60,7 +60,7 @@ public void andThenVisit(TaskSpecFluent taskSpec) { .withImage(image) .withCommand(command) .withArgs(arguments) - .withWorkingDir(sourcePath(projectName)) + .withWorkingDir(sourcePath()) .endStep(); } diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/manifest/TektonManifestGenerator.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/manifest/TektonManifestGenerator.java index 9087ba115..6a8aa37d9 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/manifest/TektonManifestGenerator.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/manifest/TektonManifestGenerator.java @@ -17,6 +17,8 @@ import static io.dekorate.tekton.util.TektonUtils.getContextPath; import static io.dekorate.utils.Git.REMOTE_PATTERN; +import static io.dekorate.utils.Git.sanitizeRemoteUrl; +import static io.dekorate.utils.Maps.getDekoratePropertyFromSystem; import java.nio.file.Path; import java.nio.file.Paths; @@ -57,7 +59,7 @@ import io.dekorate.tekton.decorator.AddArrayParamToTaskDecorator; import io.dekorate.tekton.decorator.AddDeployStepDecorator; import io.dekorate.tekton.decorator.AddDockerSocketMountDecorator; -import io.dekorate.tekton.decorator.AddDockerSocketVolumeDecorator; +import io.dekorate.tekton.decorator.AddDockerSocketVolumeTaskDecorator; import io.dekorate.tekton.decorator.AddImageBuildStepDecorator; import io.dekorate.tekton.decorator.AddImagePushStepDecorator; import io.dekorate.tekton.decorator.AddProjectBuildStepDecorator; @@ -139,7 +141,7 @@ public class TektonManifestGenerator implements ManifestGenerator, private static final String DASH = "-"; private static final String MAVEN_LOCAL_REPO_SYS_PROPERTY = "-Dmaven.repo.local=%s"; - private static final String IMAGE_PULL_SECRETS_SYS_PROPERTY = "-Ddekorate.%s.image-pull-secrets="; + private static final String IMAGE_PULL_SECRETS_SYS_PROPERTY = "-Ddekorate.image-pull-secrets="; private static final String USER_NAME_SYS_PROP = "-Duser.name="; private static final String TEKTON = "tekton"; @@ -221,19 +223,21 @@ public void generateCommon(String group, TektonConfig config, ImageConfiguration BuildInfo build = config.getProject().getBuildInfo(); BuildImage projectBuildImage = Strings.isNotNullOrEmpty(config.getProjectBuilderImage()) && Strings.isNotNullOrEmpty(config.getProjectBuilderCommand()) - ? new BuildImage(config.getProjectBuilderImage(), config.getProjectBuilderCommand(), - config.getProjectBuilderArguments()) + ? new BuildImage(config.getProjectBuilderImage(), config.getProjectBuilderCommand()) : BuildImage.find(build.getBuildTool(), build.getBuildToolVersion(), Jvm.getVersion(), null) .orElseThrow(() -> new IllegalStateException("No project builder image was found!")); resourceRegistry.decorate(group, - new AddResourceInputToTaskDecorator(projectBuildTaskName, GIT, GIT_SOURCE, "/source/" + config.getName())); + new AddResourceInputToTaskDecorator(projectBuildTaskName, GIT, GIT_SOURCE, "/source")); resourceRegistry.decorate(group, new AddStringParamToTaskDecorator(projectBuildTaskName, ProjectBuildStep.IMAGE_PARAM_NAME, ProjectBuildStep.IMAGE_PARAM_DESCRIPTION, projectBuildImage.getImage())); resourceRegistry.decorate(group, new AddStringParamToTaskDecorator(projectBuildTaskName, ProjectBuildStep.COMMAND_PARAM_NAME, ProjectBuildStep.COMMAND_PARAM_DESCRIPTION, projectBuildImage.getCommand())); resourceRegistry.decorate(group, new AddArrayParamToTaskDecorator(projectBuildTaskName, ProjectBuildStep.ARGS_PARAM_NAME, - ProjectBuildStep.ARGS_PARAM_DESCRIPTION, projectBuildImage.getArguments())); + ProjectBuildStep.ARGS_PARAM_DESCRIPTION, + config.getProjectBuilderArguments() == null || config.getProjectBuilderArguments().length == 0 + ? projectBuildImage.getArguments() + : config.getProjectBuilderArguments())); //This is needed so that we pass a sensible group to the `in-cluster build`. resourceRegistry.decorate(group, @@ -252,7 +256,8 @@ public void generateCommon(String group, TektonConfig config, ImageConfiguration .withBuildArguments(config.getImageBuildArguments()) .withPushImage(config.getImagePushImage()) .withPushCommand(config.getImagePushCommand()) - .withPushArguments(config.getImagePushArguments()); + .withPushArguments(config.getImagePushArguments()) + .withRegistryInsecure(config.getImageRegistryInsecure()); String imageBuildImage = imageBuildStep.getBuildImage(); String imageBuildCommand = imageBuildStep.getBuildCommand(); @@ -268,12 +273,17 @@ public void generateCommon(String group, TektonConfig config, ImageConfiguration new AddStringParamToTaskDecorator(imageBuildTaskName, ImageBuildStep.PATH_TO_DOCKERFILE_PARAM_NAME, ImageBuildStep.PATH_TO_DOCKERFILE_PARAM_DESCRIPTION, config.getDockerfile())); - resourceRegistry.decorate(group, new AddImageBuildStepDecorator(imageBuildTaskName, config.getName())); + Path relativePath = Paths.get(config.getName()); + if (config.getProject().getScmInfo() != null) { + relativePath = config.getProject().getScmInfo().getRoot().relativize(config.getProject().getRoot()); + } + + resourceRegistry.decorate(group, new AddImageBuildStepDecorator(imageBuildTaskName, relativePath)); resourceRegistry.decorate(group, new AddResourceOutputToTaskDecorator(imageBuildTaskName, IMAGE, IMAGE)); if (imageBuildStep.isDockerSocketRequired()) { resourceRegistry.decorate(group, new AddDockerSocketMountDecorator(imageBuildTaskName, ImageBuildStep.ID)); - resourceRegistry.decorate(group, new AddDockerSocketVolumeDecorator(imageBuildTaskName)); + resourceRegistry.decorate(group, new AddDockerSocketVolumeTaskDecorator(imageBuildTaskName)); } if (imageBuildStep.isPushRequired()) { @@ -291,14 +301,14 @@ public void generateCommon(String group, TektonConfig config, ImageConfiguration if (imageBuildStep.isDockerSocketRequired()) { resourceRegistry.decorate(group, new AddDockerSocketMountDecorator(imagePushTaskName, ImagePushStep.ID)); - resourceRegistry.decorate(group, new AddDockerSocketVolumeDecorator(imagePushTaskName)); + resourceRegistry.decorate(group, new AddDockerSocketVolumeTaskDecorator(imagePushTaskName)); } } //Deploy Task resourceRegistry.decorate(group, new AddStringParamToTaskDecorator(deployTaskName, DeployStep.PATH_TO_YML_PARAM_NAME, DeployStep.PATH_TO_YML_PARAM_DESCRIPTION, DeployStep.PATH_TO_YML_PARAM_DEFAULT)); - resourceRegistry.decorate(group, new AddDeployStepDecorator(deployTaskName, config.getName(), config.getDeployerImage())); + resourceRegistry.decorate(group, new AddDeployStepDecorator(deployTaskName, relativePath, config.getDeployerImage())); Map annotations = new HashMap() { { @@ -316,46 +326,53 @@ public void generateCommon(String group, TektonConfig config, ImageConfiguration String.format(MAVEN_LOCAL_REPO_SYS_PROPERTY, m2Path))); } + String generatedDeploymentName = group.equals(TEKTON_TASK) ? monolithTaskName : config.getName(); + String generatedServiceAccount = config.getName(); + resourceRegistry.decorate(group, new AddServiceAccountResourceDecorator(generatedDeploymentName, generatedServiceAccount)); + resourceRegistry.decorate(group, + new AddRoleBindingResourceDecorator(generatedDeploymentName + ":deployer", + generatedServiceAccount, "pipeline-deployer", AddRoleBindingResourceDecorator.RoleKind.Role)); + if (Strings.isNotNullOrEmpty(config.getImagePushServiceAccount())) { resourceRegistry.decorate(group + DASH + RUN, new AddServiceAccountToPipelineDecorator(IMAGE + DASH + BUILD, config.getImagePushServiceAccount())); } else { - String generatedServiceAccount = config.getName(); - resourceRegistry.decorate(group + DASH + RUN, - new AddServiceAccountToPipelineDecorator(IMAGE + DASH + BUILD, generatedServiceAccount)); if (Strings.isNotNullOrEmpty(config.getImagePushSecret())) { resourceRegistry.decorate(group, new AddSecretToServiceAccountDecorator(generatedServiceAccount, config.getImagePushSecret())); resourceRegistry.decorate(group, new AddToArgsDecorator(projectBuildTaskName, projectBuildStepName, - String.format(IMAGE_PULL_SECRETS_SYS_PROPERTY, group) + config.getImagePushSecret())); + IMAGE_PULL_SECRETS_SYS_PROPERTY + config.getImagePushSecret())); } else if (config.isUseLocalDockerConfigJson()) { - String generatedSecret = config.getName() + "-registry-credentials"; + String generatedSecret = getGeneratedSecretWithRegistryCredentials(config); Path dockerConfigJson = Paths.get(System.getProperty("user.home"), ".docker", "config.json"); if (!dockerConfigJson.toFile().exists()) { throw new IllegalStateException( "User requested to use the local `.docker/config.json` file, but it doesn't exist!"); } else { - LOGGER.warning(dockerConfigJson.toAbsolutePath().normalize().toString() + LOGGER.warning(dockerConfigJson.toAbsolutePath().normalize() + " is going to be added as part of Secret: " + generatedSecret); } + resourceRegistry.decorate(group, new AddSecretToServiceAccountDecorator(generatedServiceAccount, generatedSecret)); resourceRegistry.decorate(group, new AddToArgsDecorator(projectBuildTaskName, projectBuildStepName, - String.format(IMAGE_PULL_SECRETS_SYS_PROPERTY, group) + generatedSecret)); + IMAGE_PULL_SECRETS_SYS_PROPERTY + generatedSecret)); resourceRegistry.decorate(group, new AddDockerConfigJsonSecretDecorator(generatedSecret, dockerConfigJson, annotations)); - resourceRegistry.decorate(group, new AddSecretToServiceAccountDecorator(generatedServiceAccount, generatedSecret)); } else if (Strings.isNotNullOrEmpty(config.getRegistryUsername()) && Strings.isNotNullOrEmpty(config.getRegistryPassword())) { - String generatedSecret = config.getName() + "-registry-credentials"; + String generatedSecret = getGeneratedSecretWithRegistryCredentials(config); + resourceRegistry.decorate(group, new AddSecretToServiceAccountDecorator(generatedServiceAccount, generatedSecret)); resourceRegistry.decorate(group, new AddToArgsDecorator(projectBuildTaskName, projectBuildStepName, - String.format(IMAGE_PULL_SECRETS_SYS_PROPERTY, group) + generatedSecret)); + IMAGE_PULL_SECRETS_SYS_PROPERTY + generatedSecret)); resourceRegistry.decorate(group, new AddDockerConfigJsonSecretDecorator(generatedSecret, config.getRegistry(), config.getRegistryUsername(), config.getRegistryPassword(), annotations)); - resourceRegistry.decorate(group, new AddSecretToServiceAccountDecorator(generatedServiceAccount, generatedSecret)); } else { LOGGER.error( "An existing builder image service account or secret is required! Alternatively, you can specify a registry username and password!"); } } + + // replicate all the dekorate arguments to the task + appendDekoratePropertiesToProjectBuild(group, projectBuildTaskName, projectBuildStepName); } public void generatePipelineResources(TektonConfig config) { @@ -388,10 +405,6 @@ public void generatePipelineResources(TektonConfig config) { resourceRegistry.decorate(TEKTON_PIPELINE_RUN, new AddPvcToPipelineRunDecorator(null, PIPELINE_M2_WS, m2WorkspaceClaimName, false)); } - - resourceRegistry.decorate(TEKTON_PIPELINE, new AddServiceAccountResourceDecorator(imageBuildTaskName)); - resourceRegistry.decorate(TEKTON_PIPELINE, new AddRoleBindingResourceDecorator(imageBuildTaskName + ":deployer", - config.getName(), "pipeline-deployer", AddRoleBindingResourceDecorator.RoleKind.Role)); } public void generateTaskResources(TektonConfig config) { @@ -404,10 +417,6 @@ public void generateTaskResources(TektonConfig config) { resourceRegistry.decorate(TEKTON_TASK_RUN, new AddPvcToTaskRunDecorator(null, config.getM2Workspace(), m2WorkspaceClaimName, false)); } - - resourceRegistry.decorate(TEKTON_TASK, new AddServiceAccountResourceDecorator(monolithTaskName)); - resourceRegistry.decorate(TEKTON_TASK, new AddRoleBindingResourceDecorator(monolithTaskName + ":deployer", config.getName(), - "pipeline-deployer", AddRoleBindingResourceDecorator.RoleKind.Role)); } public PipelineResource createGitResource(TektonConfig config) { @@ -447,7 +456,7 @@ public PipelineResource createGitResource(TektonConfig config) { .withType(GIT) .addNewParam() .withName(URL) - .withValue(repoUrl) + .withValue(sanitizeRemoteUrl(repoUrl)) .endParam() .addNewParam() .withName(REVISION) @@ -563,7 +572,7 @@ public PipelineRun createPipelineRun(TektonConfig config) { .withName(config.getName() + DASH + RUN + DASH + NOW) .endMetadata() .withNewSpec() - .withServiceAccountName(imageBuildTaskName(config)) + .withServiceAccountName(config.getName()) .addNewWorkspace().withName(PIPELINE_SOURCE_WS) .withNewPersistentVolumeClaim(sourceWorkspaceClaimName(config), false) .endWorkspace() @@ -587,7 +596,7 @@ public TaskRun createTaskRun(TektonConfig config) { .withName(config.getName() + DASH + RUN + DASH + NOW) .endMetadata() .withNewSpec() - .withServiceAccountName(monolithTaskName(config)) + .withServiceAccountName(config.getName()) .addNewWorkspace().withName(config.getSourceWorkspace()) .withEmptyDir(new EmptyDirVolumeSourceBuilder().withMedium("Memory").build()).endWorkspace() .withNewTaskRef().withName(monolithTaskName(config)).endTaskRef() @@ -676,6 +685,20 @@ public ConfigurationSupplier getFallbackConfig() { new TektonConfigBuilder().accept(new ApplyDeployToApplicationConfiguration()).accept(new ApplyProjectInfo(p))); } + private void appendDekoratePropertiesToProjectBuild(String group, String projectBuildTaskName, String projectBuildStepName) { + for (Map.Entry property : getDekoratePropertyFromSystem().entrySet()) { + // makes sure we exclude the tekton properties here + if (!property.getKey().startsWith("dekorate.tekton")) { + resourceRegistry.decorate(group, new AddToArgsDecorator(projectBuildTaskName, projectBuildStepName, + "-D" + property.getKey() + "=" + property.getValue())); + } + } + } + + private static String getGeneratedSecretWithRegistryCredentials(TektonConfig config) { + return config.getName() + "-registry-credentials"; + } + private static ImageConfiguration getImageConfiguration(Project project, TektonConfig tektonConfig, ConfigurationRegistry configurationRegistry) { return configurationRegistry.getImageConfig(BuildServiceFactories.supplierMatches(project)).map(i -> merge(tektonConfig, i)) diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/BuildahBuildStep.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/BuildahBuildStep.java index 25fbce263..1a864d605 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/BuildahBuildStep.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/BuildahBuildStep.java @@ -115,6 +115,11 @@ public BuildahBuildStep withPushArguments(String[] pushArguments) { pushArguments != null && pushArguments.length > 0 ? pushArguments : this.pushArguments); } + @Override + public BuildahBuildStep withRegistryInsecure(boolean insecure) { + throw new IllegalArgumentException("The registry insecure configuration is not supported in Buildah yet!"); + } + @Override public boolean isPushRequired() { return true; diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/DockerBuildStep.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/DockerBuildStep.java index add139555..7839750da 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/DockerBuildStep.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/DockerBuildStep.java @@ -116,6 +116,11 @@ public DockerBuildStep withPushArguments(String[] pushArguments) { pushArguments != null && pushArguments.length > 0 ? pushArguments : this.pushArguments); } + @Override + public DockerBuildStep withRegistryInsecure(boolean insecure) { + throw new IllegalArgumentException("The registry insecure configuration is not supported in Docker yet!"); + } + @Override public boolean isPushRequired() { return true; diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/ImageBuildStep.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/ImageBuildStep.java index e0edb1370..dd0664333 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/ImageBuildStep.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/ImageBuildStep.java @@ -98,7 +98,7 @@ public ImageBuildStep(String context, String dockerfile, String buildImage, Stri * @param buildArguments the specified build arguments. * @return the updated step. */ - public abstract T withBuildArguments(String[] buildArugments); + public abstract T withBuildArguments(String[] buildArguments); /** * Create a new step using the specified push image. @@ -124,6 +124,14 @@ public ImageBuildStep(String context, String dockerfile, String buildImage, Stri */ public abstract T withPushArguments(String[] pushArguments); + /** + * Create a new step using an unsecure registry. + * + * @param insecure if to use an unsecure registry. + * @return the updated step. + */ + public abstract T withRegistryInsecure(boolean insecure); + /** * Returns true if the current build step requires an explicit push. * diff --git a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/KanikoBuildStep.java b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/KanikoBuildStep.java index 735c9c961..41148109b 100644 --- a/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/KanikoBuildStep.java +++ b/annotations/tekton-annotations/src/main/java/io/dekorate/tekton/step/KanikoBuildStep.java @@ -27,6 +27,9 @@ public class KanikoBuildStep extends ImageBuildStep { public static final String DOCKERFILE_ARG = "--dockerfile=$(inputs.params." + PATH_TO_DOCKERFILE_PARAM_NAME + ")"; public static final String CONTEXT_FORMAT = "--context=%s"; public static final String CONTEXT_ARG = "--context=$(params." + PATH_TO_CONTEXT_PARAM_NAME + ")"; + public static final String WORKSPACE_SOURCE = "/workspace/source"; + public static final String FORCE_ARG = "--force"; + public static final String INSECURE_ARG = "--insecure"; public static final String IMAGE_DESTINATION_ARG = "--destination=$(resources.outputs.image.url)"; public static final String BUILD_IMAGE_PARAM_DEFAULT = "gcr.io/kaniko-project/executor:v1.3.0"; @@ -35,29 +38,33 @@ public class KanikoBuildStep extends ImageBuildStep { public static final String PUSH_IMAGE_PARAM_DEFAULT = BUILD_COMMAND_PARAM_DEFAULT; public static final String PUSH_COMMAND_PARAM_DEFAULT = "/kaniko/executor"; + private boolean registryInsecure; + public KanikoBuildStep() { this(PATH_TO_CONTEXT_PARAM_DEFAULT, PATH_TO_DOCKERFILE_PARAM_DEFAULT, BUILD_IMAGE_PARAM_DEFAULT, BUILD_COMMAND_PARAM_DEFAULT, getDefaultBuildArguments(PATH_TO_DOCKERFILE_PARAM_DEFAULT, getContextPath()), - null, null, null); + null, null, null, false); } public KanikoBuildStep(String context, String dockerfile, String buildImage, String buildCommand, - String[] buildArguments, String pushImage, String pushCommand, String[] pushArguments) { + String[] buildArguments, String pushImage, String pushCommand, String[] pushArguments, boolean registryInsecure) { super(context, dockerfile, buildImage, buildCommand, buildArguments, pushImage, pushCommand, pushArguments); + + this.registryInsecure = registryInsecure; } @Override public KanikoBuildStep withContext(String context) { return new KanikoBuildStep(context, dockerfile, - buildImage, buildCommand, getDefaultBuildArguments(context, dockerfile), - null, null, null); + buildImage, buildCommand, getDefaultBuildArguments(dockerfile, context), + null, null, null, registryInsecure); } @Override public KanikoBuildStep withDockerfile(String dockerfile) { return new KanikoBuildStep(context, Strings.isNotNullOrEmpty(dockerfile) ? dockerfile : this.dockerfile, buildImage, buildCommand, getDefaultBuildArguments(context, dockerfile), - null, null, null); + null, null, null, registryInsecure); } @Override @@ -65,7 +72,7 @@ public KanikoBuildStep withBuildImage(String buildImage) { return new KanikoBuildStep(context, dockerfile, Strings.isNotNullOrEmpty(buildImage) ? buildImage : this.buildImage, buildCommand, buildArguments, pushImage, pushCommand, - pushArguments); + pushArguments, registryInsecure); } @Override @@ -74,7 +81,7 @@ public KanikoBuildStep withBuildCommand(String buildCommand) { buildImage, Strings.isNotNullOrEmpty(buildCommand) ? buildCommand : this.buildCommand, buildArguments, - pushImage, pushCommand, pushArguments); + pushImage, pushCommand, pushArguments, registryInsecure); } @Override @@ -82,7 +89,7 @@ public KanikoBuildStep withBuildArguments(String[] buildArguments) { return new KanikoBuildStep(context, dockerfile, buildImage, buildCommand, buildArguments != null && buildArguments.length > 0 ? buildArguments : this.buildArguments, - pushImage, pushCommand, pushArguments); + pushImage, pushCommand, pushArguments, registryInsecure); } @Override @@ -90,7 +97,7 @@ public KanikoBuildStep withPushImage(String pushImage) { return new KanikoBuildStep(context, dockerfile, buildImage, buildCommand, buildArguments, Strings.isNotNullOrEmpty(pushImage) ? pushImage : this.pushImage, - pushCommand, pushArguments); + pushCommand, pushArguments, registryInsecure); } @Override @@ -99,7 +106,8 @@ public KanikoBuildStep withPushCommand(String pushCommand) { buildImage, buildCommand, buildArguments, pushImage, Strings.isNotNullOrEmpty(pushCommand) ? pushCommand : this.pushCommand, - pushArguments); + pushArguments, + registryInsecure); } @Override @@ -107,7 +115,34 @@ public KanikoBuildStep withPushArguments(String[] pushArguments) { return new KanikoBuildStep(context, dockerfile, buildImage, buildCommand, buildArguments, pushImage, pushCommand, - pushArguments != null && pushArguments.length > 0 ? pushArguments : this.pushArguments); + pushArguments != null && pushArguments.length > 0 ? pushArguments : this.pushArguments, + registryInsecure); + } + + @Override + public KanikoBuildStep withRegistryInsecure(boolean insecure) { + return new KanikoBuildStep(context, dockerfile, + buildImage, buildCommand, buildArguments, + pushImage, pushCommand, + pushArguments, insecure); + } + + @Override + public String[] getPushArguments() { + if (registryInsecure) { + return appendArgumentTo(pushArguments); + } + + return super.getPushArguments(); + } + + @Override + public String[] getBuildArguments() { + if (registryInsecure) { + return appendArgumentTo(buildArguments); + } + + return super.getBuildArguments(); } @Override @@ -116,7 +151,19 @@ public boolean isPushRequired() { } private static String[] getDefaultBuildArguments(String context, String dockerfile) { - return new String[] { String.format(DOCKERFILE_FORMAT, dockerfile), String.format(CONTEXT_FORMAT, context), - IMAGE_DESTINATION_ARG }; + return new String[] { String.format(DOCKERFILE_FORMAT, dockerfile), + String.format(CONTEXT_FORMAT, WORKSPACE_SOURCE + context), + FORCE_ARG, IMAGE_DESTINATION_ARG }; + } + + private static String[] appendArgumentTo(String[] existing) { + if (existing != null && existing.length > 0) { + String[] arguments = new String[existing.length + 1]; + System.arraycopy(existing, 0, arguments, 0, existing.length); + arguments[existing.length] = INSECURE_ARG; + return arguments; + } else { + return new String[] { INSECURE_ARG }; + } } } diff --git a/core/src/main/java/io/dekorate/kubernetes/configurator/ApplyImagePullSecretConfiguration.java b/core/src/main/java/io/dekorate/kubernetes/configurator/ApplyImagePullSecretConfiguration.java index e69de29bb..962038750 100644 --- a/core/src/main/java/io/dekorate/kubernetes/configurator/ApplyImagePullSecretConfiguration.java +++ b/core/src/main/java/io/dekorate/kubernetes/configurator/ApplyImagePullSecretConfiguration.java @@ -0,0 +1,39 @@ +/** + * Copyright 2018 The original authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **/ + +package io.dekorate.kubernetes.configurator; + +import java.util.Arrays; + +import io.dekorate.kubernetes.config.BaseConfigFluent; +import io.dekorate.kubernetes.config.Configurator; +import io.dekorate.utils.Strings; + +public class ApplyImagePullSecretConfiguration extends Configurator { + + private static final String EMPTY = ""; + private static final String COMA = ","; + private static final String IMAGE_PULL_SECRETS = "dekorate.image-pull-secrets"; + + @Override + public void visit(BaseConfigFluent config) { + Arrays.stream(System.getProperty(IMAGE_PULL_SECRETS, EMPTY).split(COMA)) + .map(String::trim) + .filter(Strings::isNotNullOrEmpty) + .forEach(s -> config.addToImagePullSecrets(s)); + } +} diff --git a/core/src/main/java/io/dekorate/utils/Git.java b/core/src/main/java/io/dekorate/utils/Git.java index 8717fb002..695af0f38 100644 --- a/core/src/main/java/io/dekorate/utils/Git.java +++ b/core/src/main/java/io/dekorate/utils/Git.java @@ -131,7 +131,7 @@ public static Optional getRemoteUrl(Path path, String remote, boolean ht } } - static String sanitizeRemoteUrl(String remoteUrl) { + public static String sanitizeRemoteUrl(String remoteUrl) { final int atSign = remoteUrl.indexOf('@'); if (atSign > 0) { remoteUrl = remoteUrl.substring(atSign + 1); diff --git a/core/src/main/java/io/dekorate/utils/Maps.java b/core/src/main/java/io/dekorate/utils/Maps.java index 57d4a1ac8..66c55dcc6 100644 --- a/core/src/main/java/io/dekorate/utils/Maps.java +++ b/core/src/main/java/io/dekorate/utils/Maps.java @@ -38,6 +38,19 @@ public class Maps { private static final String PROPERTY_PREFIX = "dekorate"; private static final String MULTIPART_SEPARATOR_PATTERN = Pattern.quote("."); + public static Map getDekoratePropertyFromSystem() { + Map dekorateProperties = new HashMap<>(); + for (Map.Entry property : System.getProperties().entrySet()) { + if (property.getKey() instanceof String + && ((String) property.getKey()).startsWith(PROPERTY_PREFIX) + && property.getValue() instanceof String) { + dekorateProperties.put((String) property.getKey(), (String) property.getValue()); + } + } + + return dekorateProperties; + } + public static Map from(String... values) { if (values.length == 0) { return Collections.emptyMap(); diff --git a/docs/documentation/service-binding.md b/docs/documentation/service-binding.md index 0f4c99733..cbb414833 100644 --- a/docs/documentation/service-binding.md +++ b/docs/documentation/service-binding.md @@ -8,13 +8,16 @@ permalink: /docs/service-binding [Service Binding Operator](https://github.com/redhat-developer/service-binding-operator) enables the application developers to bind the services that are backed by Kubernetes operators to an application that is deployed in kubernetes without having to perform manual configuration. Dekorate supports generation of ServiceBinding CR. The generation of ServiceBinding CR is triggered by annotating one of your classes with `@ServiceBinding` annotation and by adding the below dependency to the project and when the project gets compiled, the annotation will trigger the generation of ServiceBinding CR in both json and yml formats under the `target/classes/META-INF/dekorate`. The name of the ServiceBinding CR would be the name of the `applicationName + "-binding"`, for example if the application name is `sample-app`, the binding name would be `sample-app-binding` + ``` io.dekorate servicebinding-annotations ``` + Here is the simple example of using ServiceBinding annotations in SpringBoot application. + ``` import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -31,14 +34,18 @@ public class Main { } } ``` + For someone who wants to configure the ServiceBinding CR using system properties, they can do it in the application.properties. The ServiceBinding CR can be customized either via annotation parameters or via system properties. The parameter values provided via annotations can be overrided by configuring the ServiceBinding CR in application.properties. + ``` dekorate.servicebinding.services[0].name=demo-database dekorate.servicebinding.services[0].group=postgresql.dev dekorate.servicebinding.services[0].kind=Database dekorate.servicebinding.services[0].id=postgresDB ``` + Generated ServiceBinding CR would look something like this: + ``` apiVersion: operators.coreos.com/v1beta1 kind: ServiceBinding @@ -59,7 +66,9 @@ spec: detectBindingResources: false bindAsFiles: false ``` + If the application's `bindingPath` needs to configured, `@BindingPath` annotation can be used directly under `@ServicingBinding` annotation. For example: + ``` @ServiceBinding( bindingPath = @BindingPath(containerPath="spec.template.spec.containers") @@ -67,8 +76,8 @@ If the application's `bindingPath` needs to configured, `@BindingPath` annotatio @Service(group = "postgresql.dev", name = "demo-database", kind = "Database", version = "v1alpha1", id = "postgresDB") }, envVarPrefix = "postgresql") @SpringBootApplication ``` -**Note** : `ServiceBinding` annotations are already usuable though still highly experimental. The Service Binding operator is still in flux and may change in the near future. +**Note** : `ServiceBinding` annotations are already usuable though still highly experimental. The Service Binding operator is still in flux and may change in the near future. #### External generator integration @@ -88,15 +97,14 @@ An example could be to expose an additional port: This can be done by configuring dekorate to read the fmp generated manifests from `META-INF/fabric8` which is where fmp stores them and save them back there once decoration is finished. + ```java @GeneratorOptions(inputPath = "META-INF/fabric8", outputPath = "META-INF/fabric8") -@KubernetesApplication(port = @Port(name="srv", containerPort=8181) +@KubernetesApplication(port = @Port(name="srv", containerPort=8181)) public class Main { - ... + // ... } ``` -#### related examples -- [spring boot with fmp on openshift example](https://github.com/dekorateio/dekorate/tree/main/examples/spring-boot-with-fmp-on-kubernetes-example) - - +#### related examples +- [spring boot with fmp on kubernetes example](https://github.com/dekorateio/dekorate/tree/main/examples/spring-boot-with-fmp-on-kubernetes-example) diff --git a/docs/documentation/tekton.md b/docs/documentation/tekton.md index e6f92a616..2b9a299b8 100644 --- a/docs/documentation/tekton.md +++ b/docs/documentation/tekton.md @@ -38,7 +38,6 @@ Two sets of resources will be generated, each representing a different configura This set of resources contains: - Pipeline -- PipelineResource (git, output image) - PipelineRun - Task (build, package and push, deploy) - RBAC resources @@ -70,7 +69,7 @@ to make using `tekton` as easy as it gets. ##### Git Resource The generated tasks and pipelines, assume the project is under version control and more specifically git. -So, in order to `run` the pipeline or the `task` a `PiepelineResource` of type `git` is required. +So, in order to `run` the pipeline or the `task` a `PipelineResource` of type `git` is required. If the project is added to git, the resource will be generated for you. If for any reason the use of an external resource is preferred then it needs to be configured, like: @@ -144,4 +143,6 @@ used if explicitly requested: dekorate.tekton.use-local-docker-config-json=true ``` +#### related examples +- [spring boot with Tekton example](https://github.com/dekorateio/dekorate/tree/main/examples/spring-boot-with-tekton-example) diff --git a/examples/spring-boot-with-tekton-example/Dockerfile b/examples/spring-boot-with-tekton-example/Dockerfile index d44f82516..02dd4d075 100644 --- a/examples/spring-boot-with-tekton-example/Dockerfile +++ b/examples/spring-boot-with-tekton-example/Dockerfile @@ -1,4 +1,3 @@ FROM openjdk:8u171-alpine3.7 -RUN apk --no-cache add curl COPY target/*.jar spring-on-kubernetes-example.jar CMD java ${JAVA_OPTS} -jar spring-on-kubernetes-example.jar diff --git a/examples/spring-boot-with-tekton-example/readme.md b/examples/spring-boot-with-tekton-example/readme.md index af4a1fe1c..8abb2ca03 100644 --- a/examples/spring-boot-with-tekton-example/readme.md +++ b/examples/spring-boot-with-tekton-example/readme.md @@ -1,12 +1,9 @@ -# Spring Boot on Kubernetes +# Spring Boot with Tekton on Kubernetes The purpose of this example is to demonstrate the following: -- How you can use the kubernetes-spring-stater. -- How Dekorate detects that this is a web app and automatically configures services and probe. +- How you can use the tekton-annotations and the kubernetes-spring-starter modules. - How you can end-to-end test the application. -- How you can trigger a docker build after the compilation. - The application is using: @@ -15,18 +12,16 @@ The application is using: kubernetes-spring-starter ${project.version} + + + io.dekorate + tekton-annotations + ${project.version} + Which contains all the required modules, including the annotation processors that detect spring web applications. The [Main.class](src/main/java/io/dekorate/example/Main.java) is annotated with `@KubernetesApplication` which triggers the resource generation. -It's also annotated with `@DockerBuild`. This annotation allows the user to trigger a docker build after the compilation, by passing the system property -`dekorate.build=true` to the build for example: - - mvn clean install -Ddekorate.build=true - -Note: Dekorate is not going to generate a Dockerfile for you. It expects to find one in the root of the module and it also expects to find the `docker` binary -pointing to a running docker daemon. - The spring web application processor will detect our [Controller.java](src/main/java/io/dekorate/example/Controller.java), and will: @@ -34,22 +29,63 @@ The spring web application processor will detect our [Controller.java](src/main/ - expose port 8080 as a service - add readiness and liveness probes. -## Integration testing +## End-to-End testing -For the purpose of integration testing it includes: +0. Set Up environment - - io.dekorate - kubernetes-junit-starter - ${project.version} - test - +This step will set up a Kubernetes cluster with Tekton installed. -This annotation will bring in the junit5 extension that dekorate provides, that allows you to run integration tests via the '@KubernetesIntegrationTest' annotation. +**Note:** If you have already logged in a Kubernetes environment, you can skip this step. -The tests annotated with `@KubernetesIntegrationTest` are going to be automatically run when building the application. For example: +To use Tekton, it is needed to have a k8s cluster (>= 1.24) & local docker registry & the kind CLI installed (>= 0.17): +```bash +curl -s -L "https://raw.githubusercontent.com/snowdrop/k8s-infra/main/kind/kind.sh" | bash -s install --registry-name kind-registry.local +``` - mvn clean install - -Note: To run the integration tests an actual kubernetes environment is required. -As the integration test requires a docker build to run, the tests will only run if an existing `Dockerfile` is found, and if the docker daemon used is the one use by kubernetes. +Next, let's install the release 0.39.0 of Tekton (or a specific release): +```bash +kubectl apply -f https://github.com/tektoncd/pipeline/releases/download/v0.39.0/release.yaml +``` + +1. Build the manifests: +```bash +mvn clean install -DskipTests -Ddekorate.docker.registry=quay.io -Ddekorate.tekton.use-local-docker-config-json=true -Ddekorate.tekton.projectBuilderArguments=clean,install,-Pwith-examples,-DskipTests,-Dformat.skip=true,-pl,examples/spring-boot-with-tekton-example,-am +``` + +Because we're going to use our private container registry `quay.io`, we need to configure Tekton to use our local docker config file, so Kubernetes can log into quay.io. + +Moreover, by default, the project will be built by Tekton using the command `mvn clean install`, however since this example is located at the Dekorate github repository, we need to turn on the Maven profile `with-examples` to also build the examples, and to skip the tests, so the build does not take much time to finish. The final property to configure the project builder is `-Ddekorate.tekton.projectBuilderArguments=clean,install,-Pwith-examples,-DskipTests,-Dformat.skip=true,-pl examples/spring-boot-with-tekton-example,-am`. + +2. Run Tekton to build and start the application + +There are two ways to do this: via pipelines and via tasks. We'll proceed with installing the app via tasks, which it's simpler procedure because Pipelines +require to configure a `PersistentVolumeClaim` volume in order to share the workspace between steps. + +We need first to install the Tekton tasks manifests: + +```bash +kubectl apply -f target/classes/META-INF/dekorate/tekton-task.yml +``` + +And next, run the task workflow: +```bash +kubectl apply -f target/classes/META-INF/dekorate/tekton-task-run.yml +``` + +This workflow will download the sources, build the project and the image into your quay.io account and start the application. + +Let's wait until the pipeline is finished by doing: + +```bash +kubectl wait --for=condition=Succeeded --timeout=800s TaskRun/spring-boot-with-tekton-example-run-now +``` + +**TIP:** You can see the logs of the workflow using `kubectl logs -f spring-boot-with-tekton-example-run-now-pod --all-containers --max-log-requests 10`. + +After the Tekton task is finished, we can see our example is up and running using `kubectl get pods` and call our Hello World endpoint: + +```bash +kubectl port-forward svc/spring-boot-with-tekton-example 8000:80 +wget -qO- http://localhost:8000 +> Hello world +```