-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathcloudypad.sh
executable file
·160 lines (135 loc) · 5.46 KB
/
cloudypad.sh
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
#!/usr/bin/env bash
# Wrapper around CloudyPad container in which most operations are performed
# so that no other dependency other than Docker or a container runtime is needed
#
# Build a container image with current user ID on the fly (to avoid permission issues)
# and run instructions.
# Only a few commands need to run directly for user (eg. moonlight setup)
if [ -n "$CLOUDYPAD_CLI_LAUNCHER_DEBUG" ]; then
set -x
fi
CLOUDYPAD_VERSION=0.10.0
CLOUDYPAD_IMAGE="${CLOUDYPAD_IMAGE:-"ghcr.io/pierrebeucher/cloudypad:$CLOUDYPAD_VERSION"}"
CLOUDYPAD_TARGET_IMAGE="cloudypad/local-runner:local"
# Hidden command used during installation to setup Docker image locally
if [ "$1" == "download-container-images" ]; then
docker pull $CLOUDYPAD_IMAGE >&2
exit 0
fi
if ! docker image inspect $CLOUDYPAD_IMAGE > /dev/null 2>&1; then
echo "Please wait a moment while Cloudy Pad container image $CLOUDYPAD_IMAGE is being pulled..." >&2
echo "This is normally done once during installation but may happen again if you deleted or cleaned-up your local Docker images." >&2
docker pull $CLOUDYPAD_IMAGE >&2
fi
# Build Dockerfile on-the-fly
# make sure the container's user ID and group match host to prevent permission issue
HOST_UID=$(id -u)
HOST_USER_NAME=$(id -un)
HOST_GID=$(id -g)
HOST_GROUP_NAME=$(id -gn)
cat <<EOF > /tmp/Dockerfile-cloudypad-run
FROM $CLOUDYPAD_IMAGE
# Ensure the host user matches user in container.
# If host user is root, do nothing and run container as root, otherwise:
# - Delete user matching host's user ID if already exists
# - Create group if not exists
# - Create user
RUN if [ "$(id -u $HOST_UID)" -ne 0 ] >/dev/null 2>&1; then \
if id -u $HOST_UID >/dev/null 2>&1; then \
deluser \$(id -un $HOST_UID); \
fi && \
if ! getent group $HOST_GID >/dev/null; then \
groupadd -g $HOST_GID $HOST_GROUP_NAME; \
fi && \
useradd -u $HOST_UID -g $HOST_GID --home-dir $HOME --create-home $HOST_USER_NAME; \
fi
USER $HOST_UID
EOF
container_build_output=$(docker build -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1)
container_build_result=$?
if [ $container_build_result -ne 0 ]; then
echo "Error: could not build CloudyPad container image, build exited with code: $container_build_result" >&2
echo "Build command was: docker build -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1" >&2
echo "Build output: "
echo "$container_build_output"
echo
echo "If you think this is a bug, please file an issue." >&2
exit 1
fi
# Create and run a container for Cloudy Pad
# Builds a docker command with required volumes and env vars so that
# running container matches host:
# - user (uid and main group)
# - mount important directories (such as home and ssh)
# - mount Cloud credentials if available
# - add environment variable matching host
run_cloudypad_docker() {
# Ensure Cloudy Pad home exists and is secure enough
# So as not to create it from Docker volume mount as root
# TODO check permission?
mkdir -p $HOME/.cloudypad
chmod 0700 $HOME/.cloudypad
# Create Paperspace and directory if not already exists to keep it if user log-in from container
mkdir -p $HOME/.paperspace
# List of directories to mount only if they exist
local mounts=(
"$HOME/.cloudypad"
"$HOME/.ssh"
"$HOME/.aws"
"$HOME/.paperspace"
"$HOME/.azure"
"$HOME/.config/gcloud"
)
# Build run command with proper directories
local cmd="docker run --rm"
# Set interactive+tty by default
# no tty if CLOUDYPAD_CONTAINER_NO_TTY is set (for CI)
if [ -n "$CLOUDYPAD_CONTAINER_NO_TTY" ]; then
cmd="$cmd -t"
else
cmd="$cmd -it"
fi
# Only mount a directory if it exists on host
for mount in "${mounts[@]}"; do
if [ -d "$mount" ]; then
cmd+=" -v $mount:$mount"
fi
done
# Local environment variables to pass-through in container
local env_vars=(
# AWS
"AWS_PROFILE" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_SESSION_TOKEN"
"AWS_DEFAULT_REGION" "AWS_REGION" "AWS_ENDPOINT_URL" "AWS_PROFILE"
"AWS_ROLE_ARN" "AWS_ROLE_SESSION_NAME"
# Paperspace
# Not yet "PAPERSPACE_API_KEY_FILE" as it's likely file won't exist in container as not bind-mounted
"PAPERSPACE_API_KEY"
# Azure
"AZURE_LOCATION" "AZURE_SUBSCRIPTION_ID" "AZURE_CLIENT_ID" "AZURE_SECRET" "AZURE_TENANT"
"ARM_SUBSCRIPTION_ID" "ARM_CLIENT_ID" "ARM_CLIENT_SECRET" "ARM_TENANT_ID"
# Google
"GOOGLE_PROJECT" "GOOGLE_CLOUD_PROJECT" "GCLOUD_PROJECT" "CLOUDSDK_CORE_PROJECT"
"GOOGLE_REGION" "GCLOUD_REGION" "CLOUDSDK_COMPUTE_REGION"
"GOOGLE_ZONE" "GCLOUD_ZONE" "CLOUDSDK_COMPUTE_ZONE"
"GOOGLE_IMPERSONATE_SERVICE_ACCOUNT"
)
for env_var in "${env_vars[@]}"; do
if [ -n "${!env_var}" ]; then
cmd+=" -e $env_var=${!env_var}"
fi
done
# Add SSH agent volume and env var if it's available locally
if [ -n "$SSH_AUTH_SOCK" ]; then
cmd+=" -v $SSH_AUTH_SOCK:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent"
fi
# If first arg is "debug-container" run a bash as entrypoint
# This is a hidden command for debugging :)
if [ "$1" == "debug-container" ]; then
cmd+=" --entrypoint /bin/bash $CLOUDYPAD_TARGET_IMAGE"
$cmd
else
cmd+=" $CLOUDYPAD_TARGET_IMAGE"
$cmd "${@}"
fi
}
run_cloudypad_docker "${@:1}"