diff --git a/README.md b/README.md index 0465832..209a16f 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,12 @@ This package contains all necessary code to run a Cloud Foundry (CF) app on Kubernetes. It is used by Eirini's [`k8s`](https://github.com/cloudfoundry-incubator/eirini/tree/master/k8s) package to run the app as Kubernetes `StatefulSet`. -If no startup command was provided for a CF app, this package parses the `startup_command` from `staging_info.yml` (inside the CF app). It simply wraps the launcher (added via the `buildpackapplifecycle` submodule), which provides the environment setup and launch command for the the app. +If no startup command was provided for a CF app, this package parses the `startup_command` from `staging_info.yml` (inside the CF app). It simply wraps the launcher (added via the `buildpackapplifecycle` submodule), which provides the environment setup and launch command for the the app. The `launchcmd` is then provided together with [`cflinuxfs2`](https://github.com/cloudfoundry/cflinuxfs2) and [`launcher`](https://github.com/cloudfoundry/buildpackapplifecycle/tree/master/launcher) as `eirinifs.tar`, forming the root filesystem of the CF app. The [bits-service](https://github.com/cloudfoundry-incubator/bits-service) consumes the GitHub release of `eirinifs` when building the OCI image for the CF app (running in Kubernetes). Building and updating the release is handled in the [CI pipeline](https://flintstone.ci.cf-app.com/teams/eirini/pipelines/eirinifs). Use [`ci/set-pipeline`](ci/set-pipeline) to configure it. +# Test + +Run `ginkgo` in the `launchcmd` directory. diff --git a/ci/build-eirinifs.sh b/ci/build-eirinifs/task.sh similarity index 100% rename from ci/build-eirinifs.sh rename to ci/build-eirinifs/task.sh diff --git a/ci/build-eirinifs.yml b/ci/build-eirinifs/task.yml similarity index 84% rename from ci/build-eirinifs.yml rename to ci/build-eirinifs/task.yml index 1b3e8ea..57cf1bf 100644 --- a/ci/build-eirinifs.yml +++ b/ci/build-eirinifs/task.yml @@ -14,5 +14,5 @@ outputs: - name: go/src/github.com/cloudfoundry-incubator/eirinifs/image/ run: - path: eirinifs/ci/build-eirinifs.sh + path: eirinifs/ci/build-eirinifs/task.sh diff --git a/ci/pipeline.yml b/ci/pipeline.yml index c1dfc46..57841b4 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -1,9 +1,20 @@ jobs: +- name: run-tests + plan: + - get: eirinifs + trigger: true + - get: launcher + trigger: true + - task: run-integration-tests + file: eirinifs/ci/run-integration-tests/task.yml - name: update-eirinifs plan: - get: eirinifs + trigger: true + passed: [ run-tests ] - get: launcher trigger: true + passed: [ run-tests ] - get: cflinuxfs2-image trigger: true - get: eirinifs-version @@ -14,7 +25,7 @@ jobs: file: eirinifs-version/version - task: build eirinifs privileged: true - file: eirinifs/ci/build-eirinifs.yml + file: eirinifs/ci/build-eirinifs/task.yml - put: eirinifs-release params: name: eirinifs-version/version diff --git a/ci/run-integration-tests/task.sh b/ci/run-integration-tests/task.sh new file mode 100755 index 0000000..0f671bb --- /dev/null +++ b/ci/run-integration-tests/task.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -euo pipefail + +export GOPATH="$PWD" +mkdir -p src/code.cloudfoundry.org +cp -r eirinifs src/code.cloudfoundry.org/ +cd src/code.cloudfoundry.org/eirinifs +ginkgo -v launchcmd diff --git a/ci/run-integration-tests/task.yml b/ci/run-integration-tests/task.yml new file mode 100644 index 0000000..d25f7e0 --- /dev/null +++ b/ci/run-integration-tests/task.yml @@ -0,0 +1,14 @@ +--- +platform: linux + +image_resource: + type: docker-image + source: + repository: eirini/ci + +inputs: +- name: eirinifs + +run: + path: eirinifs/ci/run-integration-tests/task.sh + diff --git a/launchcmd/launch_suite_test.go b/launchcmd/launch_suite_test.go new file mode 100644 index 0000000..1b39dff --- /dev/null +++ b/launchcmd/launch_suite_test.go @@ -0,0 +1,13 @@ +package main_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestLaunch(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Launch Suite") +} diff --git a/launchcmd/launch_test.go b/launchcmd/launch_test.go new file mode 100644 index 0000000..6e4da23 --- /dev/null +++ b/launchcmd/launch_test.go @@ -0,0 +1,133 @@ +package main_test + +import ( + "os/exec" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Launch", func() { + var ( + launchPath string + session *gexec.Session + err error + envs []string + mockLauncher string + ) + + Context("When launcher is provided", func() { + + BeforeEach(func() { + envs = []string{"START_COMMAND=dummy"} + launchPath, err = gexec.Build("code.cloudfoundry.org/eirinifs/launchcmd") + Expect(err).ToNot(HaveOccurred()) + }) + + JustBeforeEach(func() { + cmd := exec.Command(launchPath, mockLauncher) //#nosec + cmd.Env = envs + session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + }) + + Context("Launcher args", func() { + BeforeEach(func() { + mockLauncher = "mock-launchers/verify-args.sh" + envs = append(envs, "POD_NAME=my-super-pod-42") + }) + + Context("when START_COMMAND env var is provided", func() { + It("should exit with a zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).To(Equal(0)) + }) + }) + + Context("when START_COMMAND env var is NOT provided", func() { + BeforeEach(func() { + envs = []string{} + }) + + It("should exit with a non-zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).ToNot(Equal(0)) + }) + }) + }) + + Context("Environment variables", func() { + BeforeEach(func() { + mockLauncher = "mock-launchers/verify-env-vars.sh" + }) + + Context("when CF_INSTANCE_INDEX is provided", func() { + BeforeEach(func() { + envs = append(envs, "POD_NAME=my-super-pod-42") + }) + + It("should exit with a zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("should expose the INSTANCE_INDEX as environment name", func() { + Eventually(session.Out, 5*time.Second).Should(gbytes.Say("INSTANCE_INDEX:42")) + }) + }) + + Context("when INSTANCE_INDEX is NOT provided", func() { + It("should exit with a non-zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).ToNot(Equal(0)) + }) + }) + + Context("when CF_INSTANCE_INDEX is provided", func() { + BeforeEach(func() { + envs = append(envs, "POD_NAME=my-super-pod-11") + }) + + It("should exit with a zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("should expose the CF_INSTANCE_INDEX as environment name", func() { + Eventually(session.Out, 5*time.Second).Should(gbytes.Say("CF_INSTANCE_INDEX:11")) + }) + }) + + Context("when CF_INSTANCE_INDEX is NOT provided", func() { + It("should exit with a non-zero exit code", func() { + <-session.Exited + Expect(session.ExitCode()).ToNot(Equal(0)) + }) + }) + }) + }) + + Context("When no launcher is provided", func() { + BeforeEach(func() { + envs = []string{"START_COMMAND=run-it", "POD_NAME=my-super-pod-11"} + launchPath, err = gexec.Build("code.cloudfoundry.org/eirinifs/launchcmd") + Expect(err).ToNot(HaveOccurred()) + }) + + JustBeforeEach(func() { + cmd := exec.Command(launchPath) //#nosec + cmd.Env = envs + session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should fallback to the default command, which is not present on your machine", func() { + <-session.Exited + Expect(session.ExitCode()).ToNot(Equal(0)) + }) + }) + +}) diff --git a/launchcmd/mock-launchers/verify-args.sh b/launchcmd/mock-launchers/verify-args.sh new file mode 100755 index 0000000..97d1028 --- /dev/null +++ b/launchcmd/mock-launchers/verify-args.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +[ "$1" = /home/vcap/app ] || exit 1 +[ "$2" = dummy ] || exit 2 diff --git a/launchcmd/mock-launchers/verify-env-vars.sh b/launchcmd/mock-launchers/verify-env-vars.sh new file mode 100755 index 0000000..14a60c1 --- /dev/null +++ b/launchcmd/mock-launchers/verify-env-vars.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "INSTANCE_INDEX:$INSTANCE_INDEX" +echo "CF_INSTANCE_INDEX:$CF_INSTANCE_INDEX" \ No newline at end of file