This topic provides a step-by-step tutorial for setting up Dispatch and getting your first successful cloud native CI build on your GitHub repository. It will leverage the YAML front end language.
Prerequisites
- Some basic knowledge of git, bash, and Docker.
- A GitHub account.
- Owner permissions for a project hosted on GitHub or access to create one.
- Deploy access to a namespace in Kubernetes cluster.
- Dispatch CLI installed in the environment.
Setup a git repository
Initialize a git repository
Begin by creating a new repository on GitHub and clone it locally. You may skip this if you intend to use an existing repository on GitHub. Throughout the rest of this tutorial, this repository is named helloworld.
For the purposes of this tutorial, focus on creating a CI check that uses a Docker image to test the source code. This tutorial assumes the Docker image is testing the golang source code present in the repository but this can be replaced with any other Docker image trivially.
Go to the directory where you’ve cloned the repository
cd helloworld
Add some golang source and test files
cat <<EOF | > main.go
package main
import "fmt"
func main() {
fmt.Printf("Hello %v!", World())
}
func World() string {
return "World"
}
EOF
cat <<EOF | > main_test.go
package main
import (
"testing"
)
func Test_World(t *testing.T) {
actual := World()
if actual != "World" {
t.Fail()
}
}
EOF
If you have go in the environment, you can verify that the above code works using go test -v ./....
For the sake of this tutorial, we can add a simple Dockerfile that builds from the base golang image to run the tests. Create this dockerfile:
cat <<EOF | > Dockerfile
FROM golang:1.14.0
ADD main.go /test/main.go
ADD main_test.go /test/main_test.go
EOF
In the later sections, we will setup CI infrastructure to run the tests on every pull request. As a side note, real life test scripts are much more complicated (obviously!) such as launching a kind cluster and running some tests on the cluster. The focus of this tutorial is to demonstrate the ability to run a simple CI build and this should be extendable to more complicated tests.
Install Dispatch
Refer to this guide for install options.
By default, Dispatch installs to dispatch namespace and can be overridden during install time. This tutorial assumes Dispatch is installed to dispatch namespace.
Rest of the tutorial assumes you are working with default namespace for pipelines. Add a --namespace flag to commands as applicable if you are working with a different namespace.
Setup credentials & Service accounts
Refer to this guide on setting up credentials.
For the purposes of this tutorial, you need at least Github & Docker credentials. At the bare minimum, you should have executed (with appropriate names):
-
Create a service account named team-1
dispatch serviceaccount create team-1 -
Create a Docker credential
dispatch login docker --service-account team-1 -
Create a github credential
dispatch login github --service-account team-1 --user $YOURGITHUBUSERNAME --token $YOURGITHUBTOKEN
-
Create a git SSH credential only if you want to be able to build locally
dispatch login git --service-account team-1 --private-key-path $SSH_KEY_PATH
Setup repository in Dispatch
Refer to this guide for repo setup.
At the least, you should execute the following:
dispatch ci repository create --service-account=team-1
In the next section, we are going to define the build specification in a file named Dispatchfile
Adding a Dispatchfile to git repository
In this tutorial, we are going to use starlark and create a file named Dispatchfile which holds our build specification. This is a step-by-step walk-through of creating our Dispatchfile:
-
Declare the DSL (Domain Specific Language) syntax for our
Dispatchfileusing shebang:#!mesosphere/dispatch-yaml:v0.3This specifies to use version
0.3of YAML DSL parser. -
Declare the git resource:
resource: helloworld-git: param: revision: $(context.git.commit) url: $(context.git.url) type: git -
Declare a docker image resource to which to push the new image:
resource: docker-image: param: digest: $(inputs.resources.docker-image.digest) url: docker.io/$YOURDOCKERUSERNAME/helloworld:$(context.build.name) type: image -
Declare a task to build and push the Docker image using kaniko:
task: build: inputs: - helloworld-git outputs: - docker-image steps: - args: - --destination=$(outputs.resources.docker-image.url) - --context=/workspace/helloworld-git - --oci-layout-path=/workspace/output/docker-image - --dockerfile=/workspace/helloworld-git/Dockerfile image: chhsiao/kaniko-executor name: build-and-push resources: {} -
Declare a task using above Docker image to run tests:
task: unit-test-simple: inputs: - docker-image - helloworld-git steps: - command: - go - test - ./... image: $YOURDOCKERUSERNAME/helloworld:$(context.build.name) name: unit-test-simple resources: {} workingDir: /workspace/helloworld-git/We listed the “helloworld-git” git resource as an input to the “unit-test-simple” task. That means that our git repository contents will be available at
/workspace/helloworld-git/, so we set theworkingDirto that directory.
-
Define an Action to run the task on every pull request:
actions: - "on": pull_request: {} tasks: - unit-test-simple - "on": pull_request: chatops: - test tasks: - unit-test-simpleThe first action triggers the
unit-test-simpletask upon creating/updating a pull request. The second action triggersunit-test-simplewhenever a comment/testis made on the pull request.
Hence, The entire Dispatchfile becomes:
cat <<EOF | > Dispatchfile
#!mesosphere/dispatch-yaml:v0.3
resource:
docker-image:
param:
digest: $(inputs.resources.docker-image.digest)
url: docker.io/$YOURDOCKERUSERNAME/helloworld:$(context.build.name)
type: image
helloworld-git:
param:
revision: $(context.git.commit)
url: $(context.git.url)
type: git
task:
build:
inputs:
- helloworld-git
outputs:
- docker-image
steps:
- args:
- --destination=$(outputs.resources.docker-image.url)
- --context=/workspace/helloworld-git
- --oci-layout-path=/workspace/output/docker-image
- --dockerfile=/workspace/helloworld-git/Dockerfile
image: chhsiao/kaniko-executor
name: build-and-push
resources: {}
unit-test-simple:
inputs:
- docker-image
- helloworld-git
steps:
- command:
- go
- test
- ./...
image: $YOURDOCKERUSERNAME/helloworld:$(context.build.name)
name: unit-test-simple
resources: {}
workingDir: /workspace/helloworld-git/
actions:
- "on":
pull_request: {}
tasks:
- unit-test-simple
- "on":
pull_request:
chatops:
- test
tasks:
- unit-test-simple
EOF
You can use the Dispatch CLI to validate above Dispatchfile:
dispatch ci render --file=Dispatchfile
which would result in an output similar to:
...
#!yaml
# vi:syntax=yaml
actions:
- "on":
pull_request: {}
tasks:
- unit-test-simple
- "on":
pull_request:
chatops:
- test
tasks:
- unit-test-simple
resource:
$YOURDOCKERUSERNAME-helloworld:
param:
digest: $(inputs.resources.$YOURDOCKERUSERNAME-helloworld.digest)
url: $YOURDOCKERUSERNAME/helloworld:$(context.build.name)
type: image
helloworld-git:
param:
revision: $(context.git.commit)
url: $(context.git.url)
type: git
task:
$YOURDOCKERUSERNAME-helloworld:
inputs:
- helloworld-git
outputs:
- $YOURDOCKERUSERNAME-helloworld
steps:
- args:
- --destination=$YOURDOCKERUSERNAME/helloworld:$(context.build.name)
- --context=/workspace/helloworld-git/
- --oci-layout-path=/workspace/output/$YOURDOCKERUSERNAME-helloworld
- --dockerfile=/workspace/helloworld-git/Dockerfile
image: gcr.io/kaniko-project/executor
name: docker-build
resources: {}
unit-test-simple:
inputs:
- $YOURDOCKERUSERNAME-helloworld
steps:
- command:
- go
- test
- ./...
image: $YOURDOCKERUSERNAME/helloworld:$(context.build.name)
name: unit-test-docker
resources: {}
workingDir: /test
See full reference of a Dispatchfile.
After setting up Dispatch, adding relevant credentials, and creating helloworld repository, we can move on to running our CI.
Continuous Integration in Action
After creating your Dispatchfile, you can push it to a branch of your choice and create a pull request against default branch (or any branch).
When you executed the dispatch ci create repository command in earlier sections, Dispatch repository controller created a webhook in your GitHub repository. This webhook enables Dispatch to receive events (such as pull request events) from GitHub. When you create a Pull Request, a PullRequest event is posted to Dispatch and this in turn triggers a pipeline to run unit-test-simple task as declared in your Dispatchfile. If you make a comment on the Pull Request that starts with /test then this would have a similar effect (useful in cases where you want to rerun a flaky CI test). Make such a comment and the build status should be reflected shortly on your Pull Request as soon as the build is scheduled. See the troubleshooting guide if you are having problems.
You can look at logs of various dispatch components as well as pipelines.
Dispatch Documentation