Deployment hooks ================ Deployment hooks enable services to program and gate their deployments. This is often used for testing but can also be used for things like deploying static assets to a CDN or building other types of automation into a deployment. Hooks are Dockerfiles meaning hook authors can choose any language or runtime. Hooks can be referenced in 2 ways: 1. By specifying the `dockerfile` for the hook 1. By specifying a git `repo` to be cloned, a `ref` to be checked out, and `dockerfile` to be built and run. These are called plugins. All hooks are optional and will only be called if they exist. If they exist they must exit with a code of 0 to continue the deployment. Any non-zero exit code is considered a failure and will halt whatever command was called that caused the hook to be called. Exceptions to the rule can be configured via [run conditions](#run-conditions). There are currently 9 hooks porter defines. 8 of them are used during the 4 build phases (pre and post), the other is used to customize EC2 initialization. The `pre` and `post` hooks are tied to the `porter build ...` command names, not the underlying mechanisms of provisioning, promoting, etc. The hooks --------- - [pre-pack](hooks/pre-pack.md) - [post-pack](hooks/post-pack.md) - [pre-provision](hooks/pre-provision.md) - [post-provision](hooks/post-provision.md) - [pre-promote](hooks/pre-promote.md) - [post-promote](hooks/post-promote.md) - [pre-prune](hooks/pre-prune.md) - [post-prune](hooks/post-prune.md) - [ec2-bootstrap](hooks/ec2-bootstrap.md) User-defined hooks ------------------ A hook named other than one of above is a user defined hook. The internal structure is the same as any other hook. A user-defined hook is never automatically called by porter. It can only be called by the `porter build hook` command. **Example** ```yaml hooks: alert_the_operator: - dockerfile: path/to/Dockerfile repo: https://github.com/person/repo.git ref: v1.0.0 ``` This hook might be run by a build system as such: ``` porter build hook -name alert_the_operator -e Stage ``` Execution order --------------- Multiple of each hook can be run. They are run in the order defined unless [configured to run concurrently](config-reference.md#concurrent). Hook environment ---------------- Each hook's Docker context is the directory containing the hook. The repo's root is volume mapped to `/repo_root` so hooks can access and mutate the contents of a repo. Environment variables are injected as they are available. See the [config reference](config-reference.md#hook-dockerfile) for more. ### Standard environment variables These are available to all hooks and provided by porter. ``` PORTER_SERVICE_NAME PORTER_SERVICE_VERSION (git sha) DOCKER_ENV_FILE HAPROXY_STATS_USERNAME HAPROXY_STATS_PASSWORD HAPROXY_STATS_URL ``` ### Custom environment variables You can whitelist what environment each hook receives with the same semantics as [Docker Compose](https://docs.docker.com/compose/compose-file/#/environment) This is a pre_pack hook with `FOO` set to `bar`, and `BAZ` set to the value of `BAZ` when porter is run. ```yaml hooks: pre_pack: - dockerfile: path/to/Dockerfile environment: FOO: bar BAZ: ``` Plugins ------- Single hooks are hardly sufficient for non-trivial projects. Porter enables hooks to be developed independently and referenced by porter projects in `.porter/config`. Plugins are porter hooks that live in a separate repo. Plugins can run as part of more than one hook. By default porter looks for a `dockerfile` value in the hook config. If this isn't found it defaults to `Dockerfile`. Here is an example config for a made up plugin called porter-contrib-foo that runs for both `pre_pack` and `post_pack`, and a porter-contrib-bar that contains a single Dockerfile at its root (to illustrate the behavior). ```yaml hooks: pre_pack: - repo: git@github.com:adobe-platform/porter-contrib-foo.git ref: v1.0.0 dockerfile: pre-pack post_pack: - repo: git@github.com:adobe-platform/porter-contrib-foo.git ref: v1.0.0 dockerfile: post-pack - repo: git@github.com:adobe-platform/porter-contrib-bar.git ref: v1.0.0 # This is the default if undefined # dockerfile: Dockerfile ``` `pre-pack` and `post-pack` are Dockerfiles found in the root directory of the porter-contrib-foo repo. There's no restriction on the name or placement of these Dockerfiles (i.e. `pre-pack` could also have been `some_dir/Dockerfile`) meaning a single repo could contain many hooks. pre hook vs. post hook ---------------------- When to use a pre hook vs a post hook is determined by what build command you want the hook to operate as part of. For example, `post-provision` and `pre-promote` aren't very different. The state of the deployment hasn't changed and all of the same variables are available. The only difference is that `post-provision` is called during `porter build provision`, while `pre-promote` is called during `porter build promote`. That difference is probably only meaningful if these commands are run on different boxes, with different environment variables, or are otherwise logically separate from the caller's perspective. The recommendation is to use pre hooks and then add or change to post hooks as hooks are better understood or requirements dictate. Run conditions -------------- For post-pack, post-provision, and post-promote, and post-prune you can set run conditions so that these hooks may run regardless of failure in the command they run after. The implicit run condition is `run_condition: pass` which means the hook will only be run only if the build step passed. Additional options are `run_condition: fail` and `run_condition: always` which be run only on failure, and always, respectively. Failure is defined as the underlying command failing meaning if a `pre-pack` hooks fails, the packaging never takes place and `post-pack` is not called even with a `run_condition: fail` or `run_condition: always`. If `pre-pack` succeeds or is undefined and packaging fails, then all `post-pack` hooks with `run_condition: fail` or `run_condition: always` are run. The intent of `run_condition` is to allow scripts that may have altered some state in a `pre-*` hook to be able to clean up if something goes wrong.