-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathstart-docker.sh
More file actions
299 lines (269 loc) · 12 KB
/
start-docker.sh
File metadata and controls
299 lines (269 loc) · 12 KB
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
#!/bin/bash
#
# start-docker.sh - Initialize and start Tymeslot inside a Docker container
#
# This script runs inside the Docker container as the entry point. It:
# 1. Sets environment variables with sensible defaults
# 2. Validates required SECRET_KEY_BASE is set
# 3. Initializes PostgreSQL database (first run only)
# 4. Starts the PostgreSQL service
# 5. Creates database and user
# 6. Runs Ecto database migrations (passing all env vars to su command)
# 7. Starts the Phoenix web server (passing all env vars to su command)
#
# Important: All environment variables must be explicitly passed to 'su' commands
# because su creates a new shell that doesn't inherit parent environment variables.
#
# Note: This runs inside the container, NOT on the host machine
set -eu # Exit on error and on undefined variables
# ==================== SECTION 1: Environment Variable Defaults ====================
# Set sensible defaults to avoid unbound variable errors when env vars are missing
# These can be overridden by passing -e flags to 'docker run'
POSTGRES_USER=${POSTGRES_USER:-tymeslot}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-tymeslot}
POSTGRES_DB=${POSTGRES_DB:-tymeslot}
PHX_HOST=${PHX_HOST:-localhost}
PORT=${PORT:-4000}
DATABASE_HOST=${DATABASE_HOST:-localhost}
DATABASE_PORT=${DATABASE_PORT:-5432}
# ==================== SECTION 2: Validate Critical Configuration ====================
# Validate all required environment variables are set
# Fail fast with clear error messages if any are missing
MISSING_VARS=""
if [ -z "${SECRET_KEY_BASE:-}" ]; then
MISSING_VARS="${MISSING_VARS} - SECRET_KEY_BASE\n"
fi
if [ -n "$MISSING_VARS" ]; then
echo "========================================"
echo "✗ ERROR: Required environment variables are missing!"
echo "========================================"
echo ""
echo "The following required variables are not set:"
echo -e "$MISSING_VARS"
echo ""
echo "Generate secrets with:"
echo " openssl rand -base64 64 | tr -d '\\n'"
echo ""
echo "Then add them to your .env file:"
echo " SECRET_KEY_BASE=<generated_secret>"
echo ""
echo "Or pass them via docker-compose.yml environment section."
echo "Make sure docker-compose is reading your .env file!"
echo "========================================"
exit 1
fi
echo "✓ All required environment variables are set"
echo ""
echo "========================================"
echo "Starting Tymeslot (Docker deployment)"
echo "========================================"
echo ""
# ==================== SECTION 2B: Detect Database Configuration ====================
# Check if using external database or embedded PostgreSQL
USING_EXTERNAL_DB=false
if [ "$DATABASE_HOST" != "localhost" ] && [ "$DATABASE_HOST" != "127.0.0.1" ]; then
USING_EXTERNAL_DB=true
echo "✓ External database detected: $DATABASE_HOST:$DATABASE_PORT"
echo " Skipping embedded PostgreSQL initialization"
elif [ -n "${DATABASE_URL:-}" ]; then
USING_EXTERNAL_DB=true
echo "✓ DATABASE_URL detected"
echo " Skipping embedded PostgreSQL initialization"
fi
echo ""
# ==================== SECTION 3: PostgreSQL Database Initialization ====================
# Check if PostgreSQL has already been initialized
# If not, perform first-time initialization and configuration
# SKIP this section if using an external database
if [ "$USING_EXTERNAL_DB" = false ] && [ ! -f /var/lib/postgresql/data/postgresql.conf ]; then
echo "PostgreSQL not initialized. Running first-time setup..."
# Run initdb to create the initial database cluster
if su - postgres -c "/usr/lib/postgresql/*/bin/initdb -D /var/lib/postgresql/data"; then
echo "✓ PostgreSQL initialized successfully"
else
echo "✗ ERROR: PostgreSQL initialization failed!"
exit 1
fi
# Configure PostgreSQL for Docker environment
# Allow remote connections with MD5 password authentication
echo "host all all 0.0.0.0/0 md5" >> /var/lib/postgresql/data/pg_hba.conf
# Bind to localhost interface
echo "listen_addresses = 'localhost'" >> /var/lib/postgresql/data/postgresql.conf
# Use default PostgreSQL port
echo "port = 5432" >> /var/lib/postgresql/data/postgresql.conf
echo "✓ PostgreSQL configuration completed"
else
echo "✓ PostgreSQL already initialized"
fi
# ==================== SECTION 4: Start PostgreSQL Service ====================
# Only start embedded PostgreSQL if not using external database
if [ "$USING_EXTERNAL_DB" = false ]; then
echo "Starting PostgreSQL service..."
# Start PostgreSQL daemon and log output to a file
if su - postgres -c "/usr/lib/postgresql/*/bin/pg_ctl -D /var/lib/postgresql/data -l /var/lib/postgresql/data/logfile start"; then
echo "✓ PostgreSQL service started"
else
echo "✗ ERROR: Failed to start PostgreSQL service!"
exit 1
fi
# ==================== SECTION 5: Wait for PostgreSQL Readiness ====================
# PostgreSQL needs time to start up; poll until it's ready to accept connections
echo "Waiting for PostgreSQL to be ready..."
RETRY_COUNT=0
MAX_RETRIES=30
until su - postgres -c "pg_isready -h localhost -p 5432" > /dev/null 2>&1; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo "✗ ERROR: PostgreSQL failed to become ready after ${MAX_RETRIES} seconds!"
echo "Check /var/lib/postgresql/data/logfile for details"
exit 1
fi
echo " Waiting... ($RETRY_COUNT/$MAX_RETRIES)"
sleep 1
done
echo "✓ PostgreSQL is ready and accepting connections!"
else
echo "Waiting for external database to be ready..."
RETRY_COUNT=0
MAX_RETRIES=30
until pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" > /dev/null 2>&1; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
echo "✗ ERROR: External database failed to respond after ${MAX_RETRIES} seconds!"
echo "Check your DATABASE_HOST ($DATABASE_HOST) and DATABASE_PORT ($DATABASE_PORT) configuration"
exit 1
fi
echo " Waiting... ($RETRY_COUNT/$MAX_RETRIES)"
sleep 1
done
echo "✓ External database is ready and accepting connections!"
fi
# ==================== SECTION 6: Database and User Setup ====================
# Create database user and database if they don't already exist
# Only needed for embedded PostgreSQL; external databases should be pre-configured
if [ "$USING_EXTERNAL_DB" = false ]; then
echo "Setting up database user and database..."
# Create user (suppress error if already exists)
if su - postgres -c "psql -c \"CREATE USER ${POSTGRES_USER} WITH PASSWORD '$POSTGRES_PASSWORD';\"" 2>/dev/null; then
echo "✓ Created database user: ${POSTGRES_USER}"
else
echo " User '${POSTGRES_USER}' already exists (OK)"
fi
# Create database (suppress error if already exists)
if su - postgres -c "psql -c \"CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};\"" 2>/dev/null; then
echo "✓ Created database: ${POSTGRES_DB}"
else
echo " Database '${POSTGRES_DB}' already exists (OK)"
fi
# Grant privileges
su - postgres -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_USER};\"" >/dev/null 2>&1 || true
echo "✓ Database setup completed"
else
echo "✓ Using external database (ensure it's pre-configured)"
fi
# ==================== SECTION 7: Application Data Directory Setup ====================
# Create required directories for timezone data and user uploads
echo "Setting up application data directories..."
mkdir -p /app/data/tzdata /app/data/uploads
# Set proper ownership so the 'app' user can write to these directories
chown -R app:app /app/data
echo "✓ Data directories ready"
# ==================== SECTION 8: Log Configuration (Non-Sensitive) ====================
# Display current configuration for debugging purposes
# Intentionally omit SECRET_KEY_BASE and database password for security
echo ""
echo "========================================"
echo "Environment Configuration:"
echo "========================================"
echo " MIX_ENV: ${MIX_ENV:-not set (will default to prod in release)}"
echo " DEPLOYMENT_TYPE: ${DEPLOYMENT_TYPE:-docker}"
echo " PHX_HOST: ${PHX_HOST}"
echo " PORT: ${PORT}"
if [ "$USING_EXTERNAL_DB" = true ]; then
echo " Database: EXTERNAL ($DATABASE_HOST:$DATABASE_PORT)"
else
echo " Database: EMBEDDED (localhost:5432)"
echo " Database Name: ${POSTGRES_DB}"
echo " Database User: ${POSTGRES_USER}"
fi
echo " EMAIL_ADAPTER: ${EMAIL_ADAPTER:-test}"
echo ""
echo "Security Configuration:"
echo " SECRET_KEY_BASE: ✓ SET ($(echo -n "$SECRET_KEY_BASE" | wc -c) characters)"
echo ""
echo "Note: OAuth and calendar integrations are"
echo " configured through the dashboard"
echo "========================================"
echo ""
# ==================== SECTION 9: Database Migrations ====================
# Run Ecto migrations to set up or update the database schema
# Must be run as 'app' user (for proper permissions)
# We export variables to the environment and use 'su -p' to preserve them,
# which prevents secrets from appearing in the process list (ps).
echo "========================================"
echo "Running database migrations..."
echo "========================================"
# Export all required environment variables for the app
export SECRET_KEY_BASE
export DEPLOYMENT_TYPE="${DEPLOYMENT_TYPE:-docker}"
export PHX_HOST
export PORT
export POSTGRES_DB
export POSTGRES_USER
export POSTGRES_PASSWORD
export DATABASE_POOL_SIZE="${DATABASE_POOL_SIZE:-10}"
export EMAIL_ADAPTER="${EMAIL_ADAPTER:-test}"
export EMAIL_FROM_NAME="${EMAIL_FROM_NAME:-Tymeslot}"
export EMAIL_FROM_ADDRESS="${EMAIL_FROM_ADDRESS:-hello@localhost}"
export SMTP_HOST="${SMTP_HOST:-}"
export SMTP_PORT="${SMTP_PORT:-587}"
export SMTP_USERNAME="${SMTP_USERNAME:-}"
export SMTP_PASSWORD="${SMTP_PASSWORD:-}"
export POSTMARK_API_KEY="${POSTMARK_API_KEY:-}"
export GITHUB_CLIENT_ID="${GITHUB_CLIENT_ID:-}"
export GITHUB_CLIENT_SECRET="${GITHUB_CLIENT_SECRET:-}"
export GOOGLE_CLIENT_ID="${GOOGLE_CLIENT_ID:-}"
export GOOGLE_CLIENT_SECRET="${GOOGLE_CLIENT_SECRET:-}"
export ENABLE_GOOGLE_AUTH="${ENABLE_GOOGLE_AUTH:-false}"
export ENABLE_GITHUB_AUTH="${ENABLE_GITHUB_AUTH:-false}"
if su -p app -c "cd /app && bin/tymeslot eval 'Ecto.Migrator.with_repo(Tymeslot.Repo, &Ecto.Migrator.run(&1, :up, all: true))'"; then
echo "========================================"
echo "✓ Database migrations completed successfully"
echo "========================================"
else
echo "========================================"
echo "✗ ERROR: Database migrations failed!"
echo "========================================"
echo "This usually means:"
echo " - Database connection failed"
echo " - Missing required environment variables"
echo " - Migration syntax error"
echo ""
echo "Check the error output above for details."
exit 1
fi
# ==================== SECTION 10: Start Phoenix Web Server ====================
# Start the Phoenix web server in foreground (important for Docker)
# We use 'su -p' to preserve the exported environment variables.
# PHX_SERVER=true enables the web server (vs. just eval mode)
echo ""
echo "========================================"
echo "Starting Tymeslot Phoenix server..."
echo "========================================"
echo "Server will be available at:"
echo " http://${PHX_HOST}:${PORT}"
echo ""
echo "If you see this message without errors above, startup was successful!"
echo "Check for 'Running TymeslotWeb.Endpoint' message below to confirm."
echo "========================================"
echo ""
export PHX_SERVER=true
# Start the server (this runs in foreground and blocks)
# We use exec to replace the shell process with the su process
exec su -p app -c "cd /app && bin/tymeslot start"
# If we reach here, the server has stopped (shouldn't happen in normal operation)
echo ""
echo "========================================"
echo "✗ WARNING: Phoenix server has stopped!"
echo "========================================"
exit 1