├── .classpath ├── .github ├── project.yml └── workflows │ ├── build.yml │ ├── integration-tests.yml │ ├── multiplatform-registry-test.yml │ ├── multiplatform-test.yml │ └── release.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .project ├── .settings ├── org.eclipse.jdt.apt.core.prefs ├── org.eclipse.jdt.core.prefs ├── org.eclipse.jdt.ui.prefs └── org.eclipse.m2e.core.prefs ├── LICENSE ├── README.md ├── client ├── pom.xml └── src │ ├── main │ └── java │ │ └── dev │ │ └── snowdrop │ │ └── buildpack │ │ ├── BuildConfig.java │ │ ├── BuilderImage.java │ │ ├── BuildpackBuild.java │ │ ├── BuildpackException.java │ │ ├── ContainerLogReader.java │ │ ├── Logger.java │ │ ├── Slf4jLogger.java │ │ ├── SystemLogger.java │ │ ├── TestDriver.java │ │ ├── config │ │ ├── CacheConfig.java │ │ ├── DockerConfig.java │ │ ├── HostAndSocketConfig.java │ │ ├── ImageReference.java │ │ ├── LogConfig.java │ │ ├── PlatformConfig.java │ │ └── RegistryAuthConfig.java │ │ ├── docker │ │ ├── AuthDelegatingDockerClientConfig.java │ │ ├── BuildContainerUtils.java │ │ ├── ContainerEntry.java │ │ ├── ContainerUtils.java │ │ ├── Content.java │ │ ├── DockerClientUtils.java │ │ ├── FileContent.java │ │ ├── ImageUtils.java │ │ ├── StreamContent.java │ │ ├── StringContent.java │ │ ├── VolumeBind.java │ │ └── VolumeUtils.java │ │ ├── lifecycle │ │ ├── ContainerStatus.java │ │ ├── LifecycleExecutor.java │ │ ├── LifecyclePhase.java │ │ ├── LifecyclePhaseAnalyzedTomlUpdater.java │ │ ├── LifecyclePhaseFactory.java │ │ ├── Version.java │ │ └── phases │ │ │ ├── Analyzer.java │ │ │ ├── Builder.java │ │ │ ├── Creator.java │ │ │ ├── Detector.java │ │ │ ├── Exporter.java │ │ │ ├── Extender.java │ │ │ └── Restorer.java │ │ └── utils │ │ ├── FilePermissions.java │ │ ├── JsonUtils.java │ │ ├── LifecycleArgs.java │ │ ├── LifecycleMetadata.java │ │ └── OperatingSytem.java │ └── test │ └── java │ └── dev │ └── snowdrop │ └── buildpack │ ├── config │ ├── CacheConfigTest.java │ ├── DockerConfigTest.java │ ├── ImageReferenceTest.java │ ├── LogConfigTest.java │ └── PlatformConfigTest.java │ ├── docker │ ├── ContainerEntryTest.java │ ├── ContainerUtilsTest.java │ ├── ContentTest.java │ ├── DockerClientUtilsTest.java │ ├── ImageUtilsTest.java │ └── VolumeUtilsTest.java │ ├── lifecycle │ ├── ContainerStatusTest.java │ ├── LifecycleExecutorTest.java │ └── phases │ │ ├── AnalzyerTest.java │ │ ├── BuilderTest.java │ │ ├── CreatorTest.java │ │ ├── DetectorTest.java │ │ ├── ExporterTest.java │ │ ├── ExtenderTest.java │ │ └── RestorerTest.java │ └── utils │ ├── JsonUtilsTest.java │ └── LifecycleArgsTest.java ├── mvnw ├── mvnw.cmd ├── pom.xml └── samples ├── build-me ├── .gitignore ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── dev │ └── snowdrop │ └── BuildMe.java ├── hello-quarkus ├── .dockerignore ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── .gitignore │ │ ├── MavenWrapperDownloader.java │ │ └── maven-wrapper.properties ├── README.md ├── id-debug.sh ├── mvnw ├── mvnw.cmd ├── pack.java ├── pom.xml └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ └── Dockerfile.native-micro │ ├── java │ │ └── dev │ │ │ └── snowdrop │ │ │ └── GreetingResource.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── index.html │ │ └── application.properties │ └── test │ └── java │ └── dev │ └── snowdrop │ ├── GreetingResourceIT.java │ └── GreetingResourceTest.java ├── hello-spring ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pack.java ├── pom.xml └── src │ └── main │ ├── java │ └── dev │ │ └── snowdrop │ │ └── hellospring │ │ └── DemoApplication.java │ └── resources │ └── application.properties └── testcases ├── README.md ├── RunRegistryTest.java ├── RunTest.java ├── hello-quarkus ├── .dockerignore ├── .gitignore ├── .mvn │ └── wrapper │ │ ├── .gitignore │ │ ├── MavenWrapperDownloader.java │ │ └── maven-wrapper.properties ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ └── Dockerfile.native-micro │ ├── java │ │ └── dev │ │ │ └── snowdrop │ │ │ └── GreetingResource.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── index.html │ │ └── application.properties │ └── test │ └── java │ └── dev │ └── snowdrop │ ├── GreetingResourceIT.java │ └── GreetingResourceTest.java └── hello-spring ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── dev │ └── snowdrop │ └── hellospring │ └── DemoApplication.java └── resources └── application.properties /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/project.yml: -------------------------------------------------------------------------------- 1 | release: 2 | current-version: 0.0.14 3 | next-version: 0.0.15-SNAPSHOT 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The original authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: Build 16 | 17 | env: 18 | MAVEN_ARGS: -B -e 19 | 20 | on: 21 | push: 22 | branches: 23 | - main 24 | pull_request: 25 | 26 | jobs: 27 | build: 28 | name: Java ${{ matrix.java }} Maven 29 | runs-on: ubuntu-latest 30 | strategy: 31 | matrix: 32 | java: [8, 11, 17] 33 | 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v4 37 | 38 | - name: Setup Java 39 | uses: actions/setup-java@v4 40 | with: 41 | java-version: ${{ matrix.java }} 42 | distribution: 'adopt' 43 | cache: maven 44 | 45 | - name: Build Project 46 | run: mvn ${MAVEN_ARGS} clean install 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/integration-tests.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The original authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | name: Integration Tests 16 | # We need to set these defaults so that .bashrc is called for each step. 17 | # This is needed so that sdkman can be properly intialized 18 | defaults: 19 | run: 20 | shell: bash -ieo pipefail {0} 21 | 22 | env: 23 | MAVEN_ARGS: -B -e 24 | 25 | on: 26 | push: 27 | branches: 28 | - main 29 | pull_request: 30 | workflow_dispatch: 31 | 32 | jobs: 33 | integration-tests: 34 | name: Package Samples with Java ${{ matrix.java }} and JBang ${{ matrix.jbang }} 35 | runs-on: ubuntu-latest 36 | strategy: 37 | matrix: 38 | java: [8, 11, 17] 39 | jbang: [0.125.1] 40 | steps: 41 | - name: Freee Disk Space 42 | uses: jlumbroso/free-disk-space@main 43 | with: 44 | tool-cache: false 45 | android: true 46 | dotnet: true 47 | haskell: true 48 | large-packages: true 49 | docker-images: true 50 | swap-storage: false 51 | 52 | - name: Docker Ubuntu 53 | run: | 54 | echo "Configuring Docker for ubuntu" 55 | 56 | # Remove the podman, to allow library autodetect to find docker 57 | sudo apt-get remove podman 58 | 59 | - name: Checkout 60 | uses: actions/checkout@v4 61 | 62 | - name: Setup Java 63 | uses: actions/setup-java@v4 64 | with: 65 | java-version: ${{ matrix.java }} 66 | distribution: 'adopt' 67 | cache: maven 68 | 69 | - name: Setup sdkman 70 | run: | 71 | curl -s "https://get.sdkman.io" | bash 72 | source "$HOME/.sdkman/bin/sdkman-init.sh" 73 | sdkman_auto_answer=false 74 | sdkman_selfupdate_enable=false 75 | 76 | - name: Setup jbang 77 | run: | 78 | sdk install jbang ${{matrix.jbang}} 79 | sdk default jbang ${{matrix.jbang}} 80 | 81 | - name: Integration Tests 82 | run: | 83 | if [ ${{ github.event_name }} = 'pull_request' ]; then 84 | REPO="${{ github.event.pull_request.head.repo.full_name }}" 85 | REPO_NAME=`echo $REPO | cut -d'/' -f2` 86 | REPO_OWNER=`echo $REPO | cut -d'/' -f1` 87 | SHA="${{ github.event.pull_request.head.sha }}" 88 | else 89 | REPO="$GITHUB_REPOSITORY" 90 | REPO_NAME=`echo $REPO | cut -d'/' -f2` 91 | REPO_OWNER=`echo $REPO | cut -d'/' -f1` 92 | SHA=$GITHUB_SHA 93 | fi 94 | export CURRENT_WORKFLOW_DEP=com.github.$REPO_OWNER.$REPO_NAME:buildpack-client:$SHA 95 | echo Tests using dependency $CURRENT_WORKFLOW_DEP 96 | cd samples/hello-spring 97 | ./pack.java 98 | cd ../../samples/hello-quarkus 99 | ./pack.java 100 | 101 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Java Buildpack Client Release 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | paths: 7 | - '.github/project.yml' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | defaults: 14 | run: 15 | shell: bash 16 | 17 | jobs: 18 | release: 19 | runs-on: ubuntu-latest 20 | name: release 21 | if: ${{github.event.pull_request.merged == true}} 22 | 23 | steps: 24 | - uses: radcortez/project-metadata-action@main 25 | name: Retrieve project metadata 26 | id: metadata 27 | with: 28 | github-token: ${{secrets.GITHUB_TOKEN}} 29 | metadata-file-path: '.github/project.yml' 30 | 31 | - uses: actions/checkout@v4 32 | 33 | - name: Set up JDK 17 34 | uses: actions/setup-java@v4 35 | with: 36 | distribution: temurin 37 | java-version: 17 38 | cache: 'maven' 39 | server-id: oss-sonatype-staging 40 | server-username: OSS_SONATYPE_USERNAME 41 | server-password: OSS_SONATYPE_PASSWORD 42 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 43 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 44 | 45 | - name: Configure Git author 46 | run: | 47 | git config --local user.email "action@github.com" 48 | git config --local user.name "GitHub Action" 49 | - name: Set release version in the samples and readme 50 | run: | 51 | find ./samples/ -iwholename "*/pack.java" | while read f; do 52 | sed -i "s/dev.snowdrop:buildpack-client:.*/dev.snowdrop:buildpack-client:${{steps.metadata.outputs.current-version}}\}/g" $f 53 | git add $f 54 | done 55 | sed -i "s/dev.snowdrop:buildpack-client:.*/dev.snowdrop:buildpack-client:${{steps.metadata.outputs.current-version}}\}/g" README.md 56 | sed -i "s/.*<\/version>/${{steps.metadata.outputs.current-version}}<\/version>/g" README.md 57 | git add README.md 58 | git commit -m "chore: set release version: ${{steps.metadata.outputs.current-version}} in samples and README.md" 59 | - name: Maven release ${{steps.metadata.outputs.current-version}} 60 | run: | 61 | mvn -B release:prepare -Prelease -Darguments="-DskipTests" -DreleaseVersion=${{steps.metadata.outputs.current-version}} -DdevelopmentVersion=${{steps.metadata.outputs.next-version}} 62 | mvn -B release:perform -Darguments="-DperformRelease -DskipTests" -DperformRelease -Prelease 63 | env: 64 | OSS_SONATYPE_USERNAME: ${{ secrets.OSS_SONATYPE_USERNAME }} 65 | OSS_SONATYPE_PASSWORD: ${{ secrets.OSS_SONATYPE_PASSWORD }} 66 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 67 | - name: Set dev version in the samples and readme 68 | run: | 69 | find ./samples/ -iwholename "*/pack.java" | while read f; do 70 | sed -i "s/dev.snowdrop:buildpack-client:.*/dev.snowdrop:buildpack-client:${{steps.metadata.outputs.next-version}}\}/g" $f 71 | git add $f 72 | done 73 | git commit -m "chore: set dev version: ${{steps.metadata.outputs.next-version}} in samples and README.md" 74 | - name: Push changes to ${{github.base_ref}} branch 75 | run: | 76 | git push 77 | git push origin ${{steps.metadata.outputs.current-version}} 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### MACOS ### 36 | .DS_Store 37 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowdrop/java-buildpack-client/38fc7150e74795017d2333a915602e7a2f4b19b2/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | buildpack-client 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | 25 | 1666703120370 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.apt.aptEnabled=true 3 | org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations 4 | org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.ui.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.ui.text.custom_code_templates= 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | dev.snowdrop 6 | buildpack-client-project 7 | 0.0.15-SNAPSHOT 8 | .. 9 | 10 | 11 | dev.snowdrop 12 | buildpack-client 13 | jar 14 | Snowdrop :: Java Buildpack Client :: Client 15 | 16 | 17 | 18 | com.github.docker-java 19 | docker-java-api 20 | 21 | 22 | com.github.docker-java 23 | docker-java-core 24 | 25 | 26 | com.github.docker-java 27 | docker-java-transport-httpclient5 28 | 29 | 30 | org.tomlj 31 | tomlj 32 | 33 | 34 | org.apache.commons 35 | commons-compress 36 | 37 | 38 | io.sundr 39 | builder-annotations 40 | compile 41 | true 42 | 43 | 44 | org.projectlombok 45 | lombok 46 | provided 47 | 1.18.30 48 | true 49 | 50 | 51 | com.fasterxml.jackson.core 52 | jackson-annotations 53 | 54 | 55 | org.junit.jupiter 56 | junit-jupiter-engine 57 | test 58 | 59 | 60 | org.mockito 61 | mockito-inline 62 | test 63 | 64 | 65 | net.bytebuddy 66 | byte-buddy 67 | 1.15.4 68 | test 69 | 70 | 71 | org.mockito 72 | mockito-junit-jupiter 73 | test 74 | 75 | 76 | org.slf4j 77 | slf4j-simple 78 | ${version.slf4j} 79 | test 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/BuildConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dev.snowdrop.buildpack.config.CacheConfig; 7 | import dev.snowdrop.buildpack.config.DockerConfig; 8 | import dev.snowdrop.buildpack.config.ImageReference; 9 | import dev.snowdrop.buildpack.config.LogConfig; 10 | import dev.snowdrop.buildpack.config.PlatformConfig; 11 | import dev.snowdrop.buildpack.docker.Content; 12 | import io.sundr.builder.annotations.Buildable; 13 | 14 | 15 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 16 | public class BuildConfig { 17 | public static BuildConfigBuilder builder() { 18 | return new BuildConfigBuilder(); 19 | } 20 | 21 | private static final ImageReference DEFAULT_BUILDER_IMAGE = new ImageReference("paketobuildpacks/builder:base"); 22 | 23 | private DockerConfig dockerConfig; 24 | private CacheConfig buildCacheConfig; 25 | private CacheConfig launchCacheConfig; 26 | private CacheConfig kanikoCacheConfig; 27 | private PlatformConfig platformConfig; 28 | private LogConfig logConfig; 29 | private ImageReference builderImage; 30 | private ImageReference runImage; 31 | private ImageReference outputImage; 32 | private List application; 33 | 34 | private final int exitCode; 35 | 36 | public BuildConfig(DockerConfig dockerConfig, 37 | CacheConfig buildCacheConfig, 38 | CacheConfig launchCacheConfig, 39 | CacheConfig kanikoCacheConfig, 40 | PlatformConfig platformConfig, 41 | LogConfig logConfig, 42 | ImageReference builderImage, 43 | ImageReference runImage, 44 | ImageReference outputImage, 45 | List application){ 46 | this.dockerConfig = dockerConfig != null ? dockerConfig : DockerConfig.builder().build(); 47 | this.buildCacheConfig = buildCacheConfig != null ? buildCacheConfig : CacheConfig.builder().build(); 48 | this.launchCacheConfig = launchCacheConfig != null ? launchCacheConfig : CacheConfig.builder().build(); 49 | this.kanikoCacheConfig = kanikoCacheConfig != null ? kanikoCacheConfig : CacheConfig.builder().build(); 50 | this.platformConfig = platformConfig != null ? platformConfig : PlatformConfig.builder().build(); 51 | this.logConfig = logConfig != null ? logConfig : LogConfig.builder().build(); 52 | this.builderImage = builderImage != null ? builderImage : DEFAULT_BUILDER_IMAGE; 53 | this.runImage = runImage; 54 | this.outputImage = outputImage; 55 | this.application = application != null ? application : new ArrayList<>(); 56 | 57 | if(this.outputImage==null){ 58 | throw new BuildpackException("Output Image missing and must be specified", new IllegalArgumentException()); 59 | } 60 | if(this.application.size()==0){ 61 | throw new BuildpackException("Application content missing and must be specified", new IllegalArgumentException()); 62 | } 63 | 64 | exitCode = new BuildpackBuild(this).build(); 65 | } 66 | 67 | public DockerConfig getDockerConfig(){ 68 | return dockerConfig; 69 | } 70 | public CacheConfig getBuildCacheConfig(){ 71 | return buildCacheConfig; 72 | } 73 | public CacheConfig getLaunchCacheConfig(){ 74 | return launchCacheConfig; 75 | } 76 | public CacheConfig getKanikoCacheConfig(){ 77 | return kanikoCacheConfig; 78 | } 79 | public PlatformConfig getPlatformConfig(){ 80 | return platformConfig; 81 | } 82 | public LogConfig getLogConfig(){ 83 | return logConfig; 84 | } 85 | public ImageReference getBuilderImage(){ 86 | return builderImage; 87 | } 88 | public ImageReference getRunImage(){ 89 | return runImage; 90 | } 91 | public ImageReference getOutputImage(){ 92 | return outputImage; 93 | } 94 | public List getApplication(){ 95 | return application; 96 | } 97 | public int getExitCode() { 98 | return this.exitCode; 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/BuildpackException.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | public class BuildpackException extends RuntimeException { 4 | 5 | public BuildpackException() { 6 | } 7 | 8 | public BuildpackException(Throwable cause) { 9 | super(cause); 10 | } 11 | 12 | public BuildpackException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | 16 | public static RuntimeException launderThrowable(Throwable cause) { 17 | return launderThrowable(cause.getMessage(), cause); 18 | } 19 | 20 | public static RuntimeException launderThrowable(String message, Throwable cause) { 21 | if (cause instanceof RuntimeException) { 22 | return ((RuntimeException) cause); 23 | } else if (cause instanceof Error) { 24 | throw ((Error) cause); 25 | } else if (cause instanceof InterruptedException) { 26 | Thread.currentThread().interrupt(); 27 | } 28 | return new BuildpackException(message, cause); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/ContainerLogReader.java: -------------------------------------------------------------------------------- 1 | 2 | package dev.snowdrop.buildpack; 3 | 4 | import static java.nio.charset.StandardCharsets.UTF_8; 5 | 6 | import com.github.dockerjava.api.async.ResultCallback; 7 | import com.github.dockerjava.api.model.Frame; 8 | import com.github.dockerjava.api.model.StreamType; 9 | 10 | public class ContainerLogReader extends ResultCallback.Adapter { 11 | 12 | private final Logger logger; 13 | 14 | public ContainerLogReader(Logger logger) { 15 | this.logger = logger; 16 | } 17 | 18 | @Override 19 | public void onNext(Frame object) { 20 | if (StreamType.STDOUT == object.getStreamType() || StreamType.STDERR == object.getStreamType()) { 21 | String payload = new String(object.getPayload(), UTF_8); 22 | if (StreamType.STDOUT == object.getStreamType()) { 23 | logger.stdout(payload); 24 | } else if (StreamType.STDERR == object.getStreamType()) { 25 | logger.stderr(payload); 26 | } 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/Logger.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | public interface Logger { 4 | 5 | void stdout(String message); 6 | void stderr(String message); 7 | 8 | default boolean isAnsiColorEnabled() { 9 | return false; 10 | } 11 | 12 | default String prepare(String message) { 13 | String result = new String(message); 14 | if (result.endsWith("\n")) { 15 | result = result.substring(0, result.length() - 1); 16 | } 17 | 18 | if (!isAnsiColorEnabled()) { 19 | result=result.replaceAll("[^m]+m", ""); 20 | } 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/Slf4jLogger.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import io.sundr.builder.annotations.Buildable; 7 | 8 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 9 | public class Slf4jLogger implements dev.snowdrop.buildpack.Logger { 10 | 11 | private final Logger log; 12 | private final String name; 13 | 14 | public Slf4jLogger(String name) { 15 | this.name = name; 16 | this.log = LoggerFactory.getLogger(name); 17 | } 18 | 19 | public Slf4jLogger(Class c) { 20 | this.name = c.getCanonicalName(); 21 | this.log = LoggerFactory.getLogger(c); 22 | } 23 | 24 | @Override 25 | public void stdout(String message) { 26 | log.info(prepare(message)); 27 | } 28 | 29 | @Override 30 | public void stderr(String message) { 31 | log.error(prepare(message)); 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/SystemLogger.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | import io.sundr.builder.annotations.Buildable; 4 | 5 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 6 | public class SystemLogger implements Logger { 7 | 8 | private final boolean ansiColorEnabled; 9 | 10 | public SystemLogger() { 11 | this(true); 12 | } 13 | 14 | public SystemLogger(boolean ansiColorEnabled) { 15 | this.ansiColorEnabled = ansiColorEnabled; 16 | } 17 | 18 | @Override 19 | public void stdout(String message) { 20 | System.out.println(prepare(message)); 21 | } 22 | 23 | @Override 24 | public void stderr(String message) { 25 | System.err.println(prepare(message)); 26 | } 27 | 28 | public boolean isAnsiColorEnabled() { 29 | return this.ansiColorEnabled; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/TestDriver.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack; 2 | 3 | import java.io.File; 4 | import java.util.HashMap; 5 | 6 | import dev.snowdrop.buildpack.config.ImageReference; 7 | 8 | public class TestDriver { 9 | 10 | public static void main(String[] args) throws Exception { 11 | 12 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 13 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 14 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 15 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 16 | 17 | HashMap env = new HashMap<>(); 18 | 19 | 20 | int exitCode = 21 | BuildConfig.builder().withNewDockerConfig() 22 | .withUseDaemon(false) 23 | .withDockerNetwork("host") 24 | .and() 25 | .withNewLogConfig() 26 | .withLogger(new SystemLogger()) 27 | .withLogLevel("debug") 28 | .and() 29 | .withNewKanikoCacheConfig() 30 | //.withCacheVolumeName("mykanikocache") 31 | .withDeleteCacheAfterBuild(true) 32 | .and() 33 | .withNewBuildCacheConfig() 34 | //.withCacheVolumeName("buildcachevol") 35 | .withDeleteCacheAfterBuild(true) 36 | .and() 37 | .withNewLaunchCacheConfig() 38 | //.withCacheVolumeName("launchcachevol") 39 | .withDeleteCacheAfterBuild(true) 40 | .and() 41 | .withNewPlatformConfig() 42 | .withEnvironment(env) 43 | .withTrustBuilder(false) 44 | .and() 45 | .withBuilderImage(new ImageReference("paketocommunity/builder-ubi-base")) 46 | .withOutputImage(new ImageReference("localhost:5000/testdriver/newimage6:latest")) 47 | .addNewFileContentApplication(new File("/tmp/sample-springboot-java-app/")) 48 | .build() 49 | .getExitCode(); 50 | 51 | System.out.println("Build for completed with exit code "+exitCode); 52 | 53 | } 54 | } -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/CacheConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import io.sundr.builder.annotations.Buildable; 4 | 5 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 6 | public class CacheConfig { 7 | 8 | private static Boolean DEFAULT_DELETE_CACHE = Boolean.TRUE; 9 | 10 | public static CacheConfigBuilder builder() { 11 | return new CacheConfigBuilder(); 12 | } 13 | 14 | private String cacheVolumeName; 15 | private Boolean deleteCacheAfterBuild; 16 | 17 | public CacheConfig(String cacheVolumeName, 18 | Boolean deleteCacheAfterBuild){ 19 | this.cacheVolumeName = cacheVolumeName; 20 | this.deleteCacheAfterBuild = deleteCacheAfterBuild!=null ? deleteCacheAfterBuild : DEFAULT_DELETE_CACHE; //default if not set 21 | } 22 | 23 | public String getCacheVolumeName(){ 24 | return this.cacheVolumeName; 25 | } 26 | 27 | public Boolean getDeleteCacheAfterBuild(){ 28 | return this.deleteCacheAfterBuild; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/DockerConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import com.github.dockerjava.api.DockerClient; 10 | 11 | import dev.snowdrop.buildpack.BuildpackException; 12 | import dev.snowdrop.buildpack.docker.DockerClientUtils; 13 | import io.sundr.builder.annotations.Buildable; 14 | 15 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 16 | public class DockerConfig { 17 | private static final Logger log = LoggerFactory.getLogger(DockerConfig.class); 18 | 19 | public static DockerConfigBuilder builder() { 20 | return new DockerConfigBuilder(); 21 | } 22 | 23 | public static enum PullPolicy {ALWAYS, IF_NOT_PRESENT, NEVER}; 24 | 25 | private static final Integer DEFAULT_PULL_TIMEOUT = 60; 26 | private static final Integer DEFAULT_PULL_RETRY_INCREASE = 15; 27 | private static final Integer DEFAULT_PULL_RETRY_COUNT = 3; 28 | private static final PullPolicy DEFAULT_PULL_POLICY = PullPolicy.IF_NOT_PRESENT; 29 | 30 | private Integer pullTimeoutSeconds; 31 | private Integer pullRetryCount; 32 | private Integer pullRetryIncreaseSeconds; 33 | private PullPolicy pullPolicy; 34 | private HostAndSocketConfig hostAndSocketConfig; 35 | private String dockerNetwork; 36 | private Boolean useDaemon; 37 | private DockerClient dockerClient; 38 | private List authConfigs; 39 | 40 | public DockerConfig( 41 | Integer pullTimeoutSeconds, 42 | Integer pullRetryCount, 43 | Integer pullRetryIncreaseSeconds, 44 | PullPolicy pullPolicy, 45 | HostAndSocketConfig hostAndSocketConfig, 46 | String dockerNetwork, 47 | Boolean useDaemon, 48 | DockerClient dockerClient, 49 | List authConfigs 50 | ){ 51 | log.debug("DockerConfig: pullTimeoutSeconds={}, pullRetryCount={}, pullRetryIncreaseSeconds={}, pullPolicy={}, hostAndSocketConfig={}, dockerNetwork={}, useDaemon={}", 52 | pullTimeoutSeconds, pullRetryCount, pullRetryIncreaseSeconds, pullPolicy, hostAndSocketConfig, dockerNetwork, useDaemon); 53 | 54 | this.pullTimeoutSeconds = pullTimeoutSeconds != null ? Integer.max(0,pullTimeoutSeconds) : DEFAULT_PULL_TIMEOUT; 55 | this.pullRetryCount = pullRetryCount != null ? Integer.max(0,pullRetryCount) : DEFAULT_PULL_RETRY_COUNT; 56 | this.pullRetryIncreaseSeconds = pullRetryIncreaseSeconds != null ? Integer.max(0,pullRetryIncreaseSeconds) : DEFAULT_PULL_RETRY_INCREASE; 57 | this.pullPolicy = pullPolicy != null ? pullPolicy : DEFAULT_PULL_POLICY; 58 | this.dockerNetwork = dockerNetwork; 59 | this.useDaemon = useDaemon != null ? useDaemon : Boolean.TRUE; //default daemon to true for back compat. 60 | 61 | //process host & socket passed, and probe runtime to fill in unset values. 62 | setHostAndSocketConfig(hostAndSocketConfig); 63 | 64 | this.authConfigs = authConfigs == null ? new ArrayList<>() : authConfigs; 65 | this.dockerClient = dockerClient; 66 | 67 | } 68 | 69 | public void setHostAndSocketConfig(HostAndSocketConfig hostAndSocketConfig) { 70 | this.hostAndSocketConfig = DockerClientUtils.probeContainerRuntime(hostAndSocketConfig); 71 | } 72 | 73 | public Integer getPullTimeoutSeconds(){ 74 | return this.pullTimeoutSeconds; 75 | } 76 | 77 | public Integer getPullRetryCount(){ 78 | return this.pullRetryCount; 79 | } 80 | 81 | public Integer getPullRetryIncreaseSeconds(){ 82 | return this.pullRetryIncreaseSeconds; 83 | } 84 | 85 | public PullPolicy getPullPolicy(){ 86 | return this.pullPolicy; 87 | } 88 | 89 | public HostAndSocketConfig getHostAndSocketConfig(){ 90 | return this.hostAndSocketConfig; 91 | } 92 | 93 | public String getDockerNetwork(){ 94 | return this.dockerNetwork; 95 | } 96 | 97 | public DockerClient getDockerClient(){ 98 | this.dockerClient = this.dockerClient != null ? this.dockerClient : DockerClientUtils.getDockerClient(this.hostAndSocketConfig, this.authConfigs); 99 | return this.dockerClient; 100 | } 101 | 102 | public Boolean getUseDaemon(){ 103 | return this.useDaemon; 104 | } 105 | 106 | public List getAuthConfigs(){ 107 | return this.authConfigs; 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/HostAndSocketConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.util.Optional; 7 | 8 | import dev.snowdrop.buildpack.docker.DockerClientUtils; 9 | import io.sundr.builder.annotations.Buildable; 10 | 11 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 12 | public class HostAndSocketConfig { 13 | private String host; 14 | private String socket; 15 | 16 | public static HostAndSocketConfigBuilder builder() { 17 | return new HostAndSocketConfigBuilder(); 18 | } 19 | 20 | public HostAndSocketConfig(String host, 21 | String socket) { 22 | this.host = host; 23 | this.socket = socket; 24 | } 25 | 26 | public Optional getHost() { 27 | return Optional.ofNullable(host); 28 | } 29 | public Optional getSocket() { 30 | return Optional.ofNullable(socket); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/LogConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import dev.snowdrop.buildpack.Logger; 4 | import dev.snowdrop.buildpack.SystemLogger; 5 | import io.sundr.builder.annotations.Buildable; 6 | 7 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 8 | public class LogConfig { 9 | 10 | private String DEFAULT_LOG_LEVEL="info"; 11 | private Boolean DEFAULT_USE_TIMESTAMPS=Boolean.TRUE; 12 | 13 | public static LogConfigBuilder builder() { 14 | return new LogConfigBuilder(); 15 | } 16 | 17 | private String logLevel; 18 | private Boolean useTimestamps; 19 | private Logger logger; 20 | 21 | 22 | public LogConfig( 23 | String logLevel, 24 | Boolean useTimestamps, 25 | Logger logger 26 | ){ 27 | this.logLevel = logLevel!=null ? logLevel : DEFAULT_LOG_LEVEL; 28 | this.useTimestamps = useTimestamps!=null ? useTimestamps : DEFAULT_USE_TIMESTAMPS; 29 | this.logger = logger!=null ? logger : new SystemLogger(); 30 | } 31 | 32 | public String getLogLevel(){ 33 | return logLevel; 34 | } 35 | 36 | public Logger getLogger(){ 37 | return logger; 38 | } 39 | 40 | public Boolean getUseTimestamps(){ 41 | return useTimestamps; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/PlatformConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import io.sundr.builder.annotations.Buildable; 7 | 8 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 9 | public class PlatformConfig { 10 | 11 | public static PlatformConfigBuilder builder() { 12 | return new PlatformConfigBuilder(); 13 | } 14 | 15 | private String DEFAULT_PLATFORM_LEVEL="0.10"; 16 | 17 | private String platformLevel; 18 | private Map environment; 19 | private ImageReference lifecycleImage; 20 | private Boolean trustBuilder; //use creator when possible. 21 | private String phaseDebugScript; 22 | 23 | public PlatformConfig( 24 | String platformLevel, 25 | ImageReference lifecycleImage, 26 | Map environment, 27 | Boolean trustBuilder, 28 | String phaseDebugScript){ 29 | this.platformLevel = platformLevel!=null ? platformLevel : DEFAULT_PLATFORM_LEVEL; 30 | this.environment = environment!=null ? environment : new HashMap<>(); 31 | this.lifecycleImage = lifecycleImage; 32 | this.trustBuilder = trustBuilder; 33 | this.phaseDebugScript = phaseDebugScript; 34 | } 35 | 36 | public String getPlatformLevel(){ 37 | return platformLevel; 38 | } 39 | 40 | public Map getEnvironment(){ 41 | return environment; 42 | } 43 | 44 | public ImageReference getLifecycleImage(){ 45 | return lifecycleImage; 46 | } 47 | 48 | public Boolean getTrustBuilder(){ 49 | return trustBuilder; 50 | } 51 | 52 | public String getPhaseDebugScript(){ 53 | return phaseDebugScript; 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/config/RegistryAuthConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import io.sundr.builder.annotations.Buildable; 4 | 5 | @Buildable(generateBuilderPackage=true, builderPackage="dev.snowdrop.buildpack.builder") 6 | public class RegistryAuthConfig { 7 | public static RegistryAuthConfigBuilder builder() { 8 | return new RegistryAuthConfigBuilder(); 9 | } 10 | 11 | private String registryAddress; 12 | private String registryToken; 13 | private String username; 14 | private String auth; 15 | private String email; 16 | private String identityToken; 17 | private String password; 18 | 19 | public RegistryAuthConfig( 20 | String registryAddress, 21 | String registryToken, 22 | String username, 23 | String auth, 24 | String email, 25 | String identityToken, 26 | String password 27 | ){ 28 | this.registryAddress = registryAddress; 29 | this.registryToken = registryToken; 30 | this.username = username; 31 | this.auth = auth; 32 | this.email = email; 33 | this.identityToken = identityToken; 34 | this.password = password; 35 | } 36 | 37 | public String getRegistryAddress(){ 38 | return registryAddress; 39 | } 40 | 41 | public String getRegistryToken() { 42 | return registryToken; 43 | } 44 | 45 | public String getUsername() { 46 | return username; 47 | } 48 | 49 | public String getAuth() { 50 | return auth; 51 | } 52 | 53 | public String getEmail() { 54 | return email; 55 | } 56 | 57 | public String getIdentityToken() { 58 | return identityToken; 59 | } 60 | 61 | public String getPassword() { 62 | return password; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/AuthDelegatingDockerClientConfig.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import com.github.dockerjava.api.model.AuthConfig; 9 | import com.github.dockerjava.core.DockerClientConfig; 10 | import com.github.dockerjava.core.DockerClientConfigDelegate; 11 | 12 | import dev.snowdrop.buildpack.config.ImageReference; 13 | import dev.snowdrop.buildpack.config.RegistryAuthConfig; 14 | 15 | class AuthDelegatingDockerClientConfig extends DockerClientConfigDelegate { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(AuthDelegatingDockerClientConfig.class); 18 | 19 | private List registryAuthInfo; 20 | 21 | public AuthDelegatingDockerClientConfig(DockerClientConfig delegate) { 22 | super(delegate); 23 | } 24 | 25 | public void setRegistryAuthConfigs(List registryAuthInfo) { 26 | this.registryAuthInfo = registryAuthInfo; 27 | } 28 | 29 | @Override 30 | public AuthConfig effectiveAuthConfig(String imageName) { 31 | log.debug("Resolving authentication configuration for image "+imageName); 32 | AuthConfig fallbackAuthConfig; 33 | try { 34 | fallbackAuthConfig = super.effectiveAuthConfig(imageName); 35 | log.debug("fallback config retrieved"); 36 | } catch (Exception e) { 37 | fallbackAuthConfig = new AuthConfig(); 38 | log.debug("no fallback config available"); 39 | } 40 | 41 | // try and obtain more accurate auth config using our resolution 42 | final ImageReference parsed = new ImageReference(imageName); 43 | String address = parsed.getPort()!=null ? parsed.getHost()+":"+parsed.getPort() : parsed.getHost(); 44 | 45 | log.debug("Checking configuration for auth config for address "+address); 46 | 47 | if(registryAuthInfo!=null) { 48 | for(RegistryAuthConfig rac : registryAuthInfo){ 49 | if(address.equals(rac.getRegistryAddress())){ 50 | log.debug("found match, configuring"); 51 | return new AuthConfig() 52 | .withAuth(rac.getAuth()) 53 | .withEmail(rac.getEmail()) 54 | .withIdentityToken(rac.getIdentityToken()) 55 | .withPassword(rac.getPassword()) 56 | .withRegistryAddress(rac.getRegistryAddress()) 57 | .withRegistrytoken(rac.getRegistryToken()) 58 | .withUsername(rac.getUsername()); 59 | } 60 | } 61 | } 62 | 63 | log.debug("no match, using fallback if available"); 64 | return fallbackAuthConfig; 65 | } 66 | } -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/ContainerEntry.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import java.io.InputStream; 4 | 5 | /** 6 | * Abstraction representing an entry in a container. Allows for entries to be 7 | * added from sources other than File/Directory (eg, String as a test content, 8 | * or programmatic supply via future i/f) 9 | */ 10 | public interface ContainerEntry { 11 | 12 | 13 | public String getPath(); 14 | 15 | public long getSize(); 16 | 17 | public Integer getMode(); 18 | 19 | public DataSupplier getDataSupplier(); 20 | 21 | @FunctionalInterface 22 | public interface DataSupplier { 23 | InputStream getData(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/Content.java: -------------------------------------------------------------------------------- 1 | 2 | package dev.snowdrop.buildpack.docker; 3 | 4 | import java.util.List; 5 | 6 | public interface Content { 7 | List getContainerEntries(); 8 | } 9 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/FileContent.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | import dev.snowdrop.buildpack.BuildpackException; 16 | import dev.snowdrop.buildpack.utils.FilePermissions; 17 | import io.sundr.builder.annotations.Buildable; 18 | 19 | @Buildable(generateBuilderPackage = true, builderPackage = "dev.snowdrop.buildpack.builder") 20 | public class FileContent implements Content { 21 | 22 | private static final String DEFAULT_PREFIX = ""; 23 | private static final String UNIX_FILE_SEPARATOR = "/"; 24 | private static final String NON_UNIX_FILE_SEPARATOR = "\\"; 25 | 26 | private final String prefix; 27 | private final File file; 28 | private final File root; 29 | //delegate calls for permissions thru static, to enable testing. 30 | private static FilePermissions filePermissions = new FilePermissions(); 31 | 32 | public FileContent(File file) { 33 | this(DEFAULT_PREFIX, file); 34 | } 35 | 36 | public FileContent(String prefix, File file) { 37 | this(prefix, file, null); 38 | if(!file.exists()){ 39 | throw new RuntimeException(new FileNotFoundException(file.getAbsolutePath())); 40 | } 41 | } 42 | 43 | private FileContent(String prefix, File file, File root) { 44 | this.prefix = prefix; 45 | this.file = file; 46 | this.root = root; 47 | } 48 | 49 | public String getPrefix() { 50 | return prefix; 51 | } 52 | 53 | public File getFile() { 54 | return file; 55 | } 56 | 57 | /** 58 | * Build a container entry from a File, (can also be directory) to be present in 59 | * the container at prefix/name for a File, or prefix/relativePath for fies in a 60 | * dir. Eg, given /a/one/a /a/two/a, passing prefix of /stiletto with /a results 61 | * in /stiletto/one/a and /stiletto/two/a being created. 62 | */ 63 | public List getContainerEntries() { 64 | if (!file.exists()) { 65 | return Collections.emptyList(); 66 | } 67 | 68 | if (file.isFile() && !file.isDirectory()) { 69 | return Arrays.asList(new ContainerEntry() { 70 | @Override 71 | public long getSize() { 72 | try { 73 | return Files.size(file.toPath()); 74 | } catch (IOException e) { 75 | throw BuildpackException.launderThrowable(e); 76 | } 77 | } 78 | 79 | @Override 80 | public String getPath() { 81 | if (root == null) { 82 | return prefix + "/" + file.getName(); 83 | } 84 | // format MUST be unix, as we're putting them in a unix container 85 | return prefix + root.toPath().relativize(file.toPath()).toString().replace(NON_UNIX_FILE_SEPARATOR, 86 | UNIX_FILE_SEPARATOR); 87 | } 88 | 89 | @Override 90 | public Integer getMode() { 91 | return filePermissions.getPermissions(file); 92 | } 93 | 94 | @Override 95 | public DataSupplier getDataSupplier() { 96 | Path p = file.toPath(); 97 | return new DataSupplier() { 98 | public InputStream getData() { 99 | try { 100 | return new BufferedInputStream(Files.newInputStream(p)); 101 | } catch (IOException e) { 102 | throw BuildpackException.launderThrowable(e); 103 | } 104 | } 105 | }; 106 | } 107 | 108 | }); 109 | } else if (file.isDirectory()) { 110 | try { 111 | return Files.walk(file.toPath()).filter(p -> !p.toFile().isDirectory()) 112 | .flatMap(p -> new FileContent(prefix, p.toFile(), file).getContainerEntries().stream()) 113 | .collect(Collectors.toList()); 114 | } catch (IOException e) { 115 | throw BuildpackException.launderThrowable(e); 116 | } 117 | } 118 | return Collections.emptyList(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/StreamContent.java: -------------------------------------------------------------------------------- 1 | 2 | package dev.snowdrop.buildpack.docker; 3 | 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import dev.snowdrop.buildpack.docker.ContainerEntry.DataSupplier; 8 | import io.sundr.builder.annotations.Buildable; 9 | 10 | @Buildable(generateBuilderPackage = true, builderPackage = "dev.snowdrop.buildpack.builder") 11 | public class StreamContent implements Content { 12 | 13 | private final String path; 14 | private final Long size; 15 | private final Integer mode; 16 | private final DataSupplier dataSupplier; 17 | 18 | public StreamContent(String path, Long size, Integer mode, DataSupplier dataSupplier) { 19 | this.path = path; 20 | this.size = size; 21 | this.mode = mode; 22 | this.dataSupplier = dataSupplier; 23 | } 24 | 25 | public List getContainerEntries() { 26 | return Arrays.asList(new ContainerEntry() { 27 | @Override 28 | public DataSupplier getDataSupplier() { 29 | return dataSupplier; 30 | } 31 | 32 | @Override 33 | public String getPath() { 34 | return path; 35 | } 36 | 37 | @Override 38 | public long getSize() { 39 | return size; 40 | } 41 | 42 | @Override 43 | public Integer getMode() { 44 | return mode; 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/StringContent.java: -------------------------------------------------------------------------------- 1 | 2 | package dev.snowdrop.buildpack.docker; 3 | 4 | import java.io.ByteArrayInputStream; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import io.sundr.builder.annotations.Buildable; 9 | 10 | @Buildable(generateBuilderPackage = true, builderPackage = "dev.snowdrop.buildpack.builder") 11 | public class StringContent implements Content { 12 | 13 | private final String path; 14 | private final Integer mode; 15 | private final String content; 16 | 17 | public StringContent(String path, Integer mode, String content) { 18 | this.path = path; 19 | this.mode = mode; 20 | this.content = content; 21 | } 22 | 23 | public String getPath() { 24 | return path; 25 | } 26 | 27 | public String getContent() { 28 | return this.content; 29 | } 30 | 31 | @Override 32 | public List getContainerEntries() { 33 | return Arrays.asList(new ContainerEntry() { 34 | @Override 35 | public long getSize() { 36 | return content.getBytes().length; 37 | } 38 | 39 | @Override 40 | public String getPath() { 41 | return path; 42 | } 43 | 44 | @Override 45 | public Integer getMode() { 46 | return mode; 47 | } 48 | 49 | @Override 50 | public DataSupplier getDataSupplier() { 51 | return () -> new ByteArrayInputStream(content.getBytes()); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/VolumeBind.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | /** 4 | * Represents a named volume bound to a path in a container. 5 | */ 6 | public class VolumeBind { 7 | String volumeName; 8 | String mountPath; 9 | 10 | public VolumeBind(String volumeName, String mountPath) { 11 | this.volumeName = volumeName; 12 | this.mountPath = mountPath; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/docker/VolumeUtils.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.Arrays; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | import org.slf4j.Logger; 14 | 15 | import org.slf4j.LoggerFactory; 16 | 17 | import com.github.dockerjava.api.DockerClient; 18 | import com.github.dockerjava.api.exception.NotFoundException; 19 | import com.github.dockerjava.api.model.Frame; 20 | 21 | import dev.snowdrop.buildpack.ContainerLogReader; 22 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 23 | 24 | public class VolumeUtils { 25 | 26 | private static final Logger log = LoggerFactory.getLogger(VolumeUtils.class); 27 | 28 | //It is critical that we use a mountpoint that exists and is not owned by root, otherwise the volume can gain 29 | //'sticky' root ownership that cannot be undone. As such, we use the /workspace dir, as we know the ephemeral builder 30 | //will have this present as owned by build uid/gid. 31 | final static String mountPrefix = "/workspace"; 32 | 33 | public static boolean createVolumeIfRequired(DockerClient dc, String volumeName) { 34 | if (!exists(dc, volumeName)) { 35 | return internalCreateVolume(dc, volumeName); 36 | } else { 37 | return true; 38 | } 39 | } 40 | 41 | public static boolean exists(DockerClient dc, String volumeName) { 42 | try { 43 | dc.inspectVolumeCmd(volumeName).exec(); 44 | return true; 45 | } catch (NotFoundException e) { 46 | return false; 47 | } 48 | } 49 | 50 | public static void removeVolume(DockerClient dc, String volumeName) { 51 | dc.removeVolumeCmd(volumeName).exec(); 52 | } 53 | 54 | public static boolean addContentToVolume(DockerClient dc, String volumeName, String useImage, String pathInVolume, File content) { 55 | return internalAddContentToVolume(dc, volumeName, useImage, mountPrefix, 0,0, new FileContent(content).getContainerEntries()); 56 | } 57 | 58 | public static boolean addContentToVolume(DockerClient dc, String volumeName, String useImage, String name, Integer mode, String content) { 59 | return internalAddContentToVolume(dc, volumeName, useImage, mountPrefix, 0,0, new StringContent(name, mode, content).getContainerEntries()); 60 | } 61 | 62 | public static boolean addContentToVolume(DockerClient dc, String volumeName, String useImage, String prefix, int uid, int gid, List entries) { 63 | if(!prefix.isEmpty() && !prefix.startsWith("/")) prefix = "/"+prefix; 64 | return internalAddContentToVolume(dc, volumeName, useImage, mountPrefix+prefix, uid, gid, entries); 65 | } 66 | 67 | private static boolean internalCreateVolume(DockerClient dc, String volumeName) { 68 | dc.createVolumeCmd().withName(volumeName).exec(); 69 | return exists(dc, volumeName); 70 | } 71 | 72 | private static boolean internalAddContentToVolume(DockerClient dc, String volumeName, String useImage, String prefix, int uid, int gid, List entries) { 73 | return internalAddContentToVolume(dc, volumeName, useImage, prefix, uid, gid, entries.toArray(new ContainerEntry[entries.size()])); 74 | } 75 | 76 | @SuppressWarnings("resource") 77 | private static boolean internalAddContentToVolume(DockerClient dc, String volumeName, String useImage, String prefix, int uid, int gid, ContainerEntry... entries) { 78 | 79 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/analyzer", null); 80 | args.addArg("-version"); 81 | 82 | //invoking -version requires env to be set. 83 | Map envMap = new HashMap<>(); 84 | envMap.put("CNB_PLATFORM_API", "0.10"); 85 | 86 | String dummyId = ContainerUtils.createContainer(dc, useImage, args.toList(), uid, envMap, null, null, new VolumeBind(volumeName, mountPrefix)); 87 | try{ 88 | log.debug("Adding content to volume "+volumeName+" under prefix "+prefix+" using image "+useImage+" with volume bound at "+mountPrefix+" temp container id "+dummyId); 89 | 90 | log.trace("Starting container to ensure volume binds take effect correctly."); 91 | dc.startContainerCmd(dummyId).exec(); 92 | 93 | log.trace("- Attaching log relay for volume copy container"); 94 | 95 | //log relays for ignore/debug for this operation. 96 | ContainerLogReader slf4j = new ContainerLogReader(new dev.snowdrop.buildpack.Slf4jLogger(VolumeUtils.class)); 97 | ContainerLogReader ignore = new ContainerLogReader(null){ 98 | public void onNext(Frame object) { 99 | //do-nothing 100 | } 101 | }; 102 | 103 | //if debug is enabled for this class, collect container logs 104 | ContainerLogReader logger = log.isDebugEnabled() ? slf4j : ignore; 105 | 106 | // pull the logs, but ignore them.. we're only executing -version on lifecycle. 107 | dc.logContainerCmd(dummyId) 108 | .withFollowStream(true) 109 | .withStdOut(true) 110 | .withStdErr(true) 111 | .withTimestamps(true) 112 | .exec(logger); 113 | 114 | ContainerUtils.addContentToContainer(dc, dummyId, prefix, uid, gid, entries); 115 | 116 | try{ 117 | slf4j.close(); 118 | ignore.close(); 119 | }catch(Exception e){ 120 | log.error("Error closing log relay ",e); 121 | } 122 | 123 | return true; 124 | }finally{ 125 | if(dummyId!=null){ 126 | ContainerUtils.removeContainer(dc, dummyId); 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/ContainerStatus.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle; 2 | 3 | import lombok.Data; 4 | import lombok.ToString; 5 | 6 | @ToString(includeFieldNames=true) 7 | @Data(staticConstructor="of") 8 | public class ContainerStatus { 9 | final int rc; 10 | final String containerId; 11 | } -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/LifecyclePhase.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle; 2 | 3 | import dev.snowdrop.buildpack.Logger; 4 | 5 | public interface LifecyclePhase { 6 | public ContainerStatus runPhase(Logger logger, boolean useTimestamps); 7 | } 8 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/LifecyclePhaseAnalyzedTomlUpdater.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle; 2 | 3 | public interface LifecyclePhaseAnalyzedTomlUpdater { 4 | public byte[] getAnalyzedToml(); 5 | public void updateAnalyzedToml(String toml); 6 | } 7 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/Version.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | import dev.snowdrop.buildpack.BuildpackException; 7 | 8 | public class Version { 9 | String version; 10 | int major; 11 | int minor; 12 | 13 | final Pattern p = Pattern.compile("^(\\d+)\\.(\\d+)(\\..*)?$"); 14 | 15 | public Version(String v){ 16 | version = v; 17 | Matcher m = p.matcher(v); 18 | if(m.find()){ 19 | major = Integer.parseInt(m.group(1)); 20 | minor = Integer.parseInt(m.group(2)); 21 | }else{ 22 | throw new BuildpackException("Invalid format for Version "+v, new IllegalArgumentException()); 23 | } 24 | } 25 | 26 | public String toString() { 27 | return version; 28 | } 29 | 30 | public boolean equals(Version v){ 31 | return this.major == v.major && this.minor == v.minor; 32 | } 33 | public boolean greaterThan(Version v){ 34 | return this.major > v.major || ( this.major == v.major && this.minor > v.minor); 35 | } 36 | public boolean atLeast(Version v){ 37 | return this.major > v.major || ( this.major == v.major && this.minor >= v.minor); 38 | } 39 | public boolean atMost(Version v){ 40 | return this.major < v.major || ( this.major == v.major && this.minor <= v.minor); 41 | } 42 | public boolean lessThan(Version v){ 43 | return this.major < v.major || ( this.major == v.major && this.minor < v.minor); 44 | } 45 | 46 | //convenience methods to keep code a little more readable. 47 | public boolean equals(String ver){ 48 | return this.equals(new Version(ver)); 49 | } 50 | public boolean greaterThan(String ver){ 51 | return this.greaterThan(new Version(ver)); 52 | } 53 | public boolean atLeast(String ver){ 54 | return this.atLeast(new Version(ver)); 55 | } 56 | public boolean atMost(String ver){ 57 | return this.atMost(new Version(ver)); 58 | } 59 | public boolean lessThan(String ver){ 60 | return this.lessThan(new Version(ver)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Analyzer.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 11 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 12 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 13 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 14 | 15 | 16 | public class Analyzer implements LifecyclePhase{ 17 | 18 | private static final Logger log = LoggerFactory.getLogger(Analyzer.class); 19 | 20 | final LifecyclePhaseFactory factory; 21 | 22 | public Analyzer( LifecyclePhaseFactory factory ){ 23 | this.factory = factory; 24 | } 25 | 26 | @Override 27 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 28 | 29 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/analyzer", factory.getOutputImage().getReferenceWithLatest()); 30 | 31 | // 0.7 onwards.. removed 32 | // -cache-dir, Path to cach directory 33 | // -group, Path to group definition (group.toml) 34 | // -skip-layers, Do not perform layer analysis 35 | // added 36 | // -previous-image, Image reference to be analyzed (usually the result of the previous build) 37 | // -run-image, Run image reference 38 | // -stack, Path to stack.toml 39 | // -tag, additional tag to apply to exported image 40 | 41 | 42 | // 0.9 onwards.. added 43 | // -launch-cache flag, used when restoring SBOM layer from prev image in daemon. 44 | // -skip-layers flag .. skips SBOM restoration entirely 45 | 46 | // 0.12 onwards.. -stack is removed and replaced with -run 47 | 48 | args.addArg("-uid", "" + factory.getBuilderImage().getUserId()); 49 | args.addArg("-gid", "" + factory.getBuilderImage().getGroupId()); 50 | args.addArg("-run-image", factory.getBuilderImage().getRunImages(factory.getPlatformLevel())[0].getReference()); 51 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 52 | args.addArg("-analyzed", LifecyclePhaseFactory.LAYERS_VOL_PATH + "/analyzed.toml"); 53 | 54 | if(factory.getPlatformLevel().atLeast("0.9") && factory.getDockerConfig().getUseDaemon()){ 55 | args.addArg("-launch-cache", LifecyclePhaseFactory.LAUNCH_CACHE_VOL_PATH); 56 | } 57 | 58 | int runAsId = factory.getBuilderImage().getUserId(); 59 | 60 | //if using daemon, add daemon arg, run as root 61 | if(factory.getDockerConfig().getUseDaemon()){ 62 | args.addArg("-daemon"); 63 | runAsId = 0; 64 | } 65 | 66 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 67 | try{ 68 | log.info("Analyze container id " + id+ " will be run with uid "+runAsId); 69 | log.debug("- container args "+args); 70 | 71 | // launch the container! 72 | log.info("- launching analyze container"); 73 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 74 | 75 | log.info("- attaching log relay"); 76 | // grab the logs to stdout. 77 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 78 | .withFollowStream(true) 79 | .withStdOut(true) 80 | .withStdErr(true) 81 | .withTimestamps(useTimestamps) 82 | .exec(new ContainerLogReader(logger)); 83 | 84 | // wait for the container to complete, and retrieve the exit code. 85 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 86 | log.info("Analyze container complete, with exit code " + rc); 87 | 88 | return ContainerStatus.of(rc,id); 89 | }catch(Exception e){ 90 | if(id!=null){ 91 | log.info("Exception during analyzer, removing container "+id); 92 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 93 | log.info("remove complete"); 94 | } 95 | throw e; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Builder.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 11 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 12 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 13 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 14 | 15 | 16 | public class Builder implements LifecyclePhase{ 17 | 18 | private static final Logger log = LoggerFactory.getLogger(Builder.class); 19 | 20 | final LifecyclePhaseFactory factory; 21 | 22 | public Builder( LifecyclePhaseFactory factory){ 23 | this.factory = factory; 24 | } 25 | 26 | @Override 27 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 28 | 29 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/builder", null); 30 | 31 | //args.addArg("-analyzed", LifecyclePhaseFactory.LAYERS_VOL_PATH + "/analyzed.toml"); 32 | args.addArg("-app", LifecyclePhaseFactory.WORKSPACE_VOL_PATH + LifecyclePhaseFactory.APP_PATH_PREFIX); 33 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 34 | args.addArg("-platform", LifecyclePhaseFactory.PLATFORM_VOL_PATH); 35 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 36 | 37 | int runAsId = factory.getBuilderImage().getUserId(); 38 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 39 | try{ 40 | log.info("Builder container id " + id+ " will be run with uid "+runAsId); 41 | log.debug("- container args "+args); 42 | 43 | // launch the container! 44 | log.info("- launching builder container"); 45 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 46 | 47 | log.info("- attaching log relay"); 48 | // grab the logs to stdout. 49 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 50 | .withFollowStream(true) 51 | .withStdOut(true) 52 | .withStdErr(true) 53 | .withTimestamps(useTimestamps) 54 | .exec(new ContainerLogReader(logger)); 55 | 56 | // wait for the container to complete, and retrieve the exit code. 57 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 58 | log.info("Builder container complete, with exit code " + rc); 59 | 60 | return ContainerStatus.of(rc,id); 61 | }catch(Exception e){ 62 | if(id!=null){ 63 | log.info("Exception during builder, removing container "+id); 64 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 65 | log.info("remove complete"); 66 | } 67 | throw e; 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Creator.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 11 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 12 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 13 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 14 | 15 | 16 | public class Creator implements LifecyclePhase{ 17 | 18 | private static final Logger log = LoggerFactory.getLogger(Creator.class); 19 | 20 | final LifecyclePhaseFactory factory; 21 | 22 | public Creator( LifecyclePhaseFactory factory ){ 23 | this.factory = factory; 24 | } 25 | 26 | @Override 27 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 28 | 29 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/creator", factory.getOutputImage().getReferenceWithLatest()); 30 | 31 | args.addArg("-uid", "" + factory.getBuilderImage().getUserId()); 32 | args.addArg("-gid", "" + factory.getBuilderImage().getGroupId()); 33 | args.addArg("-cache-dir", LifecyclePhaseFactory.CACHE_VOL_PATH); 34 | args.addArg("-app", LifecyclePhaseFactory.WORKSPACE_VOL_PATH + LifecyclePhaseFactory.APP_PATH_PREFIX); 35 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 36 | args.addArg("-platform", LifecyclePhaseFactory.PLATFORM_VOL_PATH); 37 | args.addArg("-run-image", factory.getBuilderImage().getRunImages(factory.getPlatformLevel())[0].getReferenceWithLatest()); 38 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 39 | 40 | if(factory.getPlatformLevel().atLeast("0.9") && factory.getDockerConfig().getUseDaemon()){ 41 | args.addArg("-launch-cache", LifecyclePhaseFactory.LAUNCH_CACHE_VOL_PATH); 42 | } 43 | 44 | //if using daemon, add daemon arg 45 | if(factory.getDockerConfig().getUseDaemon()){ 46 | args.addArg("-daemon"); 47 | } 48 | 49 | //creator process always has to run as root. 50 | int runAsId = 0; 51 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 52 | try{ 53 | log.info("Creator container id " + id+ " will be run with uid "+runAsId); 54 | log.debug("- container args "+args); 55 | 56 | // launch the container! 57 | log.info("- launching build container"); 58 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 59 | 60 | log.info("- attaching log relay"); 61 | // grab the logs to stdout. 62 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 63 | .withFollowStream(true) 64 | .withStdOut(true) 65 | .withStdErr(true) 66 | .withTimestamps(useTimestamps) 67 | .exec(new ContainerLogReader(logger)); 68 | 69 | // wait for the container to complete, and retrieve the exit code. 70 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 71 | log.info("Creator container complete, with exit code " + rc); 72 | 73 | return ContainerStatus.of(rc,id); 74 | }catch(Exception e){ 75 | if(id!=null){ 76 | log.info("Exception during creator, removing container "+id); 77 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 78 | log.info("remove complete"); 79 | } 80 | throw e; 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Detector.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.docker.StringContent; 11 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 12 | 13 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 14 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseAnalyzedTomlUpdater; 15 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 16 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 17 | 18 | 19 | public class Detector implements LifecyclePhase, LifecyclePhaseAnalyzedTomlUpdater{ 20 | 21 | private static final Logger log = LoggerFactory.getLogger(Detector.class); 22 | 23 | private final LifecyclePhaseFactory factory; 24 | 25 | private byte[] analyzedToml = null; 26 | 27 | public Detector( LifecyclePhaseFactory factory ){ 28 | this.factory = factory; 29 | } 30 | 31 | @Override 32 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 33 | 34 | //0.7 onwards.. detector will look for order.toml in /layers before checking other paths. 35 | // allowing platforms to write an order.toml & override the builders order.toml 36 | 37 | //0.10 onwards.. when processing an extensions build, add -generated (default: layers/generated) 38 | 39 | //0.12 onwards.. when run Images are present, invoke with -run to specify /cnb/run.toml 40 | // (creates warning if switched image is not in set). 41 | 42 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/detector", null); 43 | 44 | args.addArg("-app", LifecyclePhaseFactory.WORKSPACE_VOL_PATH + LifecyclePhaseFactory.APP_PATH_PREFIX); 45 | args.addArg("-analyzed", LifecyclePhaseFactory.LAYERS_VOL_PATH + "/analyzed.toml"); 46 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 47 | args.addArg("-platform", LifecyclePhaseFactory.PLATFORM_VOL_PATH); 48 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 49 | 50 | if(factory.getPlatformLevel().atLeast("0.10") && factory.getBuilderImage().hasExtensions() ){ 51 | args.addArg("-generated", LifecyclePhaseFactory.LAYERS_VOL_PATH + "/generated"); 52 | } 53 | 54 | if(factory.getPlatformLevel().atLeast("0.12") && factory.getBuilderImage().hasExtensions() ){ 55 | args.addArg("-run", "/cnb/run.toml"); 56 | } 57 | 58 | // detector phase must run as non-root 59 | int runAsId = factory.getBuilderImage().getUserId(); 60 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 61 | try{ 62 | log.info("Detect container id " + id+ " will be run with uid "+runAsId); 63 | log.debug("- container args "+args); 64 | 65 | // launch the container! 66 | log.info("- launching detect container"); 67 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 68 | 69 | log.info("- attaching log relay"); 70 | // grab the logs to stdout. 71 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 72 | .withFollowStream(true) 73 | .withStdOut(true) 74 | .withStdErr(true) 75 | .withTimestamps(useTimestamps) 76 | .exec(new ContainerLogReader(logger)); 77 | 78 | // wait for the container to complete, and retrieve the exit code. 79 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 80 | log.info("Detect container complete, with exit code " + rc); 81 | 82 | analyzedToml = ContainerUtils.getFileFromContainer(factory.getDockerConfig().getDockerClient(), 83 | id, 84 | LifecyclePhaseFactory.LAYERS_VOL_PATH + "/analyzed.toml"); 85 | 86 | return ContainerStatus.of(rc,id); 87 | }catch(Exception e){ 88 | if(id!=null){ 89 | log.info("Exception during detect, removing container "+id); 90 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 91 | log.info("remove complete"); 92 | } 93 | throw e; 94 | } 95 | } 96 | 97 | public byte[] getAnalyzedToml(){ 98 | return analyzedToml; 99 | } 100 | 101 | public void updateAnalyzedToml(String toml){ 102 | factory.addContentToLayersVolume(new StringContent("analyzed.toml", 0777, toml)); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Exporter.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 11 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 12 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 13 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 14 | 15 | 16 | public class Exporter implements LifecyclePhase{ 17 | 18 | private static final Logger log = LoggerFactory.getLogger(Exporter.class); 19 | 20 | final LifecyclePhaseFactory factory; 21 | final boolean runExtended; 22 | 23 | public Exporter( LifecyclePhaseFactory factory, boolean runExtended){ 24 | this.factory = factory; 25 | this.runExtended = runExtended; 26 | } 27 | 28 | @Override 29 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 30 | 31 | //0.6 onwards.. added -process-type (to set default process type) 32 | //0.7 onwards.. removed -run-image 33 | //0.12 onwards.. -stack is removed and replaced with -run 34 | // /cnb/stack.toml removed, replaced with /cnb/run.toml 35 | 36 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/exporter", factory.getOutputImage().getReferenceWithLatest()); 37 | 38 | args.addArg("-uid", "" + factory.getBuilderImage().getUserId()); 39 | args.addArg("-gid", "" + factory.getBuilderImage().getGroupId()); 40 | args.addArg("-app", LifecyclePhaseFactory.WORKSPACE_VOL_PATH + LifecyclePhaseFactory.APP_PATH_PREFIX); 41 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 42 | args.addArg("-cache-dir", LifecyclePhaseFactory.CACHE_VOL_PATH); 43 | args.addArg("-launch-cache", LifecyclePhaseFactory.LAUNCH_CACHE_VOL_PATH); 44 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 45 | 46 | if(factory.getPlatformLevel().lessThan("0.7")){ 47 | args.addArg("-run-image", factory.getBuilderImage().getRunImages(factory.getPlatformLevel())[0].getReferenceWithLatest()); 48 | } 49 | if(factory.getPlatformLevel().atLeast("0.12") && runExtended){ 50 | args.addArg("-run", "/cnb/run.toml"); 51 | } 52 | 53 | int runAsId = factory.getBuilderImage().getUserId(); 54 | 55 | //if using daemon, add daemon arg, run as root 56 | if(factory.getDockerConfig().getUseDaemon()){ 57 | args.addArg("-daemon"); 58 | runAsId = 0; 59 | } 60 | 61 | 62 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 63 | try{ 64 | log.info("Export container id " + id+ " will be run with uid "+runAsId); 65 | log.debug("- container args "+args); 66 | 67 | // launch the container! 68 | log.info("- launching export container"); 69 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 70 | 71 | log.info("- attaching log relay"); 72 | // grab the logs to stdout. 73 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 74 | .withFollowStream(true) 75 | .withStdOut(true) 76 | .withStdErr(true) 77 | .withTimestamps(useTimestamps) 78 | .exec(new ContainerLogReader(logger)); 79 | 80 | // wait for the container to complete, and retrieve the exit code. 81 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 82 | log.info("Export container complete, with exit code " + rc); 83 | 84 | return ContainerStatus.of(rc,id); 85 | }catch(Exception e){ 86 | if(id!=null){ 87 | log.info("Exception during export, removing container "+id); 88 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 89 | log.info("remove complete"); 90 | } 91 | throw e; 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Extender.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.ContainerLogReader; 9 | import dev.snowdrop.buildpack.docker.ContainerUtils; 10 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 11 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 12 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 13 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 14 | 15 | 16 | public class Extender implements LifecyclePhase{ 17 | 18 | private static final Logger log = LoggerFactory.getLogger(Extender.class); 19 | 20 | final LifecyclePhaseFactory factory; 21 | final String kind; 22 | 23 | public Extender( LifecyclePhaseFactory factory, String kind ){ 24 | this.factory = factory; 25 | this.kind = kind; 26 | } 27 | 28 | @Override 29 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 30 | 31 | //0.12 onwards, must specify -kind of 'build' or 'run' 32 | 33 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/extender", null); 34 | 35 | args.addArg("-uid", "" + factory.getBuilderImage().getUserId()); 36 | args.addArg("-gid", "" + factory.getBuilderImage().getGroupId()); 37 | args.addArg("-app", LifecyclePhaseFactory.WORKSPACE_VOL_PATH + LifecyclePhaseFactory.APP_PATH_PREFIX); 38 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 39 | args.addArg("-platform", LifecyclePhaseFactory.PLATFORM_VOL_PATH); 40 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 41 | 42 | if(factory.getPlatformLevel().atLeast("0.12")){ 43 | args.addArg("-kind", kind); 44 | } 45 | 46 | //extender process has to run as root. 47 | // 48 | //as per https://buildpacks.io/docs/reference/spec/migration/platform-api-0.9-0.10/ 49 | //... The extender user should have sufficient permissions to execute all RUN instructions, 50 | // typically it should run as root. 51 | int runAsId = 0; 52 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 53 | try{ 54 | log.info("Extender container id " + id+ " will be run with uid "+runAsId); 55 | log.debug("- container args "+args); 56 | 57 | // launch the container! 58 | log.info("- launching extender container"); 59 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 60 | 61 | log.info("- attaching log relay"); 62 | // grab the logs to stdout. 63 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 64 | .withFollowStream(true) 65 | .withStdOut(true) 66 | .withStdErr(true) 67 | .withTimestamps(useTimestamps) 68 | .exec(new ContainerLogReader(logger)); 69 | 70 | // wait for the container to complete, and retrieve the exit code. 71 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 72 | log.info("Extender container complete, with exit code " + rc); 73 | 74 | return ContainerStatus.of(rc,id); 75 | }catch(Exception e){ 76 | if(id!=null){ 77 | log.info("Exception during extender, removing container "+id); 78 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 79 | log.info("remove complete"); 80 | } 81 | throw e; 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/lifecycle/phases/Restorer.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle.phases; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import com.github.dockerjava.api.command.WaitContainerResultCallback; 7 | 8 | import dev.snowdrop.buildpack.BuilderImage; 9 | import dev.snowdrop.buildpack.ContainerLogReader; 10 | import dev.snowdrop.buildpack.docker.ContainerUtils; 11 | import dev.snowdrop.buildpack.docker.StringContent; 12 | import dev.snowdrop.buildpack.lifecycle.ContainerStatus; 13 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhase; 14 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseAnalyzedTomlUpdater; 15 | import dev.snowdrop.buildpack.lifecycle.LifecyclePhaseFactory; 16 | import dev.snowdrop.buildpack.utils.LifecycleArgs; 17 | 18 | 19 | public class Restorer implements LifecyclePhase, LifecyclePhaseAnalyzedTomlUpdater{ 20 | 21 | private static final Logger log = LoggerFactory.getLogger(Restorer.class); 22 | 23 | final LifecyclePhaseFactory factory; 24 | final BuilderImage originalBuilder; 25 | private byte[] analyzedToml = null; 26 | 27 | public Restorer( LifecyclePhaseFactory factory , BuilderImage originalBuilder){ 28 | this.factory = factory; 29 | this.originalBuilder = originalBuilder; 30 | } 31 | 32 | @Override 33 | public ContainerStatus runPhase(dev.snowdrop.buildpack.Logger logger, boolean useTimestamps) { 34 | 35 | //0.7 onwards.. added -analyzed, -skip-layers 36 | //0.10 onwards.. when building with extensions, add -build-image flag 37 | //0.12 onwards.. if in daemon mode, use new -daemon flag for restorer 38 | 39 | LifecycleArgs args = new LifecycleArgs("/cnb/lifecycle/restorer", null); 40 | 41 | args.addArg("-uid", "" + factory.getBuilderImage().getUserId()); 42 | args.addArg("-gid", "" + factory.getBuilderImage().getGroupId()); 43 | args.addArg("-layers", LifecyclePhaseFactory.LAYERS_VOL_PATH); 44 | args.addArg("-cache-dir", LifecyclePhaseFactory.CACHE_VOL_PATH); 45 | args.addArg("-log-level", factory.getLogConfig().getLogLevel()); 46 | 47 | if(factory.getPlatformLevel().atLeast("0.10") && factory.getBuilderImage().hasExtensions()){ 48 | //Spec unclear, setting this should be allowed, but causes errors 49 | //Not an issue, because kaniko dir MUST always be /kaniko (lpf.KANIKO_VOL_PATH) 50 | //args.addArg("-kaniko-dir", LifecyclePhaseFactory.KANIKO_VOL_PATH); 51 | 52 | //Can't use ephemeral/extended builder here as not in registry and restore tries to pull image.. 53 | //Use original configured builder instead.. 54 | args.addArg("-build-image", originalBuilder.getImage().getReferenceWithLatest()); 55 | } 56 | 57 | int runAsId = factory.getBuilderImage().getUserId(); 58 | 59 | if(factory.getPlatformLevel().atLeast("0.12") && factory.getDockerConfig().getUseDaemon()){ 60 | args.addArg("-daemon"); 61 | runAsId = 0; 62 | } 63 | 64 | 65 | String id = factory.getContainerForPhase(args.toArray(), runAsId); 66 | try{ 67 | log.info("Restorer container id " + id+ " will be run with uid "+runAsId); 68 | log.debug("- container args "+args); 69 | 70 | // launch the container! 71 | log.info("- launching restorer container"); 72 | factory.getDockerConfig().getDockerClient().startContainerCmd(id).exec(); 73 | 74 | log.info("- attaching log relay"); 75 | // grab the logs to stdout. 76 | factory.getDockerConfig().getDockerClient().logContainerCmd(id) 77 | .withFollowStream(true) 78 | .withStdOut(true) 79 | .withStdErr(true) 80 | .withTimestamps(useTimestamps) 81 | .exec(new ContainerLogReader(logger)); 82 | 83 | // wait for the container to complete, and retrieve the exit code. 84 | int rc = factory.getDockerConfig().getDockerClient().waitContainerCmd(id).exec(new WaitContainerResultCallback()).awaitStatusCode(); 85 | log.info("Restorer container complete, with exit code " + rc); 86 | 87 | analyzedToml = ContainerUtils.getFileFromContainer(factory.getDockerConfig().getDockerClient(), 88 | id, 89 | LifecyclePhaseFactory.LAYERS_VOL_PATH + "/analyzed.toml"); 90 | 91 | return ContainerStatus.of(rc,id); 92 | }catch(Exception e){ 93 | if(id!=null){ 94 | log.info("Exception during restorer, removing container "+id); 95 | ContainerUtils.removeContainer(factory.getDockerConfig().getDockerClient(), id); 96 | log.info("remove complete"); 97 | } 98 | throw e; 99 | } 100 | } 101 | 102 | public byte[] getAnalyzedToml(){ 103 | return analyzedToml; 104 | } 105 | 106 | public void updateAnalyzedToml(String toml){ 107 | factory.addContentToLayersVolume(new StringContent("analyzed.toml", 0777, toml)); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/utils/FilePermissions.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.nio.file.attribute.PosixFilePermission; 7 | import java.util.Set; 8 | 9 | public class FilePermissions { 10 | public Integer getPermissions(File file){ 11 | try{ 12 | Set fp = Files.getPosixFilePermissions(file.toPath()); 13 | int mode = (fp.contains(PosixFilePermission.OWNER_READ)?0400:0) + (fp.contains(PosixFilePermission.OWNER_WRITE)?0200:0) + (fp.contains(PosixFilePermission.OWNER_EXECUTE)?0100:0) + 14 | (fp.contains(PosixFilePermission.GROUP_READ)?040:0) + (fp.contains(PosixFilePermission.GROUP_WRITE)?020:0) + (fp.contains(PosixFilePermission.GROUP_EXECUTE)?010:0) + 15 | (fp.contains(PosixFilePermission.OTHERS_READ)?04:0) + (fp.contains(PosixFilePermission.OTHERS_WRITE)?02:0) + (fp.contains(PosixFilePermission.OTHERS_EXECUTE)?01:0); 16 | return mode; 17 | }catch(IOException | UnsupportedOperationException e){ 18 | //may not be able to process posixfileperms on all platforms, fall back to java io File perms, and set as owner & group 19 | return ( (file.canRead()?0400:0) + (file.canWrite()?0200:0) + (file.canExecute()?0100:0) )+ 20 | ( (file.canRead()?040:0) + (file.canWrite()?020:0) + (file.canExecute()?010:0) ); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | import com.fasterxml.jackson.databind.JsonNode; 8 | 9 | public class JsonUtils { 10 | public static String getValue(JsonNode root, String path) { 11 | String[] parts = path.split("/"); 12 | JsonNode next = root.get(parts[0]); 13 | if (next != null && parts.length > 1) { 14 | return getValue(next, path.substring(path.indexOf("/") + 1)); 15 | } 16 | if (next == null) { 17 | return null; 18 | } 19 | return next.asText(); 20 | } 21 | public static List getArray(JsonNode root, String path) { 22 | String[] parts = path.split("/"); 23 | JsonNode next = root.get(parts[0]); 24 | if (next != null && parts.length > 1) { 25 | return getArray(next, path.substring(path.indexOf("/") + 1)); 26 | } 27 | if (next == null) { 28 | return null; 29 | } 30 | if(next.isArray()){ 31 | ArrayList vals = new ArrayList<>(); 32 | Iterator els = next.elements(); 33 | while(els.hasNext()){ 34 | vals.add(els.next().asText()); 35 | } 36 | return vals; 37 | } 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/utils/LifecycleArgs.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class LifecycleArgs { 10 | 11 | private static final Logger log = LoggerFactory.getLogger(LifecycleArgs.class); 12 | 13 | private final String lifecycle; 14 | private final List args; 15 | private final String imageName; 16 | 17 | public LifecycleArgs(String lifecycle, String[] args, String finalImageName) { 18 | 19 | this.args = new ArrayList<>(); 20 | 21 | if(System.getenv("DEBUG_LIFECYCLE")!=null || System.getProperty("DEBUG_LIFECYCLE")!=null) { 22 | log.info("Lifecycle debug detected, invoking wrapper..."); 23 | this.lifecycle = "/cnb/lifecycle/debug"; 24 | this.args.add(lifecycle); 25 | }else{ 26 | this.lifecycle = lifecycle; 27 | } 28 | 29 | for(String arg: args){ 30 | this.args.add(arg); 31 | } 32 | this.imageName = finalImageName; 33 | } 34 | 35 | public LifecycleArgs(String lifecycle, String finalImageName) { 36 | 37 | this.args = new ArrayList<>(); 38 | 39 | if(System.getenv("DEBUG_LIFECYCLE")!=null || System.getProperty("DEBUG_LIFECYCLE")!=null) { 40 | log.info("Lifecycle debug detected, invoking wrapper..."); 41 | this.lifecycle = "/cnb/lifecycle/debug"; 42 | this.args.add(lifecycle); 43 | }else{ 44 | this.lifecycle = lifecycle; 45 | } 46 | 47 | this.imageName = finalImageName; 48 | } 49 | 50 | public void addArg(String optName, String value){ 51 | this.args.add(optName); 52 | this.args.add(value); 53 | } 54 | 55 | public void addArg(String optName){ 56 | this.args.add(optName); 57 | } 58 | 59 | 60 | public List toList(){ 61 | ArrayList allArgs = new ArrayList<>(); 62 | allArgs.add(lifecycle); 63 | allArgs.addAll(args); 64 | if(imageName!=null) 65 | allArgs.add(imageName); 66 | return allArgs; 67 | } 68 | 69 | public String[] toArray(){ 70 | return toList().toArray(new String[]{}); 71 | } 72 | 73 | public String toString(){ 74 | return toList().toString(); 75 | } 76 | 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/utils/LifecycleMetadata.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import java.util.List; 4 | 5 | import com.fasterxml.jackson.databind.DeserializationFeature; 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | 9 | import dev.snowdrop.buildpack.BuildpackException; 10 | import dev.snowdrop.buildpack.config.DockerConfig; 11 | import dev.snowdrop.buildpack.config.ImageReference; 12 | import dev.snowdrop.buildpack.docker.ImageUtils; 13 | import dev.snowdrop.buildpack.docker.ImageUtils.ImageInfo; 14 | 15 | public class LifecycleMetadata { 16 | 17 | private List platformLevels; 18 | private List buildpackLevels; 19 | 20 | public LifecycleMetadata(DockerConfig dc, ImageReference lifecycleImage) throws BuildpackException { 21 | 22 | // pull and inspect the builderImage to obtain builder metadata. 23 | ImageUtils.pullImages(dc,lifecycleImage); 24 | 25 | ImageInfo ii = ImageUtils.inspectImage(dc.getDockerClient(), lifecycleImage); 26 | 27 | String metadataJson = ii.labels.get("io.buildpacks.lifecycle.apis"); 28 | 29 | //dig into metadata for supported platforms. 30 | ObjectMapper om = new ObjectMapper(); 31 | om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 32 | try { 33 | JsonNode root = om.readTree(metadataJson); 34 | 35 | platformLevels = JsonUtils.getArray(root, "/platform/supported"); 36 | // if supported platform metadata is unparseable. 37 | if(platformLevels==null){ 38 | throw new Exception("Bad platform metadata in lifecycle image"); 39 | } 40 | 41 | buildpackLevels = JsonUtils.getArray(root, "/buildpack/supported"); 42 | // if supported platform metadata is unparseable. 43 | if(buildpackLevels==null){ 44 | throw new Exception("Bad buildpack metadata in lifecycle image"); 45 | } 46 | 47 | } catch (Exception e) { 48 | throw BuildpackException.launderThrowable(e); 49 | } 50 | } 51 | 52 | public List getSupportedPlatformLevels(){ 53 | return platformLevels; 54 | } 55 | public List getSupportedBuildpackLevels(){ 56 | return buildpackLevels; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client/src/main/java/dev/snowdrop/buildpack/utils/OperatingSytem.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | public enum OperatingSytem { 4 | 5 | WIN, 6 | LINUX, 7 | MAC, 8 | UNKNOWN; 9 | 10 | private static OperatingSytem os; 11 | 12 | public static OperatingSytem getOperationSystem() { 13 | if (os == null) { 14 | String osName = System.getProperty("os.name").toLowerCase(); 15 | if (osName.contains("win")) { 16 | os = WIN; 17 | } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { 18 | os = LINUX; 19 | } else if (osName.contains("mac")) { 20 | os = MAC; 21 | } else { 22 | os = UNKNOWN; 23 | } 24 | } 25 | return os; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/config/CacheConfigTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNull; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class CacheConfigTest { 11 | @Test 12 | void constructorTest(){ 13 | CacheConfig c1 = new CacheConfig("fred", null); 14 | assertTrue(c1.getDeleteCacheAfterBuild()); 15 | assertEquals("fred", c1.getCacheVolumeName()); 16 | 17 | CacheConfig c2 = new CacheConfig(null, null); 18 | assertTrue(c2.getDeleteCacheAfterBuild()); 19 | assertNull(c2.getCacheVolumeName()); 20 | 21 | CacheConfig c3 = new CacheConfig("fish", false); 22 | assertFalse(c3.getDeleteCacheAfterBuild()); 23 | assertEquals("fish", c3.getCacheVolumeName()); 24 | 25 | CacheConfig c4 = new CacheConfig("stilettos", true); 26 | assertTrue(c4.getDeleteCacheAfterBuild()); 27 | assertEquals("stilettos", c4.getCacheVolumeName()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/config/ImageReferenceTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | import org.junit.jupiter.api.Test; 11 | 12 | public class ImageReferenceTest { 13 | 14 | private static class P { 15 | public String val; 16 | public String expected; 17 | public P(String a, String b){ 18 | this.val = a; 19 | this.expected = b; 20 | } 21 | } 22 | 23 | @Test 24 | void checkNullImageReference(){ 25 | IllegalStateException is = assertThrows(IllegalStateException.class, () -> new ImageReference(null)); 26 | } 27 | 28 | @Test 29 | void checkImageReference(){ 30 | ImageReference ir2 = new ImageReference("wibble"); 31 | assertEquals("docker.io/wibble:latest", ir2.getCanonicalReference()); 32 | } 33 | 34 | @Test 35 | void checkAll(){ 36 | 37 | //validate permutations/combinations of docker image reference elements 38 | 39 | // [[host/]|[host:port/]]repo[:tag][@digest] 40 | 41 | //slighly complicated by needing to differentiate between stiletto/fish and localhost/fish, where the 2nd has a host 42 | //also, no host, means docker.io, and index.docker.io means docker.io 43 | 44 | List

