Binary Builds

Introduction

The binary build feature in FaktorZ allows developers to upload source or artifacts directly to a build instead of having the build pull source from a Git repository URL. Any BuildConfig with a strategy of source, Docker, or custom may be started as a binary build. When starting a build from local artifacts, the existing source reference is replaced with the source coming from the local user’s machine.

The source may be supplied in several ways which correspond to arguments available when using the start-build command:

  • From a file (--from-file): This is the case when the entire source of the build consists of a single file. For example, it may be a Dockerfile for a Docker build, pom.xml for a Wildfly build, or Gemfile for a Ruby build.

  • From a directory (--from-directory): Use this when the source is in a local directory and is not committed to a Git repository. The start-build command will create an archive of the given directory and upload it to the builder as source.

  • From an archive (--from-archive): Use this when an archive with the source already exists. The archive may be in either tar, tar.gz, or zip format.

  • From a Git repository (--from-repo): This is for source that is currently part of a Git repository on the user’s local machine. The HEAD commit of the current repository will be archived and sent to FaktorZ for building.

Use Cases

Binary builds remove the requirement for a build to pull source from an existing Git repository. Reasons to use binary builds include:

  • Building and testing local code changes. Source from a public repository can be cloned and local changes can be uploaded to FaktorZ for building. Local changes do not have to be committed or pushed anywhere.

  • Building private code. New builds can be started from scratch as binary builds. The source can then be uploaded directly from your local workstation to FaktorZ without having to check it in to an SCM.

  • Building images with artifacts from other sources. With Jenkins pipelines, binary builds are useful to combine artifacts built with tools such as Maven or C compiler, and runtime images that make use of those builds.

Limitations

  • Binary builds are not repeatable. Because binary builds rely on the user uploading artifacts at build start, FaktorZ cannot repeat the same build without the user repeating the same upload every time.

  • Binary builds cannot be triggered automatically. They can only be started manually when the user uploads the required binary artifacts.

Builds that are started as binary builds may also have a configured source URL. If that’s the case, triggers will successfully launch the build but source will come from the configured source URL and not from what was supplied by the user the last time the build ran.

Tutorials Overview

The following tutorials assume that you have an FaktorZ cluster available and that you have a project where you can create artifacts. It requires that you have both git and oc available locally.

Tutorial: Building local code changes

  1. Create a new application based on an existing source repository and create a route for it:

    $ oc new-app https://github.com/openshift/ruby-hello-world.git
    $ oc expose svc/ruby-hello-world
  2. Wait for the initial build to complete and view the application’s page by navigating to the route’s host. You should get a welcome page:

    $ oc get route ruby-hello-world
  3. Clone the repository locally:

    $ git clone https://github.com/openshift/ruby-hello-world.git
    $ cd ruby-hello-world
  4. Make a change to the application’s view. Using your favorite editor, edit views/main.rb: Change the <body> tag to <body style="background-color:blue">.

  5. Start a new build with your locally-modified source. From the repository’s local directory, run:

    ----
    $ oc start-build ruby-hello-world --from-dir="." --follow
    ----

Once your build has completed and the application has redeployed, navigating to the application’s route host should result in a page with a blue background.

You can keep making changes locally and building your code with oc start-build --from-dir.

You can also create a branch of the code, commit your changes locally, and use the repository’s HEAD as the source for your build:

$ git checkout -b my_branch
$ git add .
$ git commit -m "My changes"
$ oc start-build ruby-hello-world --from-repo="." --follow

