docs-v2/design_proposals/lifecycle-hooks.md
Skaffold lifecycle hooks
authors: Gaurav Ghosh (gaghosh@)
status: approved
approval date: 07-14-2020
proposed on: 07-06-2020
approvers:
Supporting callbacks for lifecycle hooks is a heavily requested feature in the Skaffold community with some of the top voted issues being:
In the past there have been discussions that prioritized implementing targeted features over a generic script execution that, while making the platform more flexible, renders it somewhat blind to the specific action that the user is trying to accomplish. However as evidenced by user feedback there are scenarios both in local development and CICD that can be readily solved by opening up hooks into Skaffold.
There are three broad scenarios:
hooks:
before:
- command: [ “sleep”, “5” ]
os: [ “linux”, “darwin” ]
- command: [ “timeout”, “5” ]
os: [ “windows” ]
- containerCommand: [ “echo”, “foo” ]
# containerName is optional for artifact scoped hooks like in build and sync
containerName: foo
# podPrefix is optional for artifact scoped hooks like in build and sync
podPrefix: bar
after:
- command: [ “echo”, “foo” ]
This can be nested under build, deploy and sync stages (described in examples below):
Possible values for os field are all golang recognised platforms. Missing value implies all.
If the command points to files, those don't get added to the change monitoring for dev loop. Inline commands by virtue of being part of the skaffold.yaml file would be subject to dev loop reload on change.
For multiple microservices in a repo sharing multiple common libraries it may not be ideal for the repo root to be the Dockerfile context. We use pre-build hook to copy over the necessary files prior to the build
apiVersion: skaffold/vX
kind: Config
metadata:
name: microservices
build:
artifacts:
- image: leeroy-web
context: ./leeroy-web/
hooks:
before:
- command: [ “cp”, “./shared/package1.py”, “./leeroy-web/pkg/” ]
- command: [ “./setup.sh” ]
- image: leeroy-app
context: ./leeroy-app/
deploy: ...
We might want to run tests to validate artifacts or deployment. If it exits with non-zero status code then depending on the run type it'll stop the execution
apiVersion: skaffold/vX
kind: Config
metadata:
name: microservices
build:
artifacts:
- image: leeroy-web
context: ./leeroy-web/
- image: leeroy-app
context: ./leeroy-app/
deploy:
kubectl:
manifests:
- ./leeroy-web/kubernetes/*
- ./leeroy-app/kubernetes/*
hooks:
before:
- command: [ “make”, “pre-deployment-tests” ]
after:
- command: [ “make”, “post-deployment-tests” ]
We want to run say javascript minification post every file sync.
apiVersion: skaffold/vX
kind: Config
metadata:
name: node-example
build:
artifacts:
- image: node-example
context: ./node/
sync:
manual:
- src: ‘src/**/*.js’
- dest: ‘./raw/’
hooks:
after:
- container-command: [ “./minify-script.sh’, “./raw”, “./min/” ]
deploy: ...
It might be helpful to be able to pass variables between multiple hooks callback functions. This can be achieved by setting a pattern to dump key-value pairs into standard output that are then parsed, stored and supplied in subsequent hooks command execution.
For example, running:
echo "::set-env name=FOO::BAR"
will set the environment variable FOO to the value BAR
hooks:
after:
- command: [ “/bin/echo”, “::set-env”, “name=FOO::BAR”]
- command: [ “/bin/echo”, “The value of FOO is ${FOO}”]
This is only scoped to propagate values across lifecycle hooks commands only but across several dev loops.
Note: There is a related issue #4106 requesting a feature of being able to read environment variables from files and substituting them in the skaffold template. If this feature is available then users can get away with modifying the environment variable file to propagate environment variables across stages without needing this implementation.
In addition to regular unit tests we'll add integration tests against new example projects that showcase using hooks for the scenarios:
There are currently no metrics collected within skaffold. However events are reported on the event API server. We'll add event notifications for the following phases:
<table> <thead> <tr> <th><strong>Event</strong></th> <th><strong>Params</strong></th> </tr> </thead> <tbody> <tr> <td>HostCallbackEvent</td> <td>Type(pre/post - build/sync/deploy)Status(InProgress, Completed, Failed)
error message</td>
</tr> <tr> <td>ContainerCallbackEvent</td> <td>Type(post - sync/deploy)Status(InProgress, Completed, Failed)
error message</td>
</tr> </tbody> </table>Additionally we'll append the count of each type of hooks defined in skaffold.yaml to the corresponding sections of the MetaEvent for Build, Sync and Deploy
Schema Alternative 1 (too verbose, no explicit container-command)
hooks:
pre:
- exec:
command: [ “sleep”, “5” ]
os: [ “linux”, “darwin” ]
- exec:
command: [ “timeout”, “5” ]
os: [ “windows” ]
post:
- exec:
command: [ “echo”, “foo” ]
Schema Alternative 2 (less verbose, dash-casing over camelCasing)
pre-hooks:
- command: [ “sleep”, “5” ]
os: [ “linux”, “darwin” ]
- command: [ “timeout”, “5” ]
os: [ “windows” ]
- container-command: [ “echo”, “foo” ]
# container-name is optional for artifact scoped hooks like in build and sync
container-name: foo
# pod-prefix is optional for artifact scoped hooks like in build and sync
pod-prefix: bar
post-hooks:
- command: [ “echo”, “foo” ]