|
| 1 | +# HOOKs |
| 2 | + |
| 3 | +A hook is a script in any language you prefer with an added events configuration code. Shell-operator will execute the script in a Kubernetes cluster on events you configured. |
| 4 | + |
| 5 | +> A corresponding runtime environment must be added into the image to execute a hook. |
| 6 | +
|
| 7 | +Every hook must have an argument `--config`. Getting `--config` as an argument, hook should return to `stdout` its [bindings configuration](#bindings) as a JSON structure. |
| 8 | + |
| 9 | +When one or more events the hook is bound are triggered, Shell-operator executes the hook without arguments but with the following environment variables set: |
| 10 | +- BINDING_CONTEXT_PATH — [execution context](#execution-context), contains a path to a JSON file with information about an event that was triggered. |
| 11 | +- WORKING_DIR — contains a path to a hooks directory. It can be helpful when you use shared libraries in your hooks. |
| 12 | + |
| 13 | +## How hooks run |
| 14 | + |
| 15 | +Shell-operator searches and runs hooks going through the following steps: |
| 16 | +- Recursively searches hooks in a working directory. A working directory is `"/hooks"` by default and can be specified wiht a WORKING_DIR environment variable or with a `--working-dir` argument (overrides WORKING_DIR): |
| 17 | + - directories starting from the dot symbol are excluded |
| 18 | + - directories and files are sorted alphabetically |
| 19 | + - every **executable** file not starting from dot symbol counts as a hook. |
| 20 | +- Executes hooks to get hooks configuration: |
| 21 | + - hooks are executed in the order found (see above) |
| 22 | + - each hook is executed in the hook directory in which it is located with the `--config` argument and the WORKING_DIR environment variable set |
| 23 | + - hook returns to `stdout` a JSON array of its [bindings configuration](#bindings). |
| 24 | +- Executes hooks depending on the schedule and Kubernetes events: |
| 25 | + - if there is more than one hook for a triggered event, hooks executed sequentially in the same order they have been found. |
| 26 | +- Export [metrics](METRICS.md) to Prometheus. |
| 27 | + |
| 28 | +Shell operator executes each hook one by one (without concurrency), and executes each hook until it finishes. On that note, you may have a question — "What happens if one of the hooks fails?". |
| 29 | + |
| 30 | +### What happens if hook fails? |
| 31 | + |
| 32 | +If hook fails (exit with exit status > 0) Shell-operator does the following (by default): |
| 33 | +- writes the corresponding message to stdout (use e.g. `kubectl logs` to see it) |
| 34 | +- increases the `shell_operator_hook_errors` counter |
| 35 | +- waits for 3 seconds |
| 36 | +- tries to execute the hook again... |
| 37 | + |
| 38 | +If you want Shell-operator to skip failed hook, you can use the `allowFailure: yes` option for `schedule` and `onKubernetesEvent` binding types. |
| 39 | + |
| 40 | +## Hook configuration |
| 41 | + |
| 42 | +Hook must return to stdout its binding configuration when the hook is executed with the `--config` argument. Binding configuration is the following JSON structure: |
| 43 | + |
| 44 | +``` |
| 45 | +{ |
| 46 | + "BINDING_TYPE": "BINDING_PARAMETERS", |
| 47 | + ... |
| 48 | + "BINDING_TYPE": "BINDING_PARAMETERS", |
| 49 | + "BINDING_TYPE": "BINDING_PARAMETERS" |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +Where: |
| 54 | +- BINDING_NAME is one of `onStartup`, `schedule`, `onKubernetesEvent` binding types. |
| 55 | +- BINDING_PARAMETERS is a value for binding or a JSON array with binding parameters (learn the [bindings](#bindings) section below) |
| 56 | + |
| 57 | +### Bindings |
| 58 | + |
| 59 | +There are three types of bindings: |
| 60 | +- onStartup |
| 61 | +- schedule |
| 62 | +- onKubernetesEvent |
| 63 | + |
| 64 | +#### onStartup |
| 65 | + |
| 66 | +This type of binding tells Shell-operator to execute hook once at Shell-operator startup. The `onStartup` binding requires only a single parameter — a hook execution order. |
| 67 | + |
| 68 | +Syntax: |
| 69 | +``` |
| 70 | +{ |
| 71 | + "onStartup": ORDER |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +#### schedule |
| 76 | + |
| 77 | +This type of binding tells Shell-operator to execute a hook on a specified schedule or schedules. You can bind one hook for any number of schedules. The `schedule` binding requires a JSON array of event descriptors. |
| 78 | + |
| 79 | +Syntax: |
| 80 | +``` |
| 81 | +{ |
| 82 | + "schedule": [ |
| 83 | + { |
| 84 | + "name": "EVENT_NAME", |
| 85 | + "crontab": CRON_EXPRESSION, |
| 86 | + "allowFailure": true|false, |
| 87 | + }, |
| 88 | + { |
| 89 | + "crontab": CRON_EXPRESSION, |
| 90 | + "allowFailure": true|false, |
| 91 | + }, |
| 92 | + ... |
| 93 | + ] |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +The `name` is an optional parameter. It specifies a binding name, which hook can get in the execution time from the [execution context](#execution-context) |
| 98 | + |
| 99 | +The `CRON_EXPRESSION` is a crontab syntax with 6 values separated by spaces, each of them can be: |
| 100 | +- a number |
| 101 | +- a set of numbers separated by commas |
| 102 | +- a range, — two numbers separated by a hyphen |
| 103 | +- an asterisk `*` or a slash `/` |
| 104 | +- [predefined values](https://godoc.org/github.com/robfig/cron#hdr-Predefined_schedules) |
| 105 | +- [intervals](https://godoc.org/github.com/robfig/cron#hdr-Intervals) |
| 106 | + |
| 107 | +Syntax: |
| 108 | +``` |
| 109 | +* * * * * * |
| 110 | +| | | | | | |
| 111 | +| | | | | +---- Day of the Week (range: 1-7, 1 standing for Monday) |
| 112 | +| | | | +------ Month of the Year (range: 1-12) |
| 113 | +| | | +-------- Day of the Month (range: 1-31) |
| 114 | +| | +---------- Hour (range: 0-23) |
| 115 | +| +------------ Minute (range: 0-59) |
| 116 | ++-------------- Second (range: 0-59) |
| 117 | +``` |
| 118 | + |
| 119 | +The `allowFailure` — is an optional field, false by default. When true, Shell-operator will not try to re-execute hook if it fails and will skip it. |
| 120 | + |
| 121 | +#### onKubernetesEvent |
| 122 | + |
| 123 | +This type of binding tells Shell-operator to execute hook on a specified event from a Kubernetes cluster. |
| 124 | + |
| 125 | +The `onKubernetesEvent` binding requires a JSON array of event descriptors. Each event descriptor assumes tracking object events of only one kind (see below). |
| 126 | + |
| 127 | +Syntax: |
| 128 | +``` |
| 129 | +{ |
| 130 | + "onKubernetesEvent": [ |
| 131 | + { |
| 132 | + "name": "EVENT_NAME", |
| 133 | + "kind": RESOURCE, |
| 134 | + "event": EVENTS_LIST, |
| 135 | + "selector": { |
| 136 | + "matchLabels": { |
| 137 | + "myLabel": "myLabelValue", |
| 138 | + "someKey": "someValue", |
| 139 | + ... |
| 140 | + }, |
| 141 | + "matchExpressions": [ |
| 142 | + { |
| 143 | + "key": "tier", |
| 144 | + "operation": "In", |
| 145 | + "values": ["cache"], |
| 146 | + }, |
| 147 | + ... |
| 148 | + ], |
| 149 | + }, |
| 150 | + "namespaceSelector": { |
| 151 | + "matchNames": ["somenamespace", "bush-production", "bush-stage"], |
| 152 | + "any": true|false, |
| 153 | + }, |
| 154 | + "jqFilter": ".metadata.labels", |
| 155 | + "allowFailure": true|false, |
| 156 | + "disableDebug": true|false, |
| 157 | + }, |
| 158 | + ... |
| 159 | + ] |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +Where: |
| 164 | +- `name` — is an optional parameter, specifies a binding name, which hook can get in the execution time from the [execution context](#execution-context). |
| 165 | +- `kind` — is a kind of a Kubernetes object, events of which is required to bind. Case-insensitive. Can be one of the following: |
| 166 | + - namespace |
| 167 | + - cronjob |
| 168 | + - daemonset |
| 169 | + - deployment |
| 170 | + - job |
| 171 | + - pod |
| 172 | + - replicaset |
| 173 | + - replicationcontroller |
| 174 | + - statefulset |
| 175 | + - endpoints |
| 176 | + - ingress |
| 177 | + - service |
| 178 | + - configmap |
| 179 | + - secret |
| 180 | + - persistentvolumeclaim |
| 181 | + - storageclass |
| 182 | + - node |
| 183 | + - serviceaccount |
| 184 | +- `event` — is an optional parameter, by default equals to `["add", "update", "delete"]`. Specifies an array of Kubernetes events to bind, can be any combination of: |
| 185 | + - `add` — an event of adding a Kubernetes resource of a specified `kind` |
| 186 | + - `update` — an event of updating a Kubernetes resource of a specified `kind` |
| 187 | + - `delete` — an event of deleting a Kubernetes resource of a specified `kind`. |
| 188 | +- `selector` — is an optional parameter, specifies the condition for selecting a subset of objects. The `selector.matchLabels` is a `{key,value}` Kubernetes filter by lable/selector. The `selector.matchExpressions` is a standard Kubernetes list of pod selector requirements (learn more about Kubernetes labels and selectors [here](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels)). If you don't specify a `selector`, all objects of this kind are assumed. |
| 189 | +- `namespaceSelector` — is an optional parameter, by defaul equals to `namespaceSelector.any=true`. Specifies a filter for namespaces for selecting objects. `namespaceSelector.matchNames` can contain an array of namespaces for selecting objects. |
| 190 | +- `jqFilter` — is an optional parameter, specifies additional event filter. Since a Kubernetes object has many events during its life, binding on the `update` event may give you a lot of events that you may not need. The `jqFilter` option allows binding hook only on necessary events because Shell-operator will apply defined in `jqFilter` option filter before binding to objects. |
| 191 | + - Suppose you want to execute a hook only when labels of the specified object have changed. Then you can set `"jqFilter":".metadata.labels"` and the hook will be executed only on labels of the object have changed. |
| 192 | +- `disableDebug` — is an optional field, disables debug messages for a specified event when true. |
| 193 | +- `allowFailure` — is an optional field, false by default. When true, Shell-operator will not try to re-execute hook if it fails and will skip it. |
| 194 | + |
| 195 | +Example: |
| 196 | +``` |
| 197 | +{ |
| 198 | + "onKubernetesEvent": [ |
| 199 | + { |
| 200 | + "name": "myLabelValue pods", |
| 201 | + "kind": "pod", |
| 202 | + "event": ["add", "delete"], |
| 203 | + "selector": { |
| 204 | + "matchLabels": { |
| 205 | + "myLabel": "myLabelValue" |
| 206 | + } |
| 207 | + }, |
| 208 | + "namespaceSelector": { |
| 209 | + "any": true |
| 210 | + }, |
| 211 | + "jqFilter": ".metadata.labels", |
| 212 | + "allowFailure": true |
| 213 | + } |
| 214 | + ] |
| 215 | +} |
| 216 | +``` |
| 217 | + |
| 218 | +> Note, than Shell-operator Deployment must have the serviceAccount with the appropriate rights to monitor resources in a Kubernetes cluster. |
| 219 | +
|
| 220 | +## Execution context |
| 221 | + |
| 222 | +As hook can have more than one bindings, it should identify a binding (or in other words — an event) which was triggered. When a hook is executing, the BINDING_CONTEXT_PATH environment variable contains a path to an execution context — a JSON file with information about the binding that was triggered. |
| 223 | + |
| 224 | +The execution context contains the following fields: |
| 225 | +- `binding` — contains the name of the binding type or a `name` value from the binding configuration (if set) |
| 226 | +- `resourceNamespace`, `resourceKind`, `resourceName`, `resourceEvent` — corresponding values for identifying a resource on an `onKubernetesEvent` binding. |
| 227 | + |
| 228 | +For example, you have the following hook binding configuration: |
| 229 | +```json |
| 230 | +{ |
| 231 | + "schedule": [ |
| 232 | + { |
| 233 | + "name": "incremental", |
| 234 | + "crontab": "* 2 */3 * * *", |
| 235 | + "allowFailure": true |
| 236 | + }, |
| 237 | + { |
| 238 | + "crontab": "* 59 23 * * *", |
| 239 | + "allowFailure": false |
| 240 | + } |
| 241 | + ] |
| 242 | +} |
| 243 | +``` |
| 244 | + |
| 245 | +At 12:02 this hook will be executed with the following context: |
| 246 | +```json |
| 247 | +[{ "binding": "incremental"}] |
| 248 | +``` |
| 249 | + |
| 250 | +At 23:59 this hook will be executed with the following context: |
| 251 | +```json |
| 252 | +[{ "binding": "schedule"}] |
| 253 | +``` |
| 254 | + |
| 255 | +## Debug |
| 256 | + |
| 257 | +For debugging you can use the following: |
| 258 | +- get logs of a Shell-operator pod by using `kubectl logs <POD_NAME>` |
| 259 | +- get a hook queue content in the `/tmp/shell-operator-queue` file of a Shell-operator pod |
| 260 | +- get a hook queue content by executing CURL to the `POD_IP:9115/queue` (e.g. from a Shell-operator pod itself). |
0 commit comments