diff --git a/docs/tests/use-cases/go-docker-swarm/build-action.cue.fragment b/docs/tests/use-cases/go-docker-swarm/build-action.cue.fragment new file mode 100644 index 00000000..411e33eb --- /dev/null +++ b/docs/tests/use-cases/go-docker-swarm/build-action.cue.fragment @@ -0,0 +1,20 @@ +build: { + luaDocs: docker.#Dockerfile & { + source: client.filesystem."./lua-docs".read.contents + } + + _addGithubSHA: core.#WriteFile & { + input: luaDocs.output.rootfs + path: "/www/github_sha.yml" + contents: #""" + keywords: ["particubes", "game", "mobile", "scripting", "cube", "voxel", "world", "docs"] + title: "Github SHA" + blocks: + - text: "\#(client.env.GITHUB_SHA)" + """# + } + image: docker.#Image & { + rootfs: _addGithubSHA.output + config: luaDocs.output.config + } +} diff --git a/docs/tests/use-cases/go-docker-swarm/client-api.cue.fragment b/docs/tests/use-cases/go-docker-swarm/client-api.cue.fragment new file mode 100644 index 00000000..9151c78a --- /dev/null +++ b/docs/tests/use-cases/go-docker-swarm/client-api.cue.fragment @@ -0,0 +1,20 @@ +package docs + +import ( + "dagger.io/dagger" +) + +dagger.#Plan & { + client: { + // Locally, manual source of the .env or install https://direnv.net + env: { + GITHUB_SHA: string + SSH_PRIVATE_KEY_DOCKER_SWARM: dagger.#Secret + } + filesystem: { + "./": read: contents: dagger.#FS + "./merge.output": write: contents: actions.build.image.rootfs // Creates a build artifact for debug + } + network: "unix:///var/run/docker.sock": connect: dagger.#Socket // Docker daemon socket + } +} diff --git a/docs/tests/use-cases/go-docker-swarm/full/particubes.docs.cue b/docs/tests/use-cases/go-docker-swarm/full/particubes.docs.cue new file mode 100644 index 00000000..36c5502b --- /dev/null +++ b/docs/tests/use-cases/go-docker-swarm/full/particubes.docs.cue @@ -0,0 +1,158 @@ +package docs + +import ( + "dagger.io/dagger" + "dagger.io/dagger/core" + + "universe.dagger.io/alpine" + "universe.dagger.io/bash" + "universe.dagger.io/docker" + "universe.dagger.io/docker/cli" +) + +dagger.#Plan & { + client: { + // Locally, manual source of the .env or install https://direnv.net + env: { + GITHUB_SHA: string + SSH_PRIVATE_KEY_DOCKER_SWARM: dagger.#Secret + } + filesystem: { + "./": read: contents: dagger.#FS + "./merge.output": write: contents: actions.build.image.rootfs // Creates a build artifact for debug + } + network: "unix:///var/run/docker.sock": connect: dagger.#Socket // Docker daemon socket + } + + actions: { + params: image: { + ref: "registry.particubes.com/lua-docs" + tag: "latest" + localTag: "test-particubes" // name of the image when being run locally + } + + _dockerCLI: alpine.#Build & { + packages: { + bash: {} + curl: {} + "docker-cli": {} + "openssh-client": {} + } + } + + #_verifyGithubSHA: bash.#Run & { + input: _dockerCLI.output + env: GITHUB_SHA: client.env.GITHUB_SHA + always: true + script: contents: #""" + TRIMMED_URL="$(echo $URL | cut -d '/' -f 1)" + curl --verbose --fail --connect-timeout 5 --location "$URL" >"$TRIMMED_URL.curl.out" 2>&1 + + if ! grep "$GITHUB_SHA" "$TRIMMED_URL.curl.out" + then + echo "$GITHUB_SHA not present in the $TRIMMED_URL response:" + cat "$TRIMMED_URL.curl.out" + exit 1 + fi + """# + } + + build: { + luaDocs: docker.#Dockerfile & { + source: client.filesystem."./lua-docs".read.contents + } + + _addGithubSHA: core.#WriteFile & { + input: luaDocs.output.rootfs + path: "/www/github_sha.yml" + contents: #""" + keywords: ["particubes", "game", "mobile", "scripting", "cube", "voxel", "world", "docs"] + title: "Github SHA" + blocks: + - text: "\#(client.env.GITHUB_SHA)" + """# + } + image: docker.#Image & { + rootfs: _addGithubSHA.output + config: luaDocs.output.config + } + } + + clean: cli.#Run & { + host: client.network."unix:///var/run/docker.sock".connect + always: true + env: IMAGE_NAME: params.image.localTag + command: { + name: "sh" + flags: "-c": #""" + docker rm --force "$IMAGE_NAME" + """# + } + } + + test: { + preLoad: clean + + load: cli.#Load & { + image: build.image + host: client.network."unix:///var/run/docker.sock".connect + tag: params.image.localTag + env: DEP: "\(preLoad.success)" // DEP created wth preLoad + } + + run: cli.#Run & { + host: client.network."unix:///var/run/docker.sock".connect + always: true + env: { + IMAGE_NAME: params.image.localTag + PORTS: "80:80" + DEP: "\(load.success)" // DEP created wth load + } + command: { + name: "sh" + flags: "-c": #""" + docker run -d --rm --name "$IMAGE_NAME" -p "$PORTS" "$IMAGE_NAME" + """# + } + } + + verify: #_verifyGithubSHA & { + env: { + URL: "localhost/github_sha" + DEP: "\(run.success)" // DEP created wth run + } + } + + postVerify: clean & { + env: DEP: "\(verify.success)" // DEP created wth verify + } + } + + deploy: { + publish: docker.#Push & { + dest: "\(params.image.ref):\(params.image.tag)" + image: build.image + } + + update: cli.#Run & { + host: "ssh://ubuntu@3.139.83.217" + always: true + ssh: key: client.env.SSH_PRIVATE_KEY_DOCKER_SWARM + env: DEP: "\(publish.result)" // DEP created wth publish + command: { + name: "sh" + flags: "-c": #""" + docker service update --image registry.particubes.com/lua-docs:latest lua-docs + """# + } + } + + verify: #_verifyGithubSHA & { + env: { + URL: "https://docs.particubes.com/github_sha" + DEP: "\(update.success)" // DEP created wth run + } + } + } + } +} diff --git a/docs/use-cases/1211-go-docker-swarm.md b/docs/use-cases/1211-go-docker-swarm.md index 116ccf18..d2ad2629 100644 --- a/docs/use-cases/1211-go-docker-swarm.md +++ b/docs/use-cases/1211-go-docker-swarm.md @@ -13,116 +13,106 @@ They write primarily Go & Lua, push to GitHub and use GitHub Actions for automat The production setup is a multi-node Docker Swarm cluster running on AWS. The Particubes team chose Dagger for continuous deployment because it was the easiest way of integrating GitHub with Docker Swarm. -Every commit to the main branch goes straight to [docs.particubes.com](https://docs.particubes.com) via a Dagger pipeline that runs in GitHub Actions. -`universe.dagger.io/docker` made building this pipeline trivial: +Every commit to the main branch goes straight to [docs.particubes.com](https://docs.particubes.com) via a Dagger pipeline that runs in GitHub Actions. Let us see how the Particubes Dagger plan fits together. -:::danger -TODO: this config is meta Europa, meaning that it was not tested. Next steps: +### Actions API -- implement it in GitHub Actions and ensure that it all works as expected -- update this meta config to the final version that we know works -::: +This is a high level overview of all actions in the Particubes docs Dagger plan: -```cue -package particubes +![particubes flat plan](/img/use-cases/particubes-actions.png) -import ( - "dagger.io/dagger" - "dagger.io/dagger/core" - "universe.dagger.io/docker" -) +We can see all available actions in a Plan by running the following command: -dagger.#Plan & { - inputs: { - directories: src: path: "./lua-docs" - secrets: docs: command: { - name: "sops" - args: ["-d", "../../lua-docs/sops_secrets.yaml"] - } - params: { - image: ref: docker.#Ref | *"registry.particubes.com/lua-docs:latest" - } - } +```bash +$ dagger do +Execute a dagger action. - actions: { - docs: { - // TODO: write GITHUB_SHA into a static /github_sha.txt - build: docker.#Dockerfile & { - source: inputs.directories.src.contents - } - - test: { - // TODO: - // - run container - // - check http response code - // - verify /github_sha.txt value matches GITHUB_SHA - // - stop container - } - - push: docker.#Push & { - dest: inputs.params.image.ref - image: build.output - } - - docsSecrets: core.#DecodeSecret & { - input: inputs.secrets.docs.contents - format: "yaml" - } - deploy: { - // TODO: - // - run this command in the remote Docker Swarm - // - secrests are ready in docsSecrets, e.g. docsSecrets.output.swarmKey.contents - } - - verifyDeploy: { - // TODO: - // - check http response code - // - verify /github_sha.txt value matches GITHUB_SHA - } - } - } -} +Available Actions: + build Create a container image + clean Remove a container image + test Locally test a container image + deploy Deploy a container image ``` -This is the GitHub Actions workflow config that invokes `dagger`, which in turn runs the above pipeline: +### Client API + +Dagger actions usually need to interact with the host environment where the Dagger client runs. The Particubes' plan uses environment variables and the filesystem. + +This is an overview of all client interactions for this plan: + +![Client API](/img/use-cases/client-api.png) + +This is what the above looks like in the Dagger plan config: + +```cue file=../tests/use-cases/go-docker-swarm/client-api.cue.fragment +``` + +### The *build* Action + +This is a more in-depth overview of the *build* action and how it interacts with the client in the Particubes docs Dagger plan: + +![build action](/img/use-cases/build-action.png) + +This is what the above looks like in the Dagger plan config: + +```cue file=../tests/use-cases/go-docker-swarm/build-action.cue.fragment +``` + +### Github Action integration + +This is the GitHub Actions workflow config that invokes `dagger`, which in turn runs the full plan: ```yaml name: Dagger/docs.particubes.com on: push: - branches: [ master ] + branches: [master] jobs: deploy: runs-on: ubuntu-latest + env: + GITHUB_SHA: ${{ github.sha }} + SSH_PRIVATE_KEY_DOCKER_SWARM: ${{ secrets.SSH_PRIVATE_KEY_DOCKER_SWARM }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - name: Install Dagger + uses: dagger/dagger-action@v2 with: - lfs: true - // TODO: install sops - - name: Dagger - uses: dagger/dagger-action@v1 - with: - age-key: ${{ secrets.DAGGER_AGE_KEY }} - args: up + install-only: true + + - name: Dagger poject update + run: dagger project update + + - name: Dagger do test + run: dagger do test --log-format plain + + - name: Dagger do deploy + run: dagger do deploy --log-format plain ``` Since this is a Dagger pipeline, anyone on the team can run it locally with a single command: ```console -dagger up +dagger do ``` This is the first step that enabled the Particubes team to have the same CI/CD experience everywhere. -We don't know what comes next for particubes.com, but we would like find out. Some ideas: +### Full Particubes docs Dagger plan -- deploy particubes.com with Dagger -- manage the Docker Swarm cluster with Dagger -- contribute `universe.dagger.io/particubes` package +This is the entire plan running on Particubes' CI: + +```cue file=../tests/use-cases/go-docker-swarm/full/particubes.docs.cue +``` + +### What comes next ? + +Particubes' team suggested that we create a `dev` action with *hot reload*, that way Dagger would even asbtract away the ramp-up experience when developing the doc :::tip -The latest version of this pipeline can be found at [github.com/voxowl/particubes/lua-docs/docs.cue](https://github.com/voxowl/particubes/blob/b698777465c02462296de37087dd3c341c29df92/lua-docs/docs.cue) +The latest version of this pipeline can be found at [github.com/voxowl/particubes/pull/144](https://github.com/voxowl/particubes/blob/2af173596729929cfb7a7a1f78f1ec0d8b685e5e/lua-docs/docs.cue) ::: diff --git a/website/static/img/use-cases/build-action.png b/website/static/img/use-cases/build-action.png new file mode 100644 index 00000000..39bd35b7 Binary files /dev/null and b/website/static/img/use-cases/build-action.png differ diff --git a/website/static/img/use-cases/client-api.png b/website/static/img/use-cases/client-api.png new file mode 100644 index 00000000..c5f213f1 Binary files /dev/null and b/website/static/img/use-cases/client-api.png differ diff --git a/website/static/img/use-cases/particubes-actions.png b/website/static/img/use-cases/particubes-actions.png new file mode 100644 index 00000000..7257b698 Binary files /dev/null and b/website/static/img/use-cases/particubes-actions.png differ