Tutorial: Building private code

  1. Create a local directory to hold your code:

    $ mkdir myapp
    $ cd myapp
  2. In the directory create a file named Dockerfile with the following content:

    FROM centos:centos7
    
    EXPOSE 8080
    
    COPY index.html /var/run/web/index.html
    
    CMD cd /var/run/web && python -m SimpleHTTPServer 8080
  3. Create a file named index.html with the following content:

    <html>
      <head>
        <title>My local app</title>
      </head>
      <body>
        <h1>Hello World</h1>
        <p>This is my local application</p>
      </body>
    </html>
  4. Create a new build for your application:

    $ oc new-build --strategy docker --binary --docker-image centos:centos7 --name myapp
  5. Start a binary build using the local directory’s content:

    $ oc start-build myapp --from-dir . --follow
  6. Deploy the application using new-app, then create a route for it:

    $ oc new-app myapp
    $ oc expose svc/myapp
  7. Get the host name for your route and navigate to it:

    $ oc get route myapp

After having built and deployed your code, you can iterate by making changes to your local files and starting new builds by invoking oc start-build myapp --from-dir. Once built, the code will be automatically deployed and the changes will be reflected in your browser when you refresh the page.

Tutorial: Binary artifacts from pipeline

Jenkins on FaktorZ allows using slave images with the appropriate tools to build your code. For example, you can use the maven slave to build a WAR from your code repository. However, once this artifact is built, you need to commit it to an image that contains the right runtime artifacts to run your code. A binary build may be used to add these artifacts to your runtime image. In the following tutorial, we’ll create a Jenkins pipeline that makes use of the maven slave to build a WAR, and then uses a binary build with a Dockerfile to add that WAR to a wildfly runtime image.

  1. Create a new directory for your application:

    $ mkdir mavenapp
    $ cd mavenapp
  2. Create a Dockerfile that copies a WAR to the appropriate location inside a wildfly image for execution. Copy the following to a local file named Dockerfile:

    FROM wildfly:latest
    COPY ROOT.war /wildfly/standalone/deployments/ROOT.war
    CMD  $STI_SCRIPTS_PATH/run
  3. Create a new BuildConfig for that Dockerfile:

    This will automatically start a build that will initially fail because the ROOT.war artifact is not yet available. The pipeline below will pass that WAR to the build using a binary build.

    $ cat Dockerfile | oc new-build -D - --name mavenapp
  4. Create a BuildConfig with the Jenkins pipeline that will build a WAR and then use that WAR to build an image using the previously created Dockerfile. The same pattern can be used for other platforms where a binary artifact is built by a set of tools and is then combined with a different runtime image for the final package. Save the following code to mavenapp-pipeline.yml:

    apiVersion: v1
    kind: BuildConfig
    metadata:
      name: mavenapp-pipeline
    spec:
      strategy:
        jenkinsPipelineStrategy:
          jenkinsfile: |-
            pipeline {
              agent { label "maven" }
              stages {
                stage("Clone Source") {
                  steps {
                    checkout([$class: 'GitSCM',
                                branches: [[name: '*/master']],
                                extensions: [
                                  [$class: 'RelativeTargetDirectory', relativeTargetDir: 'mavenapp']
                                ],
                                userRemoteConfigs: [[url: 'https://github.com/openshift/openshift-jee-sample.git']]
                            ])
                  }
                }
                stage("Build WAR") {
                  steps {
                    dir('mavenapp') {
                      sh 'mvn clean package -Popenshift'
                    }
                  }
                }
                stage("Build Image") {
                  steps {
                    dir('mavenapp/target') {
                      sh 'oc start-build mavenapp --from-dir . --follow'
                    }
                  }
                }
              }
            }
        type: JenkinsPipeline
      triggers: []
  5. Create the pipeline build. If Jenkins is not deployed to your project, creating the BuildConfig with the pipeline will result in Jenkins getting deployed. It may take a couple of minutes before Jenkins is ready to build your pipeline. You can check the status of the Jenkins rollout by invoking, oc rollout status dc/jenkins:

    $ oc create -f ./mavenapp-pipeline.yml
  6. Once Jenkins is ready, start the pipeline defined previously:

    $ oc start-build mavenapp-pipeline
  7. When the pipeline has finished building, deploy the new application using new-app and expose its route:

    $ oc new-app mavenapp
    $ oc expose svc/mavenapp
  8. Using your browser, navigate to the route for the application:

    $ oc get route mavenapp