forked from huggingface/tailscale-action
-
Notifications
You must be signed in to change notification settings - Fork 0
/
action.yaml
320 lines (313 loc) · 13.6 KB
/
action.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# Copyright (c) Tailscale Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause
#
name: 'Connect Tailscale'
description: 'Connect your GitHub Action workflow to Tailscale'
branding:
icon: 'arrow-right-circle'
color: 'gray-dark'
inputs:
authkey:
description: 'Your Tailscale authentication key, from the admin panel.'
required: false
deprecationMessage: 'An OAuth API client https://tailscale.com/s/oauth-clients is recommended instead of an authkey'
oauth-client-id:
description: 'Your Tailscale OAuth Client ID.'
required: false
oauth-secret:
description: 'Your Tailscale OAuth Client Secret.'
required: false
tags:
description: 'Comma separated list of Tags to be applied to nodes. The OAuth client must have permission to apply these tags.'
required: false
version:
description: 'Tailscale version to use.'
required: true
default: '1.68.1'
sha256sum:
description: 'Expected SHA256 checksum of the tarball.'
required: false
default: '18edc3067c41bfbcd6eeffeab45d93e10566219cb0ebb78a0f59eba56acc37e3'
containerMode:
description: 'This mode will use the Userspace networking mode (specially for container where tunnel VPN is not possible). DEPRECATED, not used anymore'
type: boolean
required: false
default: true
debug:
description: 'This mode generate the tailscale bug report'
type: boolean
required: false
default: false
debugEnabled:
description: 'This mode will allow to SSH to the runner. DEPRECATED, not used, replaced by github runner variable'
type: boolean
required: false
default: false
acceptDns:
description: ''
type: boolean
required: false
default: true
acceptRoutes:
description: ''
type: boolean
required: false
default: true
slackChannel:
description: 'Provide Slack Channel to send SSH information'
type: string
required: false
slackToken:
description: 'Slack Token to send message'
type: string
required: false
waitForSSH:
description: 'You can use this action at the end of your job with waitForSSH=true to handle SSH connection in case of workflow failed'
type: boolean
required: false
default: false
sshTimeout:
description: 'Number of minute to wait for SSH connection before ending the job'
type: string
required: false
default: "5m"
sshKeyId:
description: 'Internal usage. when the SSH Key changed on tailscale, please update the default value. Its in use in this action, to take the following decisions'
# - if this key is used on the WF, and debug mode is not enabled during action run, no need to connect to tailscale
# - if tailscale is used for something else than SSH (like internal ressources access), so we are testing internal url access.
type: string
required: false
default: "tskey-auth-kBgJJWKh3311CNTRL"
runs:
using: 'composite'
steps:
- name: Check Runner OS
if: ${{ runner.os != 'Linux' }}
shell: bash
run: |
echo "::error title=⛔ error hint::Support Linux Only"
exit 1
- name: check debug
#if: ${{ runner.debug == '1' }}
shell: bash
run: |
if [ "${{ runner.debug }}" = "1" ]; then
echo "debug"
else
echo "no debug"
fi
- name: Check Tailscale Action Usage mode (waitForSSH or Normal)
id: tailscale-mode
shell: bash
run: |
#if waitForSSH is enabled, we need to check if Tailscale is already connected or not (this parameter must be used for debugging usage when a step of worklfow failed)
#if Tailscale is already connected, it means the action was already called at the start of the WF with all mandatory inputs, we just need to enable SSH and wait for connection.
#if Tailscale is not yet connected, we need to execute the entire action (aka setup tailscale) and wait for SSH at the end
if [ "${{ inputs['waitForSSH'] }}" = true ]; then
if ! command -v tailscale &> /dev/null
then
echo "INSTALL=true" >> $GITHUB_OUTPUT
echo "WITHSSH=true" >> $GITHUB_OUTPUT
echo "WAITFORSSH=true" >> $GITHUB_OUTPUT
else
echo "INSTALL=false" >> $GITHUB_OUTPUT
echo "WITHSSH=true" >> $GITHUB_OUTPUT
echo "WAITFORSSH=true" >> $GITHUB_OUTPUT
fi
else
#if Debug is not enabled, so we need to check why this action is called by checking AUTH_KEY : if the workflow is using SSH'ed key, and debug is not enabled, no need to execute Tailscale
if [ "${{ runner.debug }}" = "1" ]; then #debug enabled, so connect to tailscale with SSH
echo "INSTALL=true" >> $GITHUB_OUTPUT
echo "WITHSSH=true" >> $GITHUB_OUTPUT
echo "WAITFORSSH=false" >> $GITHUB_OUTPUT
else
if [[ "${{ inputs['authkey'] }}" =~ "${{ inputs['sshKeyId'] }}" ]]; then #debug not enabled, so if the Key is the SSH'ed one, no need to execute tailscale
echo "INSTALL=false" >> $GITHUB_OUTPUT
echo "WITHSSH=false" >> $GITHUB_OUTPUT
echo "WAITFORSSH=false" >> $GITHUB_OUTPUT
else #debug not enable, but need to execute tailscale because it's standard Tailscale Key
echo "INSTALL=true" >> $GITHUB_OUTPUT
echo "WITHSSH=false" >> $GITHUB_OUTPUT
echo "WAITFORSSH=false" >> $GITHUB_OUTPUT
fi
fi
fi
- name: Check Auth Info Empty
if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' && inputs.authkey == '' && (inputs['oauth-secret'] == '' || inputs.tags == '') }}
shell: bash
run: |
echo "::error title=⛔ error hint::OAuth identity empty, Maybe you need to populate it in the Secrets for your workflow, see more in https://docs.github.com/en/actions/security-guides/encrypted-secrets and https://tailscale.com/s/oauth-clients"
exit 1
- name: Install prerequistes
if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' }}
shell: bash
run: |
if ! command -v sudo &> /dev/null
then
apt-get update
apt-get install -y sudo
else
sudo apt-get update
fi
sudo apt-get install -y curl iptables
- name: Download Tailscale
if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' }}
shell: bash
id: download
env:
VERSION: ${{ inputs.version }}
SHA256SUM: ${{ inputs.sha256sum }}
run: |
if [ ${{ runner.arch }} = "ARM64" ]; then
TS_ARCH="arm64"
elif [ ${{ runner.arch }} = "ARM" ]; then
TS_ARCH="arm"
elif [ ${{ runner.arch }} = "X86" ]; then
TS_ARCH="386"
elif [ ${{ runner.arch }} = "X64" ]; then
TS_ARCH="amd64"
else
TS_ARCH="amd64"
fi
MINOR=$(echo "$VERSION" | awk -F '.' {'print $2'})
if [ $((MINOR % 2)) -eq 0 ]; then
URL="https://pkgs.tailscale.com/stable/tailscale_${VERSION}_${TS_ARCH}.tgz"
else
URL="https://pkgs.tailscale.com/unstable/tailscale_${VERSION}_${TS_ARCH}.tgz"
fi
if ! [[ "$SHA256SUM" ]] ; then
SHA256SUM="$(curl -H user-agent:tailscale-github-action -L "${URL}.sha256")"
fi
curl -H user-agent:tailscale-github-action -L "$URL" -o tailscale.tgz --max-time 300
echo "Expected sha256: $SHA256SUM"
echo "Actual sha256: $(sha256sum tailscale.tgz)"
echo "$SHA256SUM tailscale.tgz" | sha256sum -c
tar -C /tmp -xzf tailscale.tgz
rm tailscale.tgz
TSPATH=/tmp/tailscale_${VERSION}_${TS_ARCH}
sudo mv "${TSPATH}/tailscale" "${TSPATH}/tailscaled" /usr/bin
- name: Start Tailscale Daemon
if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' }}
shell: bash
run: |
sudo mkdir -p /dev/net
if [ ! -e /dev/net/tun ]
then
sudo mknod /dev/net/tun c 10 200
fi
sudo -E tailscaled --state=mem: 2>~/tailscaled.log &
# And check that tailscaled came up. The CLI will block for a bit waiting
# for it. And --json will make it exit with status 0 even if we're logged
# out (as we will be). Without --json it returns an error if we're not up.
sudo -E tailscale status --json >/dev/null
- name: Connect to Tailscale
if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' }}
shell: bash
env:
TAILSCALE_AUTHKEY: ${{ inputs.authkey }}
HOSTNAME: ${{ inputs.hostname }}
TS_EXPERIMENT_OAUTH_AUTHKEY: true
DNS: ${{ inputs.acceptDns }}
ACCEPT_ROUTES: ${{ inputs.acceptRoutes }}
run: |
if [ -z "${HOSTNAME}" ]; then
HOSTNAME="github-$(cat /etc/hostname)"
fi
if [ -n "${{ inputs['oauth-secret'] }}" ]; then
TAILSCALE_AUTHKEY="${{ inputs['oauth-secret'] }}?preauthorized=true&ephemeral=true"
TAGS_ARG="--advertise-tags=${{ inputs.tags }}"
fi
timeout 5m sudo -E tailscale up ${TAGS_ARG} --authkey=${TAILSCALE_AUTHKEY} --hostname=${HOSTNAME} --accept-dns=${DNS} --accept-routes=${ACCEPT_ROUTES} ${ADDITIONAL_ARGS}
#we don't need network test anymore, because we are not using tailscale on CI for accessing registry
#- name: Network Test
# if: ${{ steps.tailscale-mode.outputs.INSTALL == 'true' }}
# shell: bash
# run: |
# if [[ "${{ inputs['authkey'] }}" =~ "${{ inputs['sshKeyId'] }}" ]]; then
# url=https://status.taildb5d.ts.net/status/default
# else
# url=https://registry.internal.huggingface.tech
# fi
# echo $url
# curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 $url
# echo "WAN IP:"
# curl ifconfig.me/ip
- name: Store Slack infos
#because the SSH can be enabled dynamically if the workflow failed, so we need to store slack infos to be able to retrieve them during the waitforssh step
if: ${{ inputs.slackChannel != '' }}
shell: bash
run: |
echo "SLACKCHANNEL=${{ inputs['slackChannel'] }}" >> $GITHUB_ENV
echo "SLACKTOKEN=${{ inputs['slackToken'] }}" >> $GITHUB_ENV
- name: Enable SSH
id: ssh
if: ${{ steps.tailscale-mode.outputs.WITHSSH == 'true' }}
shell: bash
run: |
sudo apt-get -y install openssh-server >> /dev/null
sudo tailscale set --ssh
echo "TS_IP=$(tailscale ip --4)" >> $GITHUB_OUTPUT
echo "USER=$(whoami)" >> $GITHUB_OUTPUT
echo "warning message content : ${{ inputs['warningMessage'] }}"
if [ "${{ runner.debug }}" = "1" ]; then
echo "MESSAGEHEADER=New Job started in debug mode" >> $GITHUB_OUTPUT
else
echo "MESSAGEHEADER=:x:JOB FAILED !" >> $GITHUB_OUTPUT
fi
- name: Send SSH informations to Slack channel
if: ${{ steps.tailscale-mode.outputs.WITHSSH == 'true' && env.SLACKCHANNEL != '' }}
uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001
with:
# Slack channel id, channel name, or user id to post message.
# See also: https://api.slack.com/methods/chat.postMessage#channels
channel-id: ${{ env.SLACKCHANNEL }}
# For posting a rich message using Block Kit
payload: |
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${{ steps.ssh.outputs.MESSAGEHEADER }}",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Repository:*\n `${{ github.repository }}`"
},
{
"type": "mrkdwn",
"text": "*Workflow:*\n `${{ github.workflow }}`"
},
{
"type": "mrkdwn",
"text": "*Started by:*\n ${{ github.actor }}"
},
{
"type": "mrkdwn",
"text": "*Job:*\n `${{ github.job}}`"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "start your VPN (tailscale) and : `ssh ${{ steps.ssh.outputs.USER }}@${{ steps.ssh.outputs.TS_IP }} `"
}
}
]
}
env:
SLACK_BOT_TOKEN: ${{ env.SLACKTOKEN }}
- name: Wait for SSH
if: ${{ steps.tailscale-mode.outputs.WAITFORSSH == 'true' }}
shell: bash
run: |
sleep "${{ inputs['sshTimeout'] }}"
while [ "$(last | grep '^\(ubuntu\|runner\|root\).*still logged in$')" ]; do sleep 1m; done