hosts = listOf( new P("","docker.io"), new P("docker.io", "docker.io"), new P("localhost", "localhost"), new P("index.docker.io", "docker.io"), new P("quay.io", "quay.io")); 45 | List

ports = listOf( new P("", null), new P("9999", "9999") ); 46 | List repos = Arrays.asList(new String[]{"fish", "fish/wibble", "fish/wibble/stiletto", "fish/wibble/stiletto/kitten"}); 47 | List

tags = listOf( new P("", "latest"), new P("tag", "tag")); 48 | List

digests = listOf( new P("", null), new P("sha256:d51bd558b181b918fe759c3166bc2d7c6e1c6b4b695a1a0bd7abfbc6bb2f89e4", "sha256:d51bd558b181b918fe759c3166bc2d7c6e1c6b4b695a1a0bd7abfbc6bb2f89e4")); 49 | 50 | for(P host: hosts){ 51 | for(P port: ports){ 52 | for(String repo : repos){ 53 | for(P tag : tags){ 54 | for(P digest : digests){ 55 | 56 | //we can't do port, if we don't have host! 57 | if(host.val.equals("") && !port.val.equals("")) continue; 58 | 59 | //assemble the test ref according to docker reference rules. 60 | String testRef = host.val + (host.val.equals("")?"":(!port.val.equals("")?":"+port.val+"/":"/")) + repo + (tag.val.equals("")?"":":"+tag.val) + (digest.val.equals("")?"":"@"+digest.val); 61 | 62 | //assemble the expected ref (easier, as host is now mandatory) 63 | String expected = host.expected + (port.expected!=null?":"+port.expected+"/":"/") + repo + ((tag.val.equals("")&&!digest.val.equals(""))?"":":"+tag.expected) + (digest.expected!=null?"@"+digest.expected:""); 64 | 65 | //generate reference from test ref string. 66 | ImageReference ref = new ImageReference(testRef); 67 | 68 | //validate properties. 69 | assertEquals(expected, ref.getCanonicalReference(), "Test: "+testRef+" Expected: "+expected); 70 | assertEquals(host.expected, ref.getHost()); 71 | assertEquals(port.expected, ref.getPort()); 72 | assertEquals(repo, ref.getRepo()); 73 | if(tag.val.equals("")){ 74 | if(digest.val.equals("")){ 75 | assertEquals(tag.expected, ref.getTag()); 76 | }else{ 77 | assertEquals(null, ref.getTag()); 78 | } 79 | } else { 80 | assertEquals(tag.expected, ref.getTag()); 81 | } 82 | assertEquals(digest.expected, ref.getDigest()); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | private List

listOf(P... p){ 91 | return Arrays.asList(p); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/config/LogConfigTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | 13 | import dev.snowdrop.buildpack.Logger; 14 | 15 | @ExtendWith(MockitoExtension.class) 16 | public class LogConfigTest { 17 | @Test 18 | void checkLogLevel() { 19 | LogConfig lc1 = new LogConfig(null, null, null); 20 | assertEquals("info", lc1.getLogLevel()); 21 | LogConfig lc2 = new LogConfig("debug", null, null); 22 | assertEquals("debug", lc2.getLogLevel()); 23 | } 24 | 25 | @Test 26 | void checkUseTimestamps() { 27 | LogConfig lc1 = new LogConfig(null, null, null); 28 | assertTrue(lc1.getUseTimestamps()); 29 | LogConfig lc2 = new LogConfig(null, true, null); 30 | assertTrue(lc2.getUseTimestamps()); 31 | LogConfig lc3 = new LogConfig(null, false, null); 32 | assertFalse(lc3.getUseTimestamps()); 33 | } 34 | 35 | @Test 36 | void checkLogger(@Mock Logger logger) { 37 | LogConfig lc1 = new LogConfig(null, null, null); 38 | assertNotNull(lc1.getLogger()); 39 | LogConfig lc2 = new LogConfig(null, null, logger); 40 | assertEquals(logger, lc2.getLogger()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/config/PlatformConfigTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.config; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertNotNull; 6 | import static org.junit.jupiter.api.Assertions.assertNull; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | public class PlatformConfigTest { 15 | @Test 16 | void checkPlatformLevel(){ 17 | PlatformConfig pc1 = new PlatformConfig(null, null, null, null, null); 18 | assertNotNull(pc1.getPlatformLevel()); 19 | 20 | PlatformConfig pc2 = new PlatformConfig("0.7", null, null, null, null); 21 | assertNotNull(pc2.getPlatformLevel()); 22 | assertEquals("0.7", pc2.getPlatformLevel()); 23 | } 24 | 25 | @Test 26 | void checkEnv() { 27 | PlatformConfig pc1 = new PlatformConfig(null, null, null, null, null); 28 | assertNotNull(pc1.getEnvironment()); 29 | 30 | Map m = new HashMap<>(); 31 | PlatformConfig pc2 = new PlatformConfig(null, null, m, null, null); 32 | } 33 | 34 | @Test 35 | void checkLifecycleImage() { 36 | PlatformConfig pc1 = new PlatformConfig(null, null, null, null, null); 37 | assertNull(pc1.getLifecycleImage()); 38 | 39 | PlatformConfig pc2 = new PlatformConfig(null, new ImageReference("fish"), null, null, null); 40 | assertNotNull(pc2.getLifecycleImage()); 41 | assertEquals(new ImageReference("fish").getCanonicalReference(), pc2.getLifecycleImage().getCanonicalReference()); 42 | } 43 | 44 | @Test 45 | void checkTrustBuilder() { 46 | PlatformConfig pc1 = new PlatformConfig(null, null, null, null, null); 47 | assertNull(pc1.getTrustBuilder()); 48 | 49 | PlatformConfig pc2 = new PlatformConfig(null, null, null, true, null); 50 | assertTrue(pc2.getTrustBuilder()); 51 | 52 | PlatformConfig pc3 = new PlatformConfig(null, null, null, false, null); 53 | assertFalse(pc3.getTrustBuilder()); 54 | } 55 | 56 | @Test 57 | void checkDebugScript() { 58 | PlatformConfig pc1 = new PlatformConfig(null, null, null, null, null); 59 | assertNull(pc1.getPhaseDebugScript()); 60 | 61 | PlatformConfig pc2 = new PlatformConfig(null, null, null, true, "echo 'hello world'"); 62 | assertNotNull(pc2.getPhaseDebugScript()); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/docker/ContainerEntryTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.ByteArrayInputStream; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | import org.junit.jupiter.api.Test; 13 | 14 | public class ContainerEntryTest { 15 | //simple test to catch if we change the public API by mistake. 16 | @Test 17 | void apiRegressionTest() throws Exception{ 18 | ContainerEntry ce = new ContainerEntry(){ 19 | 20 | @Override 21 | public String getPath() { 22 | return "fish"; 23 | } 24 | 25 | @Override 26 | public long getSize() { 27 | return 1337; 28 | } 29 | 30 | @Override 31 | public Integer getMode() { 32 | return 0755; 33 | } 34 | 35 | @Override 36 | public DataSupplier getDataSupplier() { 37 | return new DataSupplier(){ 38 | @Override 39 | public InputStream getData() { 40 | return new ByteArrayInputStream("FISH".getBytes()); 41 | } 42 | }; 43 | } 44 | 45 | }; 46 | 47 | assertEquals("fish", ce.getPath()); 48 | assertEquals(1337, ce.getSize()); 49 | assertEquals(0755, ce.getMode()); 50 | 51 | 52 | assertNotNull(ce.getDataSupplier().getData()); 53 | BufferedReader br = new BufferedReader(new InputStreamReader(ce.getDataSupplier().getData(), StandardCharsets.UTF_8)); 54 | assertEquals("FISH",br.readLine()); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/docker/ContentTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import java.util.List; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class ContentTest { 11 | //simple test to catch if we change the public API by mistake. 12 | @Test 13 | void apiRegressionTest() throws Exception{ 14 | Content c = new Content(){ 15 | @Override 16 | public List getContainerEntries() { 17 | return new StringContent("/fish", 0777, "stiletto").getContainerEntries(); 18 | } 19 | 20 | }; 21 | 22 | assertNotNull(c.getContainerEntries()); 23 | assertEquals(1,c.getContainerEntries().size()); 24 | ContainerEntry a = c.getContainerEntries().get(0); 25 | assertEquals("/fish", a.getPath()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/docker/DockerClientUtilsTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.ExtendWith; 8 | import org.mockito.junit.jupiter.MockitoExtension; 9 | 10 | import com.github.dockerjava.api.DockerClient; 11 | 12 | import dev.snowdrop.buildpack.config.HostAndSocketConfig; 13 | 14 | @ExtendWith(MockitoExtension.class) 15 | public class DockerClientUtilsTest { 16 | 17 | @Test 18 | void getDockerHost() { 19 | String val = System.getenv("DOCKER_HOST"); 20 | 21 | HostAndSocketConfig result = DockerClientUtils.probeContainerRuntime(null); 22 | 23 | if (val != null) { 24 | assertEquals(val, result.getHost().get()); 25 | } 26 | 27 | assertNotNull(result); 28 | } 29 | 30 | @Test 31 | void getDockerClient() { 32 | DockerClient dc = DockerClientUtils.getDockerClient(); 33 | assertNotNull(dc); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/docker/ImageUtilsTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.docker; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 4 | import static org.junit.jupiter.api.Assertions.assertEquals; 5 | import static org.mockito.ArgumentMatchers.eq; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | import static org.mockito.Mockito.never; 9 | import static org.mockito.Mockito.lenient; 10 | import static org.mockito.Mockito.atLeast; 11 | 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import org.junit.jupiter.api.Test; 18 | import org.junit.jupiter.api.extension.ExtendWith; 19 | import org.mockito.ArgumentMatchers; 20 | import org.mockito.Mock; 21 | import org.mockito.junit.jupiter.MockitoExtension; 22 | 23 | import com.github.dockerjava.api.DockerClient; 24 | import com.github.dockerjava.api.command.InspectImageCmd; 25 | import com.github.dockerjava.api.command.InspectImageResponse; 26 | import com.github.dockerjava.api.command.ListImagesCmd; 27 | import com.github.dockerjava.api.command.PullImageCmd; 28 | import com.github.dockerjava.api.model.ContainerConfig; 29 | import com.github.dockerjava.api.model.Image; 30 | 31 | import dev.snowdrop.buildpack.config.DockerConfig; 32 | import dev.snowdrop.buildpack.config.ImageReference; 33 | import dev.snowdrop.buildpack.docker.ImageUtils.ImageInfo; 34 | 35 | @ExtendWith(MockitoExtension.class) 36 | public class ImageUtilsTest { 37 | 38 | 39 | @Test 40 | void testInspectImage(@Mock DockerClient dc, 41 | @Mock InspectImageCmd iic, 42 | @Mock InspectImageResponse iir, 43 | @Mock ContainerConfig cc 44 | ) { 45 | 46 | ImageReference test = new ImageReference("test"); 47 | String imageName = "test:latest"; 48 | 49 | when(dc.inspectImageCmd(eq(imageName))).thenReturn(iic); 50 | when(iic.exec()).thenReturn(iir); 51 | 52 | when(iir.getId()).thenReturn("id"); 53 | when(iir.getConfig()).thenReturn(cc); 54 | when(cc.getEnv()).thenReturn(new String[] {"one","two"}); 55 | Map labels = new HashMap(); 56 | labels.put("l1", "v1"); 57 | when(cc.getLabels()).thenReturn(labels); 58 | 59 | ImageInfo ii = ImageUtils.inspectImage(dc, test); 60 | 61 | assertEquals(ii.id,"id"); 62 | assertArrayEquals(ii.env, new String[] {"one","two"} ); 63 | assertEquals(ii.labels, labels); 64 | 65 | verify(iic).exec(); 66 | } 67 | 68 | @Test 69 | void testPullImageSingleUnknown(@Mock DockerConfig config, 70 | @Mock DockerClient dc, 71 | @Mock ListImagesCmd lic, 72 | @Mock PullImageCmd pic) throws InterruptedException { 73 | 74 | ImageReference test = new ImageReference("test"); 75 | String imageName = "test:latest"; 76 | 77 | lenient().when(config.getDockerClient()).thenReturn(dc); 78 | lenient().when(config.getPullPolicy()).thenReturn(DockerConfig.PullPolicy.IF_NOT_PRESENT); 79 | lenient().when(dc.listImagesCmd()).thenReturn(lic); 80 | lenient().when(lic.exec()).thenReturn(new ArrayList()); 81 | 82 | when(dc.pullImageCmd(eq(imageName))).thenReturn(pic); 83 | 84 | ImageUtils.pullImages(config, test); 85 | 86 | verify(pic, atLeast(1)).exec(ArgumentMatchers.any()); 87 | } 88 | 89 | @Test 90 | void testPullImageSingleKnown(@Mock DockerConfig config, 91 | @Mock DockerClient dc, 92 | @Mock ListImagesCmd lic, 93 | @Mock Image i, 94 | @Mock PullImageCmd pic) throws InterruptedException { 95 | 96 | ImageReference test = new ImageReference("test:v1"); 97 | String imageName = "test:v1"; 98 | 99 | lenient().when(config.getDockerClient()).thenReturn(dc); 100 | lenient().when(config.getPullPolicy()).thenReturn(DockerConfig.PullPolicy.IF_NOT_PRESENT); 101 | lenient().when(dc.listImagesCmd()).thenReturn(lic); 102 | 103 | List li = new ArrayList(); 104 | li.add(i); 105 | when(lic.exec()).thenReturn(li); 106 | when(i.getRepoTags()).thenReturn(new String[] {imageName}); 107 | 108 | //(dc.pullImageCmd(eq(imageName))).thenReturn(pic); 109 | 110 | ImageUtils.pullImages(config, test); 111 | 112 | verify(dc, never()).pullImageCmd(ArgumentMatchers.any()); 113 | } 114 | 115 | @Test 116 | void testPullImageSingleKnownNoTag(@Mock DockerConfig config, 117 | @Mock DockerClient dc, 118 | @Mock ListImagesCmd lic, 119 | @Mock Image i, 120 | @Mock PullImageCmd pic) throws InterruptedException { 121 | 122 | ImageReference test = new ImageReference("test"); 123 | String imageName = "test:latest"; 124 | 125 | lenient().when(config.getDockerClient()).thenReturn(dc); 126 | lenient().when(config.getPullPolicy()).thenReturn(DockerConfig.PullPolicy.IF_NOT_PRESENT); 127 | lenient().when(dc.listImagesCmd()).thenReturn(lic); 128 | 129 | List li = new ArrayList(); 130 | li.add(i); 131 | when(lic.exec()).thenReturn(li); 132 | when(i.getRepoTags()).thenReturn(new String[] {imageName}); 133 | 134 | when(dc.pullImageCmd(eq(imageName))).thenReturn(pic); 135 | 136 | ImageUtils.pullImages(config, test); 137 | 138 | verify(pic, atLeast(1)).exec(ArgumentMatchers.any()); 139 | } 140 | 141 | @Test 142 | void testPullImageSingleKnownLatest(@Mock DockerConfig config, 143 | @Mock DockerClient dc, 144 | @Mock ListImagesCmd lic, 145 | @Mock Image i, 146 | @Mock PullImageCmd pic) throws InterruptedException { 147 | 148 | ImageReference test = new ImageReference("test:latest"); 149 | String imageName = "test:latest"; 150 | 151 | lenient().when(config.getDockerClient()).thenReturn(dc); 152 | lenient().when(config.getPullPolicy()).thenReturn(DockerConfig.PullPolicy.IF_NOT_PRESENT); 153 | lenient().when(dc.listImagesCmd()).thenReturn(lic); 154 | 155 | List li = new ArrayList(); 156 | li.add(i); 157 | when(lic.exec()).thenReturn(li); 158 | when(i.getRepoTags()).thenReturn(new String[] {imageName}); 159 | 160 | when(dc.pullImageCmd(eq(imageName))).thenReturn(pic); 161 | 162 | ImageUtils.pullImages(config, test); 163 | 164 | verify(pic, atLeast(1)).exec(ArgumentMatchers.any()); 165 | } 166 | 167 | } 168 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/lifecycle/ContainerStatusTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.lifecycle; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class ContainerStatusTest { 8 | @Test 9 | void testContainerStatus(){ 10 | ContainerStatus cs1 = ContainerStatus.of(66, "fish"); 11 | assertEquals(66,cs1.getRc()); 12 | assertEquals("fish", cs1.getContainerId()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/utils/JsonUtilsTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertNotNull; 5 | import static org.junit.jupiter.api.Assertions.fail; 6 | 7 | import java.util.List; 8 | 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.fasterxml.jackson.core.JsonProcessingException; 12 | import com.fasterxml.jackson.databind.DeserializationFeature; 13 | import com.fasterxml.jackson.databind.JsonMappingException; 14 | import com.fasterxml.jackson.databind.JsonNode; 15 | import com.fasterxml.jackson.databind.ObjectMapper; 16 | 17 | public class JsonUtilsTest { 18 | @Test 19 | void testJsonUtils() { 20 | 21 | String json = "{\"heels\":[\"kitten\",\"stiletto\",\"wedge\"], \"aNumber\":1337, \"aWord\":\"wibble\", \"sizes\":[11,12], \"models\":{\"patent\":{\"color\":\"red\"}}}}"; 22 | 23 | ObjectMapper om = new ObjectMapper(); 24 | om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 25 | try { 26 | JsonNode root = om.readTree(json); 27 | 28 | String word = JsonUtils.getValue(root, "aWord"); 29 | assertEquals("wibble", word); 30 | 31 | String nestedWord = JsonUtils.getValue(root, "models/patent/color"); 32 | assertEquals("red",nestedWord); 33 | 34 | String number = JsonUtils.getValue(root, "aNumber"); 35 | assertEquals("1337", number); 36 | 37 | List wordList = JsonUtils.getArray(root, "heels"); 38 | assertNotNull(wordList); 39 | assertEquals(3, wordList.size()); 40 | 41 | List numberList = JsonUtils.getArray(root, "sizes"); 42 | assertNotNull(numberList); 43 | assertEquals(2, numberList.size()); 44 | } catch (JsonMappingException e) { 45 | fail(e); 46 | } catch (JsonProcessingException e) { 47 | fail(e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /client/src/test/java/dev/snowdrop/buildpack/utils/LifecycleArgsTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.buildpack.utils; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class LifecycleArgsTest { 8 | @Test 9 | void testLifecycleArgs(){ 10 | LifecycleArgs la1 = new LifecycleArgs("/command", null); 11 | assertEquals(1, la1.toList().size()); 12 | assertEquals("/command", la1.toList().get(0)); 13 | 14 | la1.addArg("-daemon"); 15 | assertEquals(2, la1.toList().size()); 16 | 17 | la1.addArg("-flag", "value"); 18 | assertEquals(4, la1.toList().size()); 19 | 20 | LifecycleArgs la2 = new LifecycleArgs("/command", "image-name"); 21 | assertEquals(2, la2.toList().size()); 22 | assertEquals("/command", la2.toList().get(0)); 23 | assertEquals("image-name", la2.toList().get(1)); 24 | 25 | la2.addArg("-flag","value"); 26 | assertEquals(4, la2.toList().size()); 27 | assertEquals("image-name", la2.toList().get(3)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /samples/build-me/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### VS Code ### 13 | .vscode/ 14 | 15 | ### Mac OS ### 16 | .DS_Store 17 | 18 | .env -------------------------------------------------------------------------------- /samples/build-me/README.md: -------------------------------------------------------------------------------- 1 | # Buildpack Builder config example 2 | 3 | Example of a java project able to build a Quarkus, Spring Boot, ... project 4 | using the DSL `BuildConfig.builder()` 5 | 6 | To use it, configure the following mandatory environment variables pointing to a project (example: [hello-quarkus](../hello-quarkus), [hello-spring](../hello-spring)) to be built as a container image 7 | 8 | ```bash 9 | export PROJECT_PATH= 10 | # can be without registry or a full registry reference with host, port(optional), path & tag 11 | export IMAGE_REF= 12 | ``` 13 | 14 | If you plan to push your image to a registry, then set your registry credential using these variables: 15 | ```bash 16 | export REGISTRY_USERNAME="" 17 | export REGISTRY_PASSWORD="" 18 | export REGISTRY_ADDRESS="docker.io" 19 | ``` 20 | 21 | If you prefer that lifecycle don't access the mounted docker socket but talk directly with the container registry to build the image, then set to `false` the following variable: 22 | ```shell 23 | export USE_DAEMON=false 24 | ``` 25 | 26 | Execute this command in a terminal: 27 | ```bash 28 | mvn compile exec:java 29 | ``` 30 | 31 | You can also pass the `BP_` or `CNB_` environment variables: 32 | ```bash 33 | export BP_JVM_VERSION="21" 34 | export BP_MAVEN_BUILT_ARTIFACT="target/quarkus-app/lib/ target/quarkus-app/*.jar target/quarkus-app/app/ target/quarkus-app/quarkus" 35 | export CNB_LOG_LEVEL=debug 36 | etc 37 | ``` -------------------------------------------------------------------------------- /samples/build-me/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | dev.snowdrop 8 | build-me 9 | Snowdrop :: Java Buildpack Client :: Samples :: Build me 10 | 1.0-SNAPSHOT 11 | 12 | 13 | 21 14 | 21 15 | UTF-8 16 | 17 | 18 | 19 | 20 | dev.snowdrop 21 | buildpack-client 22 | 0.0.14 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | 1.18.30 28 | provided 29 | 30 | 31 | 32 | 33 | com.github.docker-java 34 | docker-java-core 35 | 3.4.0 36 | 37 | 38 | 39 | 40 | org.slf4j 41 | slf4j-simple 42 | 1.7.30 43 | 44 | 45 | 46 | 47 | 48 | org.apache.maven.plugins 49 | maven-compiler-plugin 50 | 3.11.0 51 | 52 | 53 | -proc:full 54 | 55 | 56 | 57 | org.codehaus.mojo 58 | exec-maven-plugin 59 | 3.5.0 60 | 61 | dev.snowdrop.BuildMe 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /samples/build-me/src/main/java/dev/snowdrop/BuildMe.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import java.io.File; 4 | import java.util.*; 5 | import java.util.stream.Collectors; 6 | 7 | import dev.snowdrop.buildpack.*; 8 | import dev.snowdrop.buildpack.config.*; 9 | 10 | public class BuildMe { 11 | 12 | public static void main(String... args) { 13 | 14 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 15 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 16 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 17 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 18 | 19 | String IMAGE_REF = Optional.ofNullable(System.getenv("IMAGE_REF")) 20 | .orElseThrow(() -> new IllegalStateException("Missing env var: IMAGE_REF")); 21 | String PROJECT_PATH = Optional.ofNullable(System.getenv("PROJECT_PATH")) 22 | .orElseThrow(() -> new IllegalStateException("Missing env var: PROJECT_PATH")); 23 | String USE_DAEMON = Optional.ofNullable(System.getenv("USE_DAEMON")) 24 | .orElse("true"); 25 | 26 | Map envMap = System.getenv().entrySet().stream() 27 | .filter(entry -> entry.getKey().startsWith("BP_") || entry.getKey().startsWith("CNB_")) 28 | .collect(Collectors.toMap( 29 | Map.Entry::getKey, 30 | Map.Entry::getValue, 31 | (oldValue, newValue) -> newValue, 32 | HashMap::new 33 | )); 34 | 35 | List authInfo = new ArrayList<>(); 36 | if(System.getenv("REGISTRY_ADDRESS")!=null){ 37 | String registry = System.getenv("REGISTRY_ADDRESS"); 38 | String username = System.getenv("REGISTRY_USER"); 39 | String password = System.getenv("REGISTRY_PASS"); 40 | RegistryAuthConfig authConfig = RegistryAuthConfig.builder() 41 | .withRegistryAddress(registry) 42 | .withUsername(username) 43 | .withPassword(password) 44 | .build(); 45 | authInfo.add(authConfig); 46 | } 47 | 48 | int exitCode = BuildConfig.builder() 49 | .withBuilderImage(new ImageReference("paketocommunity/builder-ubi-base:latest")) 50 | .withOutputImage(new ImageReference(IMAGE_REF)) 51 | .withNewPlatformConfig() 52 | .withEnvironment(envMap) 53 | .endPlatformConfig() 54 | .withNewDockerConfig() 55 | .withAuthConfigs(authInfo) 56 | .withUseDaemon(Boolean.parseBoolean(USE_DAEMON)) 57 | .endDockerConfig() 58 | .withNewLogConfig() 59 | .withLogger(new SystemLogger()) 60 | .withLogLevel("debug") 61 | .and() 62 | .addNewFileContentApplication(new File(PROJECT_PATH)) 63 | .build() 64 | .getExitCode(); 65 | 66 | System.exit(exitCode); 67 | } 68 | } -------------------------------------------------------------------------------- /samples/hello-quarkus/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /samples/hello-quarkus/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | .flattened-pom.xml 8 | 9 | # Eclipse 10 | .project 11 | .classpath 12 | .settings/ 13 | bin/ 14 | 15 | # IntelliJ 16 | .idea 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # NetBeans 22 | nb-configuration.xml 23 | 24 | # Visual Studio Code 25 | .vscode 26 | .factorypath 27 | 28 | # OSX 29 | .DS_Store 30 | 31 | # Vim 32 | *.swp 33 | *.swo 34 | 35 | # patch 36 | *.orig 37 | *.rej 38 | 39 | # Local environment 40 | .env 41 | 42 | # Plugin directory 43 | /.quarkus/cli/plugins/ 44 | -------------------------------------------------------------------------------- /samples/hello-quarkus/.mvn/wrapper/.gitignore: -------------------------------------------------------------------------------- 1 | maven-wrapper.jar 2 | -------------------------------------------------------------------------------- /samples/hello-quarkus/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URL; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.Paths; 28 | import java.nio.file.StandardCopyOption; 29 | 30 | public final class MavenWrapperDownloader 31 | { 32 | private static final String WRAPPER_VERSION = "3.2.0"; 33 | 34 | private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); 35 | 36 | public static void main( String[] args ) 37 | { 38 | log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); 39 | 40 | if ( args.length != 2 ) 41 | { 42 | System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); 43 | System.exit( 1 ); 44 | } 45 | 46 | try 47 | { 48 | log( " - Downloader started" ); 49 | final URL wrapperUrl = new URL( args[0] ); 50 | final String jarPath = args[1].replace( "..", "" ); // Sanitize path 51 | final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); 52 | downloadFileFromURL( wrapperUrl, wrapperJarPath ); 53 | log( "Done" ); 54 | } 55 | catch ( IOException e ) 56 | { 57 | System.err.println( "- Error downloading: " + e.getMessage() ); 58 | if ( VERBOSE ) 59 | { 60 | e.printStackTrace(); 61 | } 62 | System.exit( 1 ); 63 | } 64 | } 65 | 66 | private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) 67 | throws IOException 68 | { 69 | log( " - Downloading to: " + wrapperJarPath ); 70 | if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) 71 | { 72 | final String username = System.getenv( "MVNW_USERNAME" ); 73 | final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); 74 | Authenticator.setDefault( new Authenticator() 75 | { 76 | @Override 77 | protected PasswordAuthentication getPasswordAuthentication() 78 | { 79 | return new PasswordAuthentication( username, password ); 80 | } 81 | } ); 82 | } 83 | try ( InputStream inStream = wrapperUrl.openStream() ) 84 | { 85 | Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); 86 | } 87 | log( " - Downloader complete" ); 88 | } 89 | 90 | private static void log( String msg ) 91 | { 92 | if ( VERBOSE ) 93 | { 94 | System.out.println( msg ); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /samples/hello-quarkus/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /samples/hello-quarkus/README.md: -------------------------------------------------------------------------------- 1 | # quarkus-hello 2 | 3 | This project uses Quarkus, the Supersonic Subatomic Java Framework. 4 | 5 | If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . 6 | 7 | ## Running the application in dev mode 8 | 9 | You can run your application in dev mode that enables live coding using: 10 | ```shell script 11 | ./mvnw compile quarkus:dev 12 | ``` 13 | 14 | > **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. 15 | 16 | ## Packaging and running the application 17 | 18 | The application can be packaged using: 19 | ```shell script 20 | ./mvnw package 21 | ``` 22 | It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. 23 | Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. 24 | 25 | The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. 26 | 27 | If you want to build an _über-jar_, execute the following command: 28 | ```shell script 29 | ./mvnw package -Dquarkus.package.type=uber-jar 30 | ``` 31 | 32 | The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. 33 | 34 | ## Creating a native executable 35 | 36 | You can create a native executable using: 37 | ```shell script 38 | ./mvnw package -Dnative 39 | ``` 40 | 41 | Or, if you don't have GraalVM installed, you can run the native executable build in a container using: 42 | ```shell script 43 | ./mvnw package -Dnative -Dquarkus.native.container-build=true 44 | ``` 45 | 46 | You can then execute your native executable with: `./target/quarkus-hello-1.0-runner` 47 | 48 | If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling. 49 | 50 | ## Related Guides 51 | 52 | - Kubernetes ([guide](https://quarkus.io/guides/kubernetes)): Generate Kubernetes resources from annotations 53 | - RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. 54 | 55 | ## Provided Code 56 | 57 | ### RESTEasy Reactive 58 | 59 | Easily start your Reactive RESTful Web Services 60 | 61 | [Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) 62 | -------------------------------------------------------------------------------- /samples/hello-quarkus/id-debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A simple script to dump uid/gid & /workspace permissions during a build. 4 | # 5 | # This can be very useful when implementing a platform, to determine if /workspace 6 | # has permissions for the executing uid/gid. 7 | 8 | id 9 | ls -aln /workspace 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /samples/hello-quarkus/pack.java: -------------------------------------------------------------------------------- 1 | ///usr/bin/env jbang "$0" "$@" ; exit $? 2 | 3 | //REPOS mavencentral,jitpack 4 | //DEPS org.slf4j:slf4j-simple:1.7.30 5 | //DEPS ${env.CURRENT_WORKFLOW_DEP:dev.snowdrop:buildpack-client:0.0.15-SNAPSHOT} 6 | 7 | 8 | import java.io.File; 9 | import dev.snowdrop.buildpack.*; 10 | import dev.snowdrop.buildpack.config.*; 11 | import dev.snowdrop.buildpack.docker.*; 12 | import java.util.Map; 13 | 14 | import com.github.dockerjava.api.DockerClient; 15 | import com.github.dockerjava.api.command.InspectImageResponse; 16 | import com.github.dockerjava.api.command.PullImageResultCallback; 17 | import com.github.dockerjava.api.model.Image; 18 | import com.github.dockerjava.api.exception.DockerClientException; 19 | import com.github.dockerjava.api.exception.NotFoundException; 20 | 21 | public class pack { 22 | 23 | public static void main(String... args) { 24 | 25 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 26 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 27 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 28 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 29 | 30 | String debugScript = "#!/bin/bash\n" + 31 | "echo \"DEBUG INFO\"\n" + 32 | "echo \"Root Perms\"\n" + 33 | "stat -c \"%A $a %u %g %n\" /*\n" + 34 | "echo \"Layer dir Content\"\n" + 35 | "ls -lar /layers\n" + 36 | "echo \"Workspace dir Content\"\n" + 37 | "ls -lar /workspace\n" + 38 | "echo \"Analyzed toml\"\n" + 39 | "ls -la /layers\n" + 40 | "cat /layers/analyzed.toml\n" + 41 | "LC=$1\n" + 42 | "shift\n" + 43 | "$LC \"$@\""; 44 | 45 | String JDK="17"; 46 | 47 | Map envMap = new java.util.HashMap<>(); 48 | envMap.put("BP_JVM_VERSION",JDK); 49 | 50 | int exitCode = BuildConfig.builder() 51 | //.withBuilderImage(new ImageReference("docker.io/paketocommunity/builder-ubi-base")) 52 | .withBuilderImage(new ImageReference("quay.io/ozzydweller/testbuilders:paketo-default")) 53 | .withOutputImage(new ImageReference("snowdrop/hello-quarkus:jvm"+JDK)) 54 | .withNewDockerConfig() 55 | .withPullPolicy(DockerConfig.PullPolicy.IF_NOT_PRESENT) 56 | .and() 57 | .withNewPlatformConfig() 58 | .withPhaseDebugScript(debugScript) 59 | .withEnvironment(envMap) 60 | .and() 61 | .withNewLogConfig() 62 | .withLogger(new SystemLogger()) 63 | .withLogLevel("debug") 64 | .and() 65 | .addNewFileContentApplication(new File(".")) 66 | .build() 67 | .getExitCode(); 68 | 69 | System.exit(exitCode); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /samples/hello-quarkus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | dev.snowdrop 6 | quarkus-hello 7 | Snowdrop :: Java Buildpack Client :: Samples :: Hello Quarkus 8 | 1.0 9 | 10 | 3.12.1 11 | UTF-8 12 | UTF-8 13 | quarkus-bom 14 | io.quarkus.platform 15 | 3.8.3 16 | true 17 | 3.2.5 18 | 19 | 20 | 21 | 22 | ${quarkus.platform.group-id} 23 | ${quarkus.platform.artifact-id} 24 | ${quarkus.platform.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | io.quarkus 33 | quarkus-kubernetes 34 | 35 | 36 | io.quarkus 37 | quarkus-resteasy-reactive 38 | 39 | 40 | io.quarkus 41 | quarkus-container-image-buildpack 42 | 43 | 44 | io.quarkus 45 | quarkus-arc 46 | 47 | 48 | io.quarkus 49 | quarkus-junit5 50 | test 51 | 52 | 53 | io.rest-assured 54 | rest-assured 55 | test 56 | 57 | 58 | 59 | 60 | 61 | ${quarkus.platform.group-id} 62 | quarkus-maven-plugin 63 | ${quarkus.platform.version} 64 | true 65 | 66 | 67 | 68 | build 69 | generate-code 70 | generate-code-tests 71 | 72 | 73 | 74 | 75 | 76 | maven-compiler-plugin 77 | ${compiler-plugin.version} 78 | 79 | 80 | -parameters 81 | 82 | 83 | 84 | 85 | org.codehaus.mojo 86 | exec-maven-plugin 87 | 1.5.0 88 | 89 | 90 | compile 91 | 92 | exec 93 | 94 | 95 | /bin/sh 96 | ${basedir} 97 | 98 | ${basedir}/id-debug.sh 99 | 100 | 101 | 102 | 103 | 104 | 105 | maven-surefire-plugin 106 | ${surefire-plugin.version} 107 | 108 | 109 | org.jboss.logmanager.LogManager 110 | ${maven.home} 111 | 112 | 113 | 114 | 115 | maven-failsafe-plugin 116 | ${surefire-plugin.version} 117 | 118 | 119 | 120 | integration-test 121 | verify 122 | 123 | 124 | 125 | 126 | 127 | ${project.build.directory}/${project.build.finalName}-runner 128 | org.jboss.logmanager.LogManager 129 | ${maven.home} 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | native 138 | 139 | 140 | native 141 | 142 | 143 | 144 | false 145 | native 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-hello-jvm . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-jvm 15 | # 16 | # If you want to include the debug port into your docker image 17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. 18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 19 | # when running the container 20 | # 21 | # Then run the container using : 22 | # 23 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-jvm 24 | # 25 | # This image uses the `run-java.sh` script to run the application. 26 | # This scripts computes the command line to execute your Java application, and 27 | # includes memory/GC tuning. 28 | # You can configure the behavior using the following environment properties: 29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") 30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options 31 | # in JAVA_OPTS (example: "-Dsome.property=foo") 32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is 33 | # used to calculate a default maximal heap memory based on a containers restriction. 34 | # If used in a container without any memory constraints for the container then this 35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio 36 | # of the container available memory as set here. The default is `50` which means 50% 37 | # of the available memory is used as an upper boundary. You can skip this mechanism by 38 | # setting this value to `0` in which case no `-Xmx` option is added. 39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This 40 | # is used to calculate a default initial heap memory based on the maximum heap memory. 41 | # If used in a container without any memory constraints for the container then this 42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio 43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` 44 | # is used as the initial heap size. You can skip this mechanism by setting this value 45 | # to `0` in which case no `-Xms` option is added (example: "25") 46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. 47 | # This is used to calculate the maximum value of the initial heap memory. If used in 48 | # a container without any memory constraints for the container then this option has 49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set 50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will 51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") 52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output 53 | # when things are happening. This option, if set to true, will set 54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). 55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: 56 | # true"). 57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). 58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in 59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") 60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). 61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. 62 | # (example: "20") 63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. 64 | # (example: "40") 65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. 66 | # (example: "4") 67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus 68 | # previous GC times. (example: "90") 69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") 70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") 71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should 72 | # contain the necessary JRE command-line options to specify the required GC, which 73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). 74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") 75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") 76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be 77 | # accessed directly. (example: "foo.example.com,bar.example.com") 78 | # 79 | ### 80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 81 | 82 | ENV LANGUAGE='en_US:en' 83 | 84 | 85 | # We make four distinct layers so if there are application changes the library layers can be re-used 86 | COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ 87 | COPY --chown=185 target/quarkus-app/*.jar /deployments/ 88 | COPY --chown=185 target/quarkus-app/app/ /deployments/app/ 89 | COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ 90 | 91 | EXPOSE 8080 92 | USER 185 93 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 94 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" 95 | 96 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] 97 | 98 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/docker/Dockerfile.legacy-jar: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Dquarkus.package.type=legacy-jar 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/quarkus-hello-legacy-jar . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-legacy-jar 15 | # 16 | # If you want to include the debug port into your docker image 17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. 18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 19 | # when running the container 20 | # 21 | # Then run the container using : 22 | # 23 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-legacy-jar 24 | # 25 | # This image uses the `run-java.sh` script to run the application. 26 | # This scripts computes the command line to execute your Java application, and 27 | # includes memory/GC tuning. 28 | # You can configure the behavior using the following environment properties: 29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") 30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options 31 | # in JAVA_OPTS (example: "-Dsome.property=foo") 32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is 33 | # used to calculate a default maximal heap memory based on a containers restriction. 34 | # If used in a container without any memory constraints for the container then this 35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio 36 | # of the container available memory as set here. The default is `50` which means 50% 37 | # of the available memory is used as an upper boundary. You can skip this mechanism by 38 | # setting this value to `0` in which case no `-Xmx` option is added. 39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This 40 | # is used to calculate a default initial heap memory based on the maximum heap memory. 41 | # If used in a container without any memory constraints for the container then this 42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio 43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` 44 | # is used as the initial heap size. You can skip this mechanism by setting this value 45 | # to `0` in which case no `-Xms` option is added (example: "25") 46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. 47 | # This is used to calculate the maximum value of the initial heap memory. If used in 48 | # a container without any memory constraints for the container then this option has 49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set 50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will 51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") 52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output 53 | # when things are happening. This option, if set to true, will set 54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). 55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: 56 | # true"). 57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). 58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in 59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") 60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). 61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. 62 | # (example: "20") 63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. 64 | # (example: "40") 65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. 66 | # (example: "4") 67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus 68 | # previous GC times. (example: "90") 69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") 70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") 71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should 72 | # contain the necessary JRE command-line options to specify the required GC, which 73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). 74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") 75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") 76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be 77 | # accessed directly. (example: "foo.example.com,bar.example.com") 78 | # 79 | ### 80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 81 | 82 | ENV LANGUAGE='en_US:en' 83 | 84 | 85 | COPY target/lib/* /deployments/lib/ 86 | COPY target/*-runner.jar /deployments/quarkus-run.jar 87 | 88 | EXPOSE 8080 89 | USER 185 90 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 91 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" 92 | 93 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] 94 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Dnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-hello . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 28 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Dnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/quarkus-hello . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:2.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/java/dev/snowdrop/GreetingResource.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.Path; 5 | import jakarta.ws.rs.Produces; 6 | import jakarta.ws.rs.core.MediaType; 7 | 8 | @Path("/hello") 9 | public class GreetingResource { 10 | 11 | @GET 12 | @Produces(MediaType.TEXT_PLAIN) 13 | public String hello() { 14 | return "Hello from RESTEasy Reactive"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowdrop/java-buildpack-client/38fc7150e74795017d2333a915602e7a2f4b19b2/samples/hello-quarkus/src/main/resources/application.properties -------------------------------------------------------------------------------- /samples/hello-quarkus/src/test/java/dev/snowdrop/GreetingResourceIT.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import io.quarkus.test.junit.QuarkusIntegrationTest; 4 | 5 | @QuarkusIntegrationTest 6 | class GreetingResourceIT extends GreetingResourceTest { 7 | // Execute the same tests but in packaged mode. 8 | } 9 | -------------------------------------------------------------------------------- /samples/hello-quarkus/src/test/java/dev/snowdrop/GreetingResourceTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | @QuarkusTest 10 | class GreetingResourceTest { 11 | @Test 12 | void testHelloEndpoint() { 13 | given() 14 | .when().get("/hello") 15 | .then() 16 | .statusCode(200) 17 | .body(is("Hello from RESTEasy Reactive")); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /samples/hello-spring/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /samples/hello-spring/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowdrop/java-buildpack-client/38fc7150e74795017d2333a915602e7a2f4b19b2/samples/hello-spring/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /samples/hello-spring/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /samples/hello-spring/pack.java: -------------------------------------------------------------------------------- 1 | ///usr/bin/env jbang "$0" "$@" ; exit $? 2 | 3 | //REPOS mavencentral,jitpack 4 | //DEPS org.slf4j:slf4j-simple:1.7.30 5 | //DEPS ${env.CURRENT_WORKFLOW_DEP:dev.snowdrop:buildpack-client:0.0.15-SNAPSHOT} 6 | 7 | import java.io.File; 8 | import java.util.HashMap; 9 | import dev.snowdrop.buildpack.*; 10 | import dev.snowdrop.buildpack.config.*; 11 | import dev.snowdrop.buildpack.docker.*; 12 | 13 | public class pack { 14 | 15 | public static void main(String... args) { 16 | 17 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 18 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 19 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 20 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 21 | 22 | HashMap env = new HashMap<>(); 23 | 24 | int exitCode = BuildConfig.builder() 25 | .withBuilderImage(new ImageReference("docker.io/paketocommunity/builder-ubi-base")) 26 | .withOutputImage(new ImageReference("snowdrop/hello-spring")) 27 | .withNewLogConfig() 28 | .withLogger(new SystemLogger()) 29 | .withLogLevel("debug") 30 | .and() 31 | .withNewPlatformConfig() 32 | .withEnvironment(env) 33 | .and() 34 | .addNewFileContentApplication(new File(".")) 35 | .build() 36 | .getExitCode(); 37 | 38 | System.exit(exitCode); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/hello-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.1 9 | 10 | 11 | dev.snowdrop 12 | hello-spring 13 | 0.0.3-SNAPSHOT 14 | Snowdrop :: Java Buildpack Client :: Samples :: Hello Spring 15 | Demo project for Spring Boot with Java Buildpack client script 16 | 17 | 11 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /samples/hello-spring/src/main/java/dev/snowdrop/hellospring/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.hellospring; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /samples/hello-spring/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /samples/testcases/README.md: -------------------------------------------------------------------------------- 1 | ## Github Workflow test projects 2 | 3 | These projects are driven by the github workflows multiplatform-test and multiplatform-registry-test, which drive this library with a variety of java levels and os platforms against a variety of container runtimes (podman 4/5 and docker), to ensure we are able to build in each case. -------------------------------------------------------------------------------- /samples/testcases/RunRegistryTest.java: -------------------------------------------------------------------------------- 1 | ///usr/bin/env jbang "$0" "$@" ; exit $? 2 | 3 | //REPOS mavencentral,jitpack 4 | //DEPS org.slf4j:slf4j-simple:1.7.30 5 | //DEPS ${env.CURRENT_WORKFLOW_DEP} 6 | 7 | 8 | import java.io.File; 9 | import dev.snowdrop.buildpack.*; 10 | import dev.snowdrop.buildpack.config.*; 11 | import dev.snowdrop.buildpack.docker.*; 12 | import dev.snowdrop.buildpack.utils.OperatingSytem; 13 | import java.util.ArrayList; 14 | import java.util.Map; 15 | import java.util.HashMap; 16 | import java.util.Optional; 17 | import java.util.List; 18 | 19 | import com.github.dockerjava.api.DockerClient; 20 | import com.github.dockerjava.api.command.InspectImageResponse; 21 | import com.github.dockerjava.api.command.PullImageResultCallback; 22 | import com.github.dockerjava.api.model.Image; 23 | import com.github.dockerjava.api.exception.DockerClientException; 24 | import com.github.dockerjava.api.exception.NotFoundException; 25 | 26 | public class RunRegistryTest { 27 | 28 | public static void main(String... args) { 29 | try{ 30 | run(); 31 | }catch(Exception e){ 32 | System.err.println("Error during run..."); 33 | e.printStackTrace(); 34 | System.exit(250); 35 | } 36 | } 37 | 38 | private static void run() throws Exception { 39 | 40 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 41 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 42 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 43 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 44 | 45 | String debugScript = "#!/bin/bash\n" + 46 | "echo \"DEBUG INFO\"\n" + 47 | "echo \"Root Perms\"\n" + 48 | "stat -c \"%A $a %u %g %n\" /*\n" + 49 | "echo \"Layer dir Content\"\n" + 50 | "ls -lar /layers\n" + 51 | "echo \"Workspace dir Content\"\n" + 52 | "ls -lar /workspace\n" + 53 | "echo \"Analyzed toml\"\n" + 54 | "cat /layers/analyzed.toml\n" + 55 | "echo \"Run toml\"\n" + 56 | "cat /cnb/run.toml\n" + 57 | "echo \"Stack toml\"\n" + 58 | "cat /cnb/stack.toml\n" + 59 | "echo \"DEBUG END\"\n" + 60 | "LC=$1\n" + 61 | "shift\n" + 62 | "$LC \"$@\""; 63 | 64 | String projectPath = Optional.ofNullable(System.getenv("PROJECT_PATH")).orElse("."); 65 | String JDK = Optional.ofNullable(System.getenv("JDK")).orElse("17"); 66 | String builderImage = Optional.ofNullable(System.getenv("BUILDER_IMAGE")).orElse("quay.io/ozzydweller/testbuilders:debug-exporter"); 67 | String outputImage = Optional.ofNullable(System.getenv("OUTPUT_IMAGE")).orElse("snowdrop/hello-quarkus:jvm"+JDK); 68 | 69 | System.out.println("RunTest Building path '"+projectPath+"' using '"+builderImage+"' requesting jdk '"+JDK+"'"); 70 | 71 | Map envMap = new HashMap<>(); 72 | envMap.put("BP_JVM_VERSION",JDK); 73 | 74 | List authInfo = new ArrayList<>(); 75 | 76 | if(System.getenv("REGISTRY_ADDRESS")!=null){ 77 | String registry = System.getenv("REGISTRY_ADDRESS"); 78 | String username = System.getenv("REGISTRY_USER"); 79 | String password = System.getenv("REGISTRY_PASS"); 80 | RegistryAuthConfig authConfig = RegistryAuthConfig.builder() 81 | .withRegistryAddress(registry) 82 | .withUsername(username) 83 | .withPassword(password) 84 | .build(); 85 | authInfo.add(authConfig); 86 | } 87 | 88 | int exitCode = 0; 89 | 90 | OperatingSytem os = OperatingSytem.getOperationSystem(); 91 | if(os != OperatingSytem.WIN) { 92 | System.out.println("Building "+outputImage+" using "+authInfo.size()+" credentials, with builder "+builderImage); 93 | exitCode = BuildConfig.builder() 94 | .withBuilderImage(new ImageReference(builderImage)) 95 | .withOutputImage(new ImageReference(outputImage)) 96 | .withNewDockerConfig() 97 | .withAuthConfigs(authInfo) 98 | .withUseDaemon(false) 99 | .and() 100 | .withNewPlatformConfig() 101 | .withEnvironment(envMap) 102 | //.withPlatformLevel("0.12") 103 | //.withPhaseDebugScript(debugScript) 104 | .and() 105 | .withNewLogConfig() 106 | .withLogger(new SystemLogger()) 107 | //.withLogLevel("debug") 108 | .and() 109 | .addNewFileContentApplication(new File(projectPath)) 110 | .build() 111 | .getExitCode(); 112 | }else{ 113 | //github windows runner cannot run linux docker containers, 114 | //so we'll just test the ability for the library to correctly talk 115 | //to the docker daemon. 116 | DockerClient dc = DockerClientUtils.getDockerClient(); 117 | try{ 118 | dc.pingCmd().exec(); 119 | }catch(Exception e){ 120 | throw new RuntimeException("Unable to verify docker settings", e); 121 | } 122 | } 123 | 124 | System.exit(exitCode); 125 | } 126 | } -------------------------------------------------------------------------------- /samples/testcases/RunTest.java: -------------------------------------------------------------------------------- 1 | ///usr/bin/env jbang "$0" "$@" ; exit $? 2 | 3 | //REPOS mavencentral,jitpack 4 | //DEPS org.slf4j:slf4j-simple:1.7.30 5 | //DEPS ${env.CURRENT_WORKFLOW_DEP} 6 | 7 | 8 | import java.io.File; 9 | import dev.snowdrop.buildpack.*; 10 | import dev.snowdrop.buildpack.config.*; 11 | import dev.snowdrop.buildpack.docker.*; 12 | import dev.snowdrop.buildpack.utils.OperatingSytem; 13 | import java.util.Map; 14 | import java.util.HashMap; 15 | import java.util.Optional; 16 | 17 | import com.github.dockerjava.api.DockerClient; 18 | import com.github.dockerjava.api.command.InspectImageResponse; 19 | import com.github.dockerjava.api.command.PullImageResultCallback; 20 | import com.github.dockerjava.api.model.Image; 21 | import com.github.dockerjava.api.exception.DockerClientException; 22 | import com.github.dockerjava.api.exception.NotFoundException; 23 | 24 | public class RunTest { 25 | 26 | public static void main(String... args) { 27 | try{ 28 | run(); 29 | }catch(Exception e){ 30 | System.err.println("Error during run..."); 31 | e.printStackTrace(); 32 | System.exit(250); 33 | } 34 | } 35 | 36 | private static void run() throws Exception { 37 | 38 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack","debug"); 39 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.docker","debug"); 40 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle","debug"); 41 | System.setProperty("org.slf4j.simpleLogger.log.dev.snowdrop.buildpack.lifecycle.phases","debug"); 42 | 43 | String debugScript = "#!/bin/bash\n" + 44 | "echo \"DEBUG INFO\"\n" + 45 | "echo \"Root Perms\"\n" + 46 | "stat -c \"%A $a %u %g %n\" /*\n" + 47 | "echo \"Layer dir Content\"\n" + 48 | "ls -lar /layers\n" + 49 | "echo \"Workspace dir Content\"\n" + 50 | "ls -lar /workspace\n" + 51 | "echo \"Analyzed toml\"\n" + 52 | "cat /layers/analyzed.toml\n" + 53 | "echo \"Run toml\"\n" + 54 | "cat /cnb/run.toml\n" + 55 | "echo \"Stack toml\"\n" + 56 | "cat /cnb/stack.toml\n" + 57 | "echo \"DEBUG END\"\n" + 58 | "LC=$1\n" + 59 | "shift\n" + 60 | "$LC \"$@\""; 61 | 62 | String projectPath = Optional.ofNullable(System.getenv("PROJECT_PATH")).orElse("."); 63 | String JDK = Optional.ofNullable(System.getenv("JDK")).orElse("17"); 64 | String builderImage = Optional.ofNullable(System.getenv("BUILDER_IMAGE")).orElse("quay.io/ozzydweller/testbuilders:debug-exporter"); 65 | String outputImage = Optional.ofNullable(System.getenv("OUTPUT_IMAGE")).orElse("snowdrop/hello-quarkus:jvm"+JDK); 66 | 67 | System.out.println("RunTest Building path '"+projectPath+"' using '"+builderImage+"' requesting jdk '"+JDK+"'"); 68 | 69 | Map envMap = new HashMap<>(); 70 | envMap.put("BP_JVM_VERSION",JDK); 71 | 72 | int exitCode = 0; 73 | 74 | OperatingSytem os = OperatingSytem.getOperationSystem(); 75 | if(os != OperatingSytem.WIN) { 76 | exitCode = BuildConfig.builder() 77 | .withBuilderImage(new ImageReference(builderImage)) 78 | .withOutputImage(new ImageReference(outputImage)) 79 | .withNewPlatformConfig() 80 | .withEnvironment(envMap) 81 | //.withPlatformLevel("0.12") 82 | //.withPhaseDebugScript(debugScript) 83 | .and() 84 | .withNewLogConfig() 85 | .withLogger(new SystemLogger()) 86 | //.withLogLevel("debug") 87 | .and() 88 | .addNewFileContentApplication(new File(projectPath)) 89 | .build() 90 | .getExitCode(); 91 | }else{ 92 | //github windows runner cannot run linux docker containers, 93 | //so we'll just test the ability for the library to correctly talk 94 | //to the docker daemon. 95 | DockerClient dc = DockerClientUtils.getDockerClient(); 96 | try{ 97 | dc.pingCmd().exec(); 98 | }catch(Exception e){ 99 | throw new RuntimeException("Unable to verify docker settings", e); 100 | } 101 | } 102 | 103 | System.exit(exitCode); 104 | } 105 | } -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | .flattened-pom.xml 8 | 9 | # Eclipse 10 | .project 11 | .classpath 12 | .settings/ 13 | bin/ 14 | 15 | # IntelliJ 16 | .idea 17 | *.ipr 18 | *.iml 19 | *.iws 20 | 21 | # NetBeans 22 | nb-configuration.xml 23 | 24 | # Visual Studio Code 25 | .vscode 26 | .factorypath 27 | 28 | # OSX 29 | .DS_Store 30 | 31 | # Vim 32 | *.swp 33 | *.swo 34 | 35 | # patch 36 | *.orig 37 | *.rej 38 | 39 | # Local environment 40 | .env 41 | 42 | # Plugin directory 43 | /.quarkus/cli/plugins/ 44 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/.mvn/wrapper/.gitignore: -------------------------------------------------------------------------------- 1 | maven-wrapper.jar 2 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.Authenticator; 23 | import java.net.PasswordAuthentication; 24 | import java.net.URL; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.Paths; 28 | import java.nio.file.StandardCopyOption; 29 | 30 | public final class MavenWrapperDownloader 31 | { 32 | private static final String WRAPPER_VERSION = "3.2.0"; 33 | 34 | private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); 35 | 36 | public static void main( String[] args ) 37 | { 38 | log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); 39 | 40 | if ( args.length != 2 ) 41 | { 42 | System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); 43 | System.exit( 1 ); 44 | } 45 | 46 | try 47 | { 48 | log( " - Downloader started" ); 49 | final URL wrapperUrl = new URL( args[0] ); 50 | final String jarPath = args[1].replace( "..", "" ); // Sanitize path 51 | final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); 52 | downloadFileFromURL( wrapperUrl, wrapperJarPath ); 53 | log( "Done" ); 54 | } 55 | catch ( IOException e ) 56 | { 57 | System.err.println( "- Error downloading: " + e.getMessage() ); 58 | if ( VERBOSE ) 59 | { 60 | e.printStackTrace(); 61 | } 62 | System.exit( 1 ); 63 | } 64 | } 65 | 66 | private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) 67 | throws IOException 68 | { 69 | log( " - Downloading to: " + wrapperJarPath ); 70 | if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) 71 | { 72 | final String username = System.getenv( "MVNW_USERNAME" ); 73 | final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); 74 | Authenticator.setDefault( new Authenticator() 75 | { 76 | @Override 77 | protected PasswordAuthentication getPasswordAuthentication() 78 | { 79 | return new PasswordAuthentication( username, password ); 80 | } 81 | } ); 82 | } 83 | try ( InputStream inStream = wrapperUrl.openStream() ) 84 | { 85 | Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); 86 | } 87 | log( " - Downloader complete" ); 88 | } 89 | 90 | private static void log( String msg ) 91 | { 92 | if ( VERBOSE ) 93 | { 94 | System.out.println( msg ); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/README.md: -------------------------------------------------------------------------------- 1 | # quarkus-hello 2 | 3 | This project uses Quarkus, the Supersonic Subatomic Java Framework. 4 | 5 | If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . 6 | 7 | ## Running the application in dev mode 8 | 9 | You can run your application in dev mode that enables live coding using: 10 | ```shell script 11 | ./mvnw compile quarkus:dev 12 | ``` 13 | 14 | > **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. 15 | 16 | ## Packaging and running the application 17 | 18 | The application can be packaged using: 19 | ```shell script 20 | ./mvnw package 21 | ``` 22 | It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. 23 | Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. 24 | 25 | The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. 26 | 27 | If you want to build an _über-jar_, execute the following command: 28 | ```shell script 29 | ./mvnw package -Dquarkus.package.type=uber-jar 30 | ``` 31 | 32 | The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. 33 | 34 | ## Creating a native executable 35 | 36 | You can create a native executable using: 37 | ```shell script 38 | ./mvnw package -Dnative 39 | ``` 40 | 41 | Or, if you don't have GraalVM installed, you can run the native executable build in a container using: 42 | ```shell script 43 | ./mvnw package -Dnative -Dquarkus.native.container-build=true 44 | ``` 45 | 46 | You can then execute your native executable with: `./target/quarkus-hello-1.0-runner` 47 | 48 | If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling. 49 | 50 | ## Related Guides 51 | 52 | - Kubernetes ([guide](https://quarkus.io/guides/kubernetes)): Generate Kubernetes resources from annotations 53 | - RESTEasy Reactive ([guide](https://quarkus.io/guides/resteasy-reactive)): A Jakarta REST implementation utilizing build time processing and Vert.x. This extension is not compatible with the quarkus-resteasy extension, or any of the extensions that depend on it. 54 | 55 | ## Provided Code 56 | 57 | ### RESTEasy Reactive 58 | 59 | Easily start your Reactive RESTful Web Services 60 | 61 | [Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) 62 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | dev.snowdrop 6 | quarkus-hello 7 | Snowdrop :: Java Buildpack Client :: Samples :: Hello Quarkus 8 | 1.0 9 | 10 | 3.12.1 11 | UTF-8 12 | UTF-8 13 | quarkus-bom 14 | io.quarkus.platform 15 | 3.8.3 16 | true 17 | 3.2.5 18 | 19 | 20 | 21 | 22 | ${quarkus.platform.group-id} 23 | ${quarkus.platform.artifact-id} 24 | ${quarkus.platform.version} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | io.quarkus 33 | quarkus-kubernetes 34 | 35 | 36 | io.quarkus 37 | quarkus-resteasy-reactive 38 | 39 | 40 | io.quarkus 41 | quarkus-container-image-buildpack 42 | 43 | 44 | io.quarkus 45 | quarkus-arc 46 | 47 | 48 | io.quarkus 49 | quarkus-junit5 50 | test 51 | 52 | 53 | io.rest-assured 54 | rest-assured 55 | test 56 | 57 | 58 | 59 | 60 | 61 | ${quarkus.platform.group-id} 62 | quarkus-maven-plugin 63 | ${quarkus.platform.version} 64 | true 65 | 66 | 67 | 68 | build 69 | generate-code 70 | generate-code-tests 71 | 72 | 73 | 74 | 75 | 76 | maven-compiler-plugin 77 | ${compiler-plugin.version} 78 | 79 | 80 | -parameters 81 | 82 | 83 | 84 | 85 | maven-surefire-plugin 86 | ${surefire-plugin.version} 87 | 88 | 89 | org.jboss.logmanager.LogManager 90 | ${maven.home} 91 | 92 | 93 | 94 | 95 | maven-failsafe-plugin 96 | ${surefire-plugin.version} 97 | 98 | 99 | 100 | integration-test 101 | verify 102 | 103 | 104 | 105 | 106 | 107 | ${project.build.directory}/${project.build.finalName}-runner 108 | org.jboss.logmanager.LogManager 109 | ${maven.home} 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | native 118 | 119 | 120 | native 121 | 122 | 123 | 124 | false 125 | native 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/docker/Dockerfile.jvm: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-hello-jvm . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-jvm 15 | # 16 | # If you want to include the debug port into your docker image 17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. 18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 19 | # when running the container 20 | # 21 | # Then run the container using : 22 | # 23 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-jvm 24 | # 25 | # This image uses the `run-java.sh` script to run the application. 26 | # This scripts computes the command line to execute your Java application, and 27 | # includes memory/GC tuning. 28 | # You can configure the behavior using the following environment properties: 29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") 30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options 31 | # in JAVA_OPTS (example: "-Dsome.property=foo") 32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is 33 | # used to calculate a default maximal heap memory based on a containers restriction. 34 | # If used in a container without any memory constraints for the container then this 35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio 36 | # of the container available memory as set here. The default is `50` which means 50% 37 | # of the available memory is used as an upper boundary. You can skip this mechanism by 38 | # setting this value to `0` in which case no `-Xmx` option is added. 39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This 40 | # is used to calculate a default initial heap memory based on the maximum heap memory. 41 | # If used in a container without any memory constraints for the container then this 42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio 43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` 44 | # is used as the initial heap size. You can skip this mechanism by setting this value 45 | # to `0` in which case no `-Xms` option is added (example: "25") 46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. 47 | # This is used to calculate the maximum value of the initial heap memory. If used in 48 | # a container without any memory constraints for the container then this option has 49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set 50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will 51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") 52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output 53 | # when things are happening. This option, if set to true, will set 54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). 55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: 56 | # true"). 57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). 58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in 59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") 60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). 61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. 62 | # (example: "20") 63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. 64 | # (example: "40") 65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. 66 | # (example: "4") 67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus 68 | # previous GC times. (example: "90") 69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") 70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") 71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should 72 | # contain the necessary JRE command-line options to specify the required GC, which 73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). 74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") 75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") 76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be 77 | # accessed directly. (example: "foo.example.com,bar.example.com") 78 | # 79 | ### 80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 81 | 82 | ENV LANGUAGE='en_US:en' 83 | 84 | 85 | # We make four distinct layers so if there are application changes the library layers can be re-used 86 | COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ 87 | COPY --chown=185 target/quarkus-app/*.jar /deployments/ 88 | COPY --chown=185 target/quarkus-app/app/ /deployments/app/ 89 | COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ 90 | 91 | EXPOSE 8080 92 | USER 185 93 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 94 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" 95 | 96 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] 97 | 98 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/docker/Dockerfile.legacy-jar: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Dquarkus.package.type=legacy-jar 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/quarkus-hello-legacy-jar . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-legacy-jar 15 | # 16 | # If you want to include the debug port into your docker image 17 | # you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. 18 | # Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 19 | # when running the container 20 | # 21 | # Then run the container using : 22 | # 23 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-legacy-jar 24 | # 25 | # This image uses the `run-java.sh` script to run the application. 26 | # This scripts computes the command line to execute your Java application, and 27 | # includes memory/GC tuning. 28 | # You can configure the behavior using the following environment properties: 29 | # - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") 30 | # - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options 31 | # in JAVA_OPTS (example: "-Dsome.property=foo") 32 | # - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is 33 | # used to calculate a default maximal heap memory based on a containers restriction. 34 | # If used in a container without any memory constraints for the container then this 35 | # option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio 36 | # of the container available memory as set here. The default is `50` which means 50% 37 | # of the available memory is used as an upper boundary. You can skip this mechanism by 38 | # setting this value to `0` in which case no `-Xmx` option is added. 39 | # - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This 40 | # is used to calculate a default initial heap memory based on the maximum heap memory. 41 | # If used in a container without any memory constraints for the container then this 42 | # option has no effect. If there is a memory constraint then `-Xms` is set to a ratio 43 | # of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` 44 | # is used as the initial heap size. You can skip this mechanism by setting this value 45 | # to `0` in which case no `-Xms` option is added (example: "25") 46 | # - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. 47 | # This is used to calculate the maximum value of the initial heap memory. If used in 48 | # a container without any memory constraints for the container then this option has 49 | # no effect. If there is a memory constraint then `-Xms` is limited to the value set 50 | # here. The default is 4096MB which means the calculated value of `-Xms` never will 51 | # be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") 52 | # - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output 53 | # when things are happening. This option, if set to true, will set 54 | # `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). 55 | # - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: 56 | # true"). 57 | # - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). 58 | # - CONTAINER_CORE_LIMIT: A calculated core limit as described in 59 | # https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") 60 | # - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). 61 | # - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. 62 | # (example: "20") 63 | # - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. 64 | # (example: "40") 65 | # - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. 66 | # (example: "4") 67 | # - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus 68 | # previous GC times. (example: "90") 69 | # - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") 70 | # - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") 71 | # - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should 72 | # contain the necessary JRE command-line options to specify the required GC, which 73 | # will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). 74 | # - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") 75 | # - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") 76 | # - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be 77 | # accessed directly. (example: "foo.example.com,bar.example.com") 78 | # 79 | ### 80 | FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 81 | 82 | ENV LANGUAGE='en_US:en' 83 | 84 | 85 | COPY target/lib/* /deployments/lib/ 86 | COPY target/*-runner.jar /deployments/quarkus-run.jar 87 | 88 | EXPOSE 8080 89 | USER 185 90 | ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" 91 | ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" 92 | 93 | ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] 94 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Dnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-hello . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 28 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Dnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/quarkus-hello . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/quarkus-hello 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:2.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/java/dev/snowdrop/GreetingResource.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import jakarta.ws.rs.GET; 4 | import jakarta.ws.rs.Path; 5 | import jakarta.ws.rs.Produces; 6 | import jakarta.ws.rs.core.MediaType; 7 | 8 | @Path("/hello") 9 | public class GreetingResource { 10 | 11 | @GET 12 | @Produces(MediaType.TEXT_PLAIN) 13 | public String hello() { 14 | return "Hello from RESTEasy Reactive"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowdrop/java-buildpack-client/38fc7150e74795017d2333a915602e7a2f4b19b2/samples/testcases/hello-quarkus/src/main/resources/application.properties -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/test/java/dev/snowdrop/GreetingResourceIT.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import io.quarkus.test.junit.QuarkusIntegrationTest; 4 | 5 | @QuarkusIntegrationTest 6 | class GreetingResourceIT extends GreetingResourceTest { 7 | // Execute the same tests but in packaged mode. 8 | } 9 | -------------------------------------------------------------------------------- /samples/testcases/hello-quarkus/src/test/java/dev/snowdrop/GreetingResourceTest.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | @QuarkusTest 10 | class GreetingResourceTest { 11 | @Test 12 | void testHelloEndpoint() { 13 | given() 14 | .when().get("/hello") 15 | .then() 16 | .statusCode(200) 17 | .body(is("Hello from RESTEasy Reactive")); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /samples/testcases/hello-spring/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /samples/testcases/hello-spring/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snowdrop/java-buildpack-client/38fc7150e74795017d2333a915602e7a2f4b19b2/samples/testcases/hello-spring/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /samples/testcases/hello-spring/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /samples/testcases/hello-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.1 9 | 10 | 11 | dev.snowdrop 12 | hello-spring 13 | 0.0.3-SNAPSHOT 14 | Snowdrop :: Java Buildpack Client :: Samples :: Hello Spring 15 | Demo project for Spring Boot with Java Buildpack client script 16 | 17 | 11 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /samples/testcases/hello-spring/src/main/java/dev/snowdrop/hellospring/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package dev.snowdrop.hellospring; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /samples/testcases/hello-spring/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------