Skip to content

Commit 49bb680

Browse files
committed
Improve slack-webhook-notification task
Fix various issues that have been discovered during the review process including outdated descriptions, better validation of parameters, YAML indentation, no root, consistency with other tasks in the repo... Signed-off-by: Michal Šoltis <msoltis@redhat.com>
1 parent 151a831 commit 49bb680

2 files changed

Lines changed: 161 additions & 64 deletions

File tree

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,55 @@
11
# slack-webhook-notification task
22

3-
Sends message to slack using incoming webhook
3+
Version: 0.1
4+
5+
Send a message to Slack using an incoming webhook
6+
7+
---
48

59
## Parameters
610

7-
|name|description|default value|required|
8-
|---|---|---|---|
9-
|message|Message to be sent||true|
10-
|secret-name|Secret with at least one key where value is webhook URL for slack. eg. oc create secret generic my-secret --from-literal team1=<https://hooks.slack.com/services/XXX/XXXXXX> --from-literal team2=<https://hooks.slack.com/services/YYY/YYYYYY>|slack-webhook-notification-secret|false|
11-
|key-name|Key in the key in secret which contains webhook URL for slack.||true|
12-
|user-ids|List of Slack user IDs to mention (e.g., U024BE7LH). If set, the users will be mentioned in the notification.|[]|false|
13-
|group-ids|List of Slack group IDs to mention (e.g., S0614TZR7). If set, the groups will be mentioned in the notification.|[]|false|
14-
|submodules|List of submodules name to dump. Git log since previous submodule commit will be added to the message. The previous submodule commit is found by looking at the previous commit in the repository that declares the submodules.|[]|false|
15-
|files|List of file to dump. The content will be added to the message.|[]|false|
11+
| Name | Description | Default | Required |
12+
| --- | --- | --- | --- |
13+
| `message` | Message to be sent | | Yes |
14+
| `secret-name` | Secret with at least one key whose value is a Slack incoming webhook URL | `slack-webhook-notification-secret` | No |
15+
| `key-name` | Key in the secret which contains the webhook URL for Slack | | Yes |
16+
| `user-ids` | List of Slack user IDs to mention (e.g. `U024BE7LH`) | `[]` | No |
17+
| `group-ids` | List of Slack group IDs to mention (e.g. `S0614TZR7`) | `[]` | No |
18+
| `submodules` | List of submodule names to dump into the message. Requires the source workspace | `[]` | No |
19+
| `files` | List of files to dump into the message. Requires the source workspace | `[]` | No |
1620

1721
## Workspaces
1822

19-
|name|description|optional|
20-
|---|---|---|
21-
|source|Workspace containing the source code to build.|true|
23+
| Name | Description | Optional |
24+
| --- | --- | --- |
25+
| `source` | Workspace containing the cloned repository. Required when files or submodules are set | Yes |
26+
27+
## Usage
28+
29+
```yaml
30+
apiVersion: tekton.dev/v1
31+
kind: Pipeline
32+
metadata:
33+
name: notify-on-failure
34+
spec:
35+
finally:
36+
- name: slack-notification
37+
when:
38+
- input: $(tasks.status)
39+
operator: in
40+
values: ["Failed"]
41+
taskRef:
42+
resolver: git
43+
params:
44+
- name: url
45+
value: https://github.com/konflux-ci/tekton-integration-catalog.git
46+
- name: revision
47+
value: main
48+
- name: pathInRepo
49+
value: tasks/slack-webhook-notification/0.1/slack-webhook-notification.yaml
50+
params:
51+
- name: message
52+
value: "Pipeline $(context.pipelineRun.name) failed"
53+
- name: key-name
54+
value: team1
55+
```
Lines changed: 114 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,54 @@
1+
---
12
apiVersion: tekton.dev/v1
23
kind: Task
34
metadata:
5+
name: slack-webhook-notification
46
labels:
57
app.kubernetes.io/version: "0.1"
8+
upstream-usable: "true"
69
annotations:
7-
tekton.dev/pipelines.minVersion: "0.12.1"
8-
tekton.dev/tags: "konflux"
9-
name: slack-webhook-notification
10+
tekton.dev/pipelines.minVersion: 0.12.1
11+
tekton.dev/tags: konflux
1012
spec:
11-
description: >-
12-
Sends message to slack using incoming webhook
13+
description: Send a message to Slack using an incoming webhook
1314
params:
1415
- name: message
16+
type: string
1517
description: Message to be sent
1618
- name: secret-name
17-
description: |
18-
Secret with at least one key where value is webhook URL for slack.
19-
eg. oc create secret generic my-secret --from-literal team1=https://hooks.slack.com/services/XXX/XXXXXX --from-literal team2=https://hooks.slack.com/services/YYY/YYYYYY
19+
type: string
20+
description: Secret with at least one key whose value is a Slack incoming webhook URL
2021
default: slack-webhook-notification-secret
2122
- name: key-name
22-
description: Key in the key in secret which contains webhook URL for slack.
23+
type: string
24+
description: Key in the secret which contains the webhook URL for Slack
2325
- name: user-ids
2426
type: array
25-
description: List of Slack user IDs to mention (e.g., U024BE7LH). If set, the users will be mentioned in the notification.
27+
description: List of Slack user IDs to mention (e.g. U024BE7LH)
2628
default: []
2729
- name: group-ids
2830
type: array
29-
description: List of Slack group IDs to mention (e.g., S0614TZR7). If set, the groups will be mentioned in the notification.
31+
description: List of Slack group IDs to mention (e.g. S0614TZR7)
3032
default: []
3133
- name: submodules
3234
type: array
33-
description: List of submodules name to dump. Git log since previous submodule commit will be added to the message. The previous submodule commit is found by looking at the previous commit in the repository that declares the submodules.
35+
description: List of submodule names to dump into the message. Requires the source workspace
3436
default: []
3537
- name: files
3638
type: array
37-
description: List of file to dump. The content will be added to the message.
39+
description: List of files to dump into the message. Requires the source workspace
3840
default: []
3941
volumes:
4042
- name: webhook-secret
4143
secret:
4244
secretName: $(params.secret-name)
4345
optional: true
46+
items:
47+
- key: $(params.key-name)
48+
path: webhook-url
4449
stepTemplate:
4550
imagePullPolicy: IfNotPresent
51+
4652
steps:
4753
- name: send-message
4854
image: quay.io/konflux-ci/task-runner:2.0.0@sha256:4b01fbf98fa7155f5c21443c285f88853864ae7cc66981cf6b543fc6ba16b81b
@@ -52,22 +58,19 @@ spec:
5258
cpu: 50m
5359
limits:
5460
memory: 64Mi
55-
securityContext:
56-
runAsUser: 0
57-
runAsNonRoot: false
58-
# per https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting
59-
# the cluster will set imagePullPolicy to IfNotPresent
6061
volumeMounts:
6162
- name: webhook-secret
62-
mountPath: "/etc/secrets"
63+
mountPath: /etc/secrets
6364
readOnly: true
6465
env:
6566
- name: HOME
6667
value: /tekton/home
67-
- name: KEY_NAME
68-
value: $(params.key-name)
6968
- name: MESSAGE
7069
value: $(params.message)
70+
- name: SOURCE_BOUND
71+
value: $(workspaces.source.bound)
72+
- name: SOURCE_PATH
73+
value: $(workspaces.source.path)
7174
args:
7275
- --files
7376
- $(params.files[*])
@@ -79,13 +82,14 @@ spec:
7982
- $(params.group-ids[*])
8083
script: |
8184
#!/usr/bin/env bash
85+
set -o errexit -o nounset -o pipefail
8286
8387
# ---------
8488
# HELPERS
8589
# ---------
8690
8791
function concat {
88-
cat << EOM
92+
cat <<EOM
8993
$1
9094
$2
9195
EOM
@@ -100,38 +104,71 @@ spec:
100104
}
101105
102106
function dumpFile {
103-
filePath=$1
107+
local filePath=$1
108+
local resolved
109+
local workspace_root
110+
111+
if [[ "${filePath}" == /* ]] || [[ "${filePath}" == *..* ]]; then
112+
echo "Refusing to read path outside workspace: ${filePath}" >&2
113+
exit 1
114+
fi
115+
116+
if [ ! -f "${filePath}" ]; then
117+
echo "File not found in workspace: ${filePath}" >&2
118+
exit 1
119+
fi
120+
121+
workspace_root=$(pwd -P)
122+
resolved=$(cd "$(dirname "${filePath}")" && pwd -P)/$(basename "${filePath}")
123+
case "${resolved}" in
124+
"${workspace_root}"|"${workspace_root}"/*) ;;
125+
*)
126+
echo "Refusing to read path outside workspace: ${filePath}" >&2
127+
exit 1
128+
;;
129+
esac
104130
105-
cat << EOM
131+
cat <<EOM
106132
*${filePath}:*
107133
\`\`\`
108-
$(cat "${filePath}")
134+
$(cat "${resolved}")
109135
\`\`\`
110136
EOM
111137
}
112138
113-
# dumpSubmodule will dump the git log history as follow
114-
# $commitShortSHA $authorName $subject
115-
# with some padding to align the lines (author name is truncated after 27 char).
116-
# A link to the previous commits is available (tested on github & gitlab).
117139
function dumpSubmodule {
118-
name=$1
140+
local name=$1
141+
local path
142+
local url
143+
local current_commit
144+
local previous_commit
145+
local commits
119146
120147
path=$(git config -f .gitmodules --get submodule."${name}".path)
121148
url=$(git config -f .gitmodules --get submodule."${name}".url)
122149
123150
current_commit=$(git -C "${path}" rev-parse HEAD)
124-
previous_commit=$(git diff HEAD~1 "${path}" | grep commit | head -n 1 | awk '{print $3;}')
125-
if [ "${previous_commit}" = "" ]; then
126-
previous_commit=${current_commit}
151+
previous_commit="${current_commit}"
152+
153+
if git rev-parse --verify --quiet HEAD~1 >/dev/null; then
154+
previous_commit=$(
155+
git diff HEAD~1 -- "${path}" \
156+
| awk '/^-Subproject commit / {print $3; exit}'
157+
)
158+
if [ -z "${previous_commit}" ]; then
159+
previous_commit="${current_commit}"
160+
elif ! [[ "${previous_commit}" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
161+
echo "Invalid previous submodule commit: ${previous_commit}" >&2
162+
exit 1
163+
fi
127164
fi
128165
129166
commits=None
130167
if [ "${previous_commit}" != "${current_commit}" ]; then
131168
commits=$(git -C "${path}" log --pretty=format:'%h %<(27,trunc)%an %s' --abbrev-commit "${previous_commit}".."${current_commit}")
132169
fi
133170
134-
cat << EOM
171+
cat <<EOM
135172
*Submodule ${name} (<${url}/commits/${current_commit}|commits>):*
136173
\`\`\`
137174
${commits}
@@ -154,43 +191,52 @@ spec:
154191
case $1 in
155192
--files)
156193
shift
157-
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^--.* ]]; do
194+
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^-- ]]; do
158195
FILES+=("$1")
159196
shift
160197
done
161198
;;
162199
--submodules)
163200
shift
164-
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^--.* ]]; do
201+
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^-- ]]; do
165202
SUBMODULES+=("$1")
166203
shift
167204
done
168205
;;
169206
--user-ids)
170207
shift
171-
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^--.* ]]; do
208+
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^-- ]]; do
172209
USER_IDS+=("$1")
173210
shift
174211
done
175212
;;
176213
--group-ids)
177214
shift
178-
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^--.* ]]; do
215+
while [[ $# -gt 0 ]] && ! [[ "$1" =~ ^-- ]]; do
179216
GROUP_IDS+=("$1")
180217
shift
181218
done
182219
;;
220+
*)
221+
echo "Unknown option: $1" >&2
222+
exit 1
223+
;;
183224
esac
184225
done
185226
186227
# -------------------
187228
# PARAMS VALIDATION
188229
# -------------------
189230
190-
if [ -f "/etc/secrets/$KEY_NAME" ]; then
191-
WEBHOOK_URL=$(cat "/etc/secrets/$KEY_NAME")
231+
if [ -f /etc/secrets/webhook-url ]; then
232+
WEBHOOK_URL=$(cat /etc/secrets/webhook-url)
192233
else
193-
echo "Secret not defined properly"
234+
echo "Secret not defined properly" >&2
235+
exit 1
236+
fi
237+
238+
if [ -z "${WEBHOOK_URL}" ]; then
239+
echo "Webhook URL is empty" >&2
194240
exit 1
195241
fi
196242
@@ -221,29 +267,46 @@ spec:
221267
slack_message="${user_mentions}${group_mentions}${slack_message}"
222268
fi
223269
270+
if [ ${#FILES[@]} -ne 0 ] || [ ${#SUBMODULES[@]} -ne 0 ]; then
271+
if [ "${SOURCE_BOUND}" != "true" ]; then
272+
echo "source workspace is required when files or submodules are set" >&2
273+
exit 1
274+
fi
275+
cd "${SOURCE_PATH}"
276+
fi
277+
224278
if [ ${#FILES[@]} -ne 0 ]; then
225-
slack_message=$(concat "${slack_message}" "$(dumpSeparator)")
279+
slack_message=$(concat "${slack_message}" "$(dumpSeparator)")
226280
fi
227281
228282
for file in "${FILES[@]}"; do
229-
content=$(dumpFile "${file}")
230-
slack_message=$(concat "${slack_message}" "${content}")
283+
content=$(dumpFile "${file}")
284+
slack_message=$(concat "${slack_message}" "${content}")
231285
done
232286
233287
if [ ${#SUBMODULES[@]} -ne 0 ]; then
234-
slack_message=$(concat "${slack_message}" "$(dumpSeparator)")
288+
slack_message=$(concat "${slack_message}" "$(dumpSeparator)")
235289
fi
236290
237291
for submodule in "${SUBMODULES[@]}"; do
238-
content=$(dumpSubmodule "${submodule}")
239-
slack_message=$(concat "${slack_message}" "${content}")
292+
content=$(dumpSubmodule "${submodule}")
293+
slack_message=$(concat "${slack_message}" "${content}")
240294
done
241295
242-
data=$(jq --compact-output --null-input --arg message "$slack_message" '{text: $message}')
296+
data=$(jq --compact-output --null-input --arg message "${slack_message}" '{text: $message}')
297+
298+
# Pass the webhook URL via a curl config file so it is not visible in process argv (/proc/<pid>/cmdline).
299+
curl_config="$(mktemp)"
300+
trap 'rm -f "${curl_config}"' EXIT
301+
printf 'url = "%s"\n' "${WEBHOOK_URL}" >"${curl_config}"
302+
chmod 600 "${curl_config}"
243303
244-
curl -X POST -H 'Content-type: application/json' --data "${data}" "$WEBHOOK_URL"
245-
workingDir: $(workspaces.source.path)/source
304+
printf '%s' "${data}" | curl --fail --silent --show-error \
305+
-X POST \
306+
-H 'Content-type: application/json' \
307+
--data-binary @- \
308+
--config "${curl_config}"
246309
workspaces:
247310
- name: source
248-
description: Workspace containing the source code to build.
311+
description: Workspace containing the cloned repository. Required when files or submodules are set
249312
optional: true

0 commit comments

Comments
 (0)