-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatlantis-deploy
More file actions
executable file
·887 lines (750 loc) · 28.6 KB
/
atlantis-deploy
File metadata and controls
executable file
·887 lines (750 loc) · 28.6 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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
#!/bin/bash
# /usr/local/bin/atlantis-deploy
set -euo pipefail
# ============================================================================
# ATLANTIS SYSTEM-WIDE DEPLOYMENT SCRIPT
# Location: /usr/local/bin/atlantis-deploy
# Usage: atlantis-deploy <environment> <workspace> [plan|apply]
# ============================================================================
# Global exit code variable
EXIT_CODE=0
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
BOLD='\033[1m'
NC='\033[0m'
# Logging functions with proper exit code handling
log_info() { echo -e "${BLUE}ℹ️ $1${NC}"; }
log_success() { echo -e "${GREEN}✅ $1${NC}"; }
log_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; }
log_error() {
echo -e "${RED}❌ $1${NC}" >&2
EXIT_CODE=1
}
# Script metadata
SCRIPT_VERSION="2.3.0"
SCRIPT_NAME="atlantis-deploy"
SCRIPT_AUTHOR="devopscorner"
SCRIPT_EMAIL="support@devopscorner.id"
SCRIPT_COMPANY="DevOpsCorner Indonesia"
SCRIPT_LICENSE="Apache v2"
SCRIPT_DATE="June, 2025"
SCRIPT_PATTERN="DCI"
SCRIPT_GH_USER="devopscorner"
SCRIPT_GH_REPO="devopscorner"
SCRIPT_RUN_TEST="${SCRIPT_RUN_TEST:-false}" # Default to false if not set
# Display logo and version
show_logo() {
cat << 'EOF'
__ .__ __ .__
_____ _/ |_| | _____ _____/ |_|__| ______
\__ \\ __\ | \__ \ / \ __\ |/ ___/
/ __ \| | | |__/ __ \| | \ | | |\___ \
(____ /__| |____(____ /___| /__| |__/____ >
\/ \/ \/ \/
.___ .__
__| _/____ ______ | | ____ ___.__.
/ __ |/ __ \\____ \| | / _ < | |
/ /_/ \ ___/| |_> > |_( <_> )___ |
\____ |\___ > __/|____/\____// ____|
\/ \/|__| \/
EOF
echo "================================================================"
echo " Atlantis Schema Deploy - Infrastructure as Code"
echo "================================================================"
echo "Version : $SCRIPT_VERSION"
echo "Author : $SCRIPT_AUTHOR"
echo "Company : $SCRIPT_COMPANY"
echo "Build : $SCRIPT_DATE"
echo "License : $SCRIPT_LICENSE"
echo "================================================================"
echo ""
}
# Show version info
show_version() {
show_logo
echo -e "${GREEN}$SCRIPT_NAME version $SCRIPT_VERSION${NC}"
echo -e "${BLUE}Build for Terraform Infrastructure Automation${NC}"
echo -e "${BLUE}Supports environments: dev01-05, uat01-05, prod01-05${NC}"
echo -e "${BLUE}Supports workspaces: default, custom workspace names${NC}"
echo ""
exit 0
}
# Show help
show_help() {
show_logo
cat << EOF
USAGE:
$SCRIPT_NAME <environment> <workspace> [operation]
ARGUMENTS:
environment Target environment (dev01-dev05, uat01-uat05, prod01-05)
workspace Terraform workspace name (default, custom names)
operation Operation to perform (plan, apply) [default: plan]
EXAMPLES:
$SCRIPT_NAME dev01 default plan # Plan deployment for dev01 with default workspace
$SCRIPT_NAME uat02 staging apply # Apply deployment for uat02 with staging workspace
$SCRIPT_NAME dev01 feature-123 # Plan deployment for dev01 with feature-123 workspace
$SCRIPT_NAME prod01 production apply # Apply deployment for prod01 with production workspace
ENVIRONMENT VARIABLES:
GITHUB_TOKEN Required: GitHub Personal Access Token
GIT_USER_NAME Git commit author name (default: $SCRIPT_AUTHOR)
GIT_USER_EMAIL Git commit author email (default: $SCRIPT_EMAIL)
GITHUB_USERNAME GitHub username for authentication (default: $SCRIPT_GH_USER)
ATLANTIS_CONFIG_PATH Config path (default: /atlantis/config)
ATLANTIS_WORKSPACE Current workspace directory
ATLANTIS_PROJECT_NAME Current project name
ATLANTIS_PULL_REQUEST_HEAD_BRANCH Current branch name
SUPPORTED ENVIRONMENTS:
DEV environments: dev01, dev02, dev03, dev04, dev05
UAT environments: uat01, uat02, uat03, uat04, uat05
PROD environments: prod01, prod02, prod03, prod04, prod05
SUPPORTED WORKSPACES:
Common workspaces: default, staging, production, development
Feature branches: feature-*, bugfix-*, hotfix-*
Custom workspaces: Any valid terraform workspace name
OPTIONS:
-h, --help Show this help message
-v, --version Show version information
EOF
exit 0
}
# Safe function execution wrapper
safe_execute() {
local func_name="$1"
local error_msg="$2"
if ! "$func_name"; then
log_error "$error_msg"
return 1
fi
return 0
}
# Parse command line arguments
parse_arguments() {
case ${1:-} in
-h|--help)
show_help
;;
-v|--version)
show_version
;;
"")
show_logo
log_error "Environment and workspace arguments are required"
echo ""
echo -e "${YELLOW}Usage: $SCRIPT_NAME <environment> <workspace> [operation]${NC}"
echo -e "${YELLOW}Try '$SCRIPT_NAME --help' for more information.${NC}"
return 1
;;
*)
ENVIRONMENT="$1"
WORKSPACE="${2:-}"
OPERATION="${3:-plan}"
# Validate we have at least environment and workspace
if [ -z "$WORKSPACE" ]; then
show_logo
log_error "Workspace argument is required"
echo ""
echo -e "${YELLOW}Usage: $SCRIPT_NAME <environment> <workspace> [operation]${NC}"
echo -e "${YELLOW}Example: $SCRIPT_NAME dev01 default plan${NC}"
echo -e "${YELLOW}Try '$SCRIPT_NAME --help' for more information.${NC}"
return 1
fi
return 0
;;
esac
}
# Validate environment
validate_environment() {
case $ENVIRONMENT in
dev01|dev02|dev03|dev04|dev05|uat01|uat02|uat03|uat04|uat05|prod01|prod02|prod03|prod04|prod05)
log_info "Processing $OPERATION for environment: $ENVIRONMENT, workspace: $WORKSPACE"
return 0
;;
*)
log_error "Unsupported environment: $ENVIRONMENT"
echo "Supported environments: dev01-05, uat01-05, prod01-05"
return 1
;;
esac
}
# Validate workspace name
validate_workspace() {
# Check if workspace name contains only valid characters
if [[ ! "$WORKSPACE" =~ ^[a-zA-Z0-9_-]+$ ]]; then
log_error "Invalid workspace name: $WORKSPACE"
echo "Workspace names can only contain letters, numbers, hyphens, and underscores"
return 1
fi
# Check workspace name length (Terraform has limits)
if [ ${#WORKSPACE} -gt 90 ]; then
log_error "Workspace name too long: $WORKSPACE (max 90 characters)"
return 1
fi
log_info "Workspace name validated: $WORKSPACE"
return 0
}
# Validate operation
validate_operation() {
case $OPERATION in
plan|apply)
log_info "Operation validated: $OPERATION"
return 0
;;
*)
log_error "Unsupported operation: $OPERATION"
echo "Supported operations: plan, apply"
return 1
;;
esac
}
# Set environment-specific variables
setup_environment_variables() {
# Environment type and number
ENV_TYPE=$(echo $ENVIRONMENT | sed 's/[0-9]*$//')
ENV_NUMBER=$(echo $ENVIRONMENT | sed 's/^[a-z]*//')
# AWS Profile
AWS_PROFILE="$SCRIPT_PATTERN-TF-User-Executor-${ENVIRONMENT^^}"
# Configuration paths
CONFIG_BASE_PATH="${ATLANTIS_CONFIG_PATH:-/atlantis/config}"
CONFIG_PATH="${CONFIG_BASE_PATH}/${ENVIRONMENT}"
# Git configuration
GIT_USER_NAME="${GIT_USER_NAME:-devopscorner}" # Display name for commits
GIT_USER_EMAIL="${GIT_USER_EMAIL:-support@devopscorner.id}" # Email for commits
GITHUB_USERNAME="${GITHUB_USERNAME:-devopscorner}" # Actual GitHub username for auth
log_info "Environment Type: $ENV_TYPE, Number: $ENV_NUMBER"
log_info "AWS Profile: $AWS_PROFILE"
log_info "Config Path: $CONFIG_PATH"
log_info "Terraform Workspace: $WORKSPACE"
return 0
}
# Validate prerequisites
validate_prerequisites() {
log_info "Validating prerequisites..."
# Check required commands
local required_commands=("git" "aws" "terraform")
for cmd in "${required_commands[@]}"; do
if ! command -v "$cmd" &> /dev/null; then
log_error "Required command not found: $cmd"
return 1
fi
done
# Check GitHub token
if [ -z "${GITHUB_TOKEN:-}" ]; then
log_error "GITHUB_TOKEN environment variable is required"
return 1
fi
# Check configuration directory
if [ ! -d "$CONFIG_PATH" ]; then
log_error "Configuration directory not found: $CONFIG_PATH"
return 1
fi
log_success "Prerequisites validated"
return 0
}
# Fix Git configuration permissions
fix_git_permissions() {
local git_config_file="$HOME/.gitconfig"
local git_config_dir="$(dirname "$git_config_file")"
local git_config_global_dir="$HOME/.config/git"
log_info "Checking Git configuration permissions"
log_info "Current user: $(whoami) ($(id))"
log_info "Home directory: $HOME"
# Ensure the home directory exists and has proper permissions
if [ ! -d "$HOME" ]; then
log_error "Home directory $HOME does not exist"
return 1
fi
# Check if we can write to home directory
if [ ! -w "$HOME" ]; then
log_error "Cannot write to home directory: $HOME"
log_error "Directory permissions: $(ls -ld "$HOME")"
return 1
fi
# Remove existing .gitconfig if it has wrong permissions
if [ -f "$git_config_file" ] && [ ! -w "$git_config_file" ]; then
log_warning "Removing non-writable .gitconfig file"
if ! rm -f "$git_config_file" 2>/dev/null; then
log_error "Cannot remove .gitconfig file with wrong permissions"
log_error "File permissions: $(ls -l "$git_config_file")"
# Force use local config
GIT_CONFIG_SCOPE="--local"
return 0
fi
fi
# Create new .gitconfig with proper permissions
if [ ! -f "$git_config_file" ]; then
if ! touch "$git_config_file" || ! chmod 644 "$git_config_file"; then
log_warning "Cannot create global git config, will use local only"
GIT_CONFIG_SCOPE="--local"
return 0
fi
fi
# Test if we can write to .gitconfig
if [ -w "$git_config_file" ]; then
GIT_CONFIG_SCOPE="--global"
log_success "Using global Git configuration"
else
log_warning "Cannot write to $git_config_file, will use local git config only"
GIT_CONFIG_SCOPE="--local"
fi
return 0
}
# Setup Git configuration
setup_git_config() {
log_info "Setting up Git configuration"
# Fix permissions first
if ! fix_git_permissions; then
log_error "Failed to fix git permissions"
return 1
fi
# Configure git with appropriate scope
if ! git config $GIT_CONFIG_SCOPE user.name "$GIT_USER_NAME" 2>/dev/null; then
log_warning "Failed to set global git config, trying local config"
if ! git config --local user.name "$GIT_USER_NAME" || \
! git config --local user.email "$GIT_USER_EMAIL" || \
! git config --local init.defaultBranch main || \
! git config --local pull.rebase false; then
log_error "Failed to configure git (local scope)"
return 1
fi
log_success "Git configuration completed (local scope)"
return 0
fi
if ! git config $GIT_CONFIG_SCOPE user.email "$GIT_USER_EMAIL" || \
! git config $GIT_CONFIG_SCOPE init.defaultBranch main || \
! git config $GIT_CONFIG_SCOPE pull.rebase false; then
log_error "Failed to configure git (global scope)"
return 1
fi
log_success "Git configuration completed (global scope)"
return 0
}
# Setup GitHub authentication
setup_github_auth() {
log_info "Configuring GitHub authentication"
# Configure GitHub authentication using URL rewriting
# Try global first, fall back to local if needed
if ! git config --global url."https://${GITHUB_USERNAME}:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" 2>/dev/null; then
log_warning "Failed to set global GitHub auth, using local config"
if ! git config --local url."https://${GITHUB_USERNAME}:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"; then
log_error "Failed to configure GitHub authentication"
return 1
fi
fi
log_success "GitHub authentication configured"
return 0
}
# Test GitHub access
test_github_access() {
log_info "Testing GitHub repository access"
local test_repo="$SCRIPT_GH_REPO/report_templates"
local timeout_duration=30
if timeout $timeout_duration git ls-remote --exit-code "https://github.com/${test_repo}.git" > /dev/null 2>&1; then
log_success "GitHub repository access confirmed"
return 0
else
log_error "Failed to access GitHub repository: $test_repo"
log_error "Check if $GITHUB_USERNAME has access to the repository"
return 1
fi
}
# Setup AWS credentials
setup_aws_credentials() {
log_info "Setting up AWS credentials for profile: $AWS_PROFILE"
# Clean existing credentials
unset AWS_SESSION_TOKEN AWS_SECRET_ACCESS_KEY AWS_ACCESS_KEY_ID AWS_SECURITY_TOKEN
# Set AWS profile and region
export AWS_PROFILE="$AWS_PROFILE"
# Verify AWS access
if aws sts get-caller-identity --output table > /dev/null 2>&1; then
local account_id=$(aws sts get-caller-identity --query Account --output text)
local caller_arn=$(aws sts get-caller-identity --query Arn --output text)
log_success "AWS authenticated successfully"
log_info "Account ID: $account_id"
log_info "Caller ARN: $(basename $caller_arn)"
return 0
else
log_error "AWS authentication failed for profile: $AWS_PROFILE"
return 1
fi
}
# Initialize Git submodules
initialize_submodules() {
log_info "Initializing Git submodules"
if git submodule update --init --recursive --progress; then
log_success "All submodules initialized successfully"
# Show submodule status
log_info "Submodule status:"
git submodule status --recursive
return 0
else
log_error "Submodule initialization failed"
log_info "Current submodule status:"
git submodule status --recursive || true
return 1
fi
}
# Setup Terraform configuration
setup_terraform_config() {
log_info "Setting up Terraform configuration for $ENVIRONMENT"
# Configuration file paths
local backend_tf="${CONFIG_PATH}/backend.tf"
local backend_tfvars="${CONFIG_PATH}/backend.tfvars"
local terraform_tfvars="${CONFIG_PATH}/${ENVIRONMENT}.tfvars"
# Check if configuration files exist
local files=("$backend_tf" "$backend_tfvars" "$terraform_tfvars")
for file in "${files[@]}"; do
if [ ! -f "$file" ]; then
log_error "Configuration file not found: $file"
return 1
fi
done
# Copy configuration files to current directory
if ! cp "$backend_tf" . || \
! cp "$backend_tfvars" . || \
! cp "$terraform_tfvars" .; then
log_error "Failed to copy configuration files"
return 1
fi
log_success "Configuration files copied:"
log_info "- backend.tf"
log_info "- backend.tfvars"
log_info "- ${ENVIRONMENT}.tfvars"
return 0
}
# Run initialization script
run_initialization_script() {
log_info "Running environment initialization scripts"
local current_dir=$(pwd)
local init_script="./init.sh"
log_info "Current working directory: $current_dir"
# Check if main init.sh exists in current directory
if [ -f "$init_script" ]; then
log_info "Found init.sh in current directory"
# Check if script is executable, make it executable if needed
if [ ! -x "$init_script" ]; then
log_info "Making init.sh executable"
if ! chmod +x "$init_script"; then
log_error "Failed to make init.sh executable"
return 1
fi
fi
# Execute the script
log_info "Executing init.sh"
if bash "$init_script"; then
log_success "Successfully executed init.sh"
return 0
else
log_error "Failed to execute init.sh"
return 1
fi
else
log_warning "init.sh not found in current directory: $current_dir"
log_info "Checking if this is expected for this project structure..."
# List contents to help with debugging
log_info "Directory contents:"
ls -la . || true
return 0 # Not finding init.sh is not necessarily an error
fi
}
# Initialize Terraform
initialize_terraform() {
log_info "Initializing Terraform"
# Check Terraform version
local current_version=$(terraform version -json | jq -r '.terraform_version' 2>/dev/null || echo "unknown")
log_info "Terraform version: $current_version"
# Initialize Terraform
if terraform init -backend-config=backend.tfvars -upgrade -input=false; then
log_success "Terraform initialized successfully"
else
log_error "Terraform initialization failed"
return 1
fi
# Handle workspace operations
manage_terraform_workspace
return 0
}
# Manage Terraform workspace
manage_terraform_workspace() {
log_info "Managing Terraform workspace: $WORKSPACE"
# Get current workspace
local current_workspace=$(terraform workspace show 2>/dev/null || echo "unknown")
log_info "Current workspace: $current_workspace"
# If we're already in the correct workspace, no action needed
if [ "$current_workspace" = "$WORKSPACE" ]; then
log_success "Already in workspace: $WORKSPACE"
return 0
fi
# Check if workspace exists
local workspace_exists=false
if terraform workspace list | grep -q "^\s*$WORKSPACE\s*$"; then
workspace_exists=true
fi
# Create workspace if it doesn't exist
if [ "$workspace_exists" = false ]; then
log_info "Creating new workspace: $WORKSPACE"
if terraform workspace new "$WORKSPACE"; then
log_success "Workspace created: $WORKSPACE"
else
log_error "Failed to create workspace: $WORKSPACE"
return 1
fi
else
# Select existing workspace
log_info "Selecting existing workspace: $WORKSPACE"
if terraform workspace select "$WORKSPACE"; then
log_success "Workspace selected: $WORKSPACE"
else
log_error "Failed to select workspace: $WORKSPACE"
return 1
fi
fi
# Verify we're in the correct workspace
local new_current_workspace=$(terraform workspace show)
if [ "$new_current_workspace" = "$WORKSPACE" ]; then
log_success "Successfully switched to workspace: $WORKSPACE"
return 0
else
log_error "Workspace verification failed. Expected: $WORKSPACE, Current: $new_current_workspace"
return 1
fi
}
# Run application tests
run_application_tests() {
log_info "Running application test suites"
local test_results=()
local overall_result=0
# Define test suites with their paths and descriptions
local test_suites=(
"unit_test:./tests/index.sh:Unit Test"
)
log_info "Found ${#test_suites[@]} test suites to execute"
echo ""
# Execute each test suite
for suite in "${test_suites[@]}"; do
IFS=':' read -r name path description <<< "$suite"
log_info "============================================================"
log_info "Executing: $description"
log_info "Test path: $path"
log_info "============================================================"
# Check if test script exists
if [ ! -f "$path" ]; then
log_warning "Test script not found: $path"
log_warning "Skipping $description"
test_results+=("$name:SKIPPED:Test script not found")
continue
fi
# Check if test script is executable
if [ ! -x "$path" ]; then
log_info "Making test script executable: $path"
if ! chmod +x "$path"; then
log_error "Failed to make test script executable: $path"
test_results+=("$name:FAILED:Cannot make executable")
overall_result=1
continue
fi
fi
# Execute the test suite with timeout
local test_timeout=300 # 5 minutes timeout per test suite
local test_start_time=$(date +%s)
if timeout $test_timeout bash "$path"; then
local test_end_time=$(date +%s)
local test_duration=$((test_end_time - test_start_time))
log_success "$description completed successfully (${test_duration}s)"
test_results+=("$name:PASSED:${test_duration}s")
else
local exit_code=$?
local test_end_time=$(date +%s)
local test_duration=$((test_end_time - test_start_time))
if [ $exit_code -eq 124 ]; then
log_error "$description timed out after ${test_timeout}s"
test_results+=("$name:TIMEOUT:${test_timeout}s")
else
log_error "$description failed with exit code: $exit_code (${test_duration}s)"
test_results+=("$name:FAILED:Exit code $exit_code")
fi
overall_result=1
fi
echo ""
done
# Display test summary
log_info "============================================================"
log_info "TEST EXECUTION SUMMARY"
log_info "============================================================"
local passed_count=0
local failed_count=0
local skipped_count=0
local timeout_count=0
for result in "${test_results[@]}"; do
IFS=':' read -r name status details <<< "$result"
case $status in
"PASSED")
log_success "✅ $name: PASSED ($details)"
((passed_count++))
;;
"FAILED")
log_error "❌ $name: FAILED ($details)"
((failed_count++))
;;
"SKIPPED")
log_warning "⚠️ $name: SKIPPED ($details)"
((skipped_count++))
;;
"TIMEOUT")
log_error "⏰ $name: TIMEOUT ($details)"
((timeout_count++))
;;
esac
done
echo ""
log_info "Test Results: ${passed_count} passed, ${failed_count} failed, ${skipped_count} skipped, ${timeout_count} timeout"
# Determine if we should continue based on test results
if [ $overall_result -eq 0 ]; then
log_success "All available tests passed successfully"
return 0
else
# Check if we should fail hard or continue with warnings
if [ $failed_count -gt 0 ] || [ $timeout_count -gt 0 ]; then
log_error "Test failures detected - stopping deployment"
log_error "Fix failing tests before proceeding with deployment"
return 1
else
log_warning "Some tests were skipped, but no failures detected"
log_warning "Proceeding with deployment..."
return 0
fi
fi
}
# Create Terraform plan
terraform_plan() {
log_info "Creating Terraform plan for $ENVIRONMENT in workspace $WORKSPACE"
local plan_file="${ENVIRONMENT}-${WORKSPACE}-plan.out"
if terraform plan -var-file="${ENVIRONMENT}.tfvars" -out="$plan_file" -input=false; then
log_success "Terraform plan created successfully: $plan_file"
# Show plan summary
log_info "Plan summary:"
terraform show -no-color "$plan_file" | head -20
return 0
else
log_error "Terraform plan creation failed"
return 1
fi
}
# Apply Terraform
terraform_apply() {
log_info "Applying Terraform for $ENVIRONMENT in workspace $WORKSPACE"
if terraform apply -var-file="${ENVIRONMENT}.tfvars" -auto-approve -input=false; then
log_success "Terraform apply completed successfully"
return 0
else
log_error "Terraform apply failed"
return 1
fi
}
# Cleanup sensitive files
cleanup_sensitive_files() {
log_info "Cleaning up sensitive files"
# Remove credentials file if it exists
[ -f ~/.git-credentials ] && rm -f ~/.git-credentials
# Clear Git credential cache (ignore errors)
git config --global --unset credential.helper 2>/dev/null || true
git config --local --unset credential.helper 2>/dev/null || true
log_success "Cleanup completed"
return 0
}
# Main execution function with proper error handling
main() {
show_logo
log_info "Starting Atlantis $OPERATION for environment: $ENVIRONMENT, workspace: $WORKSPACE"
log_info "============================================================"
# Validation phase
safe_execute validate_workspace "Invalid workspace name" || return 1
safe_execute validate_operation "Invalid operation" || return 1
# Setup phase - each function properly handles errors
safe_execute validate_prerequisites "Failed to validate prerequisites" || return 1
safe_execute setup_environment_variables "Failed to setup environment variables" || return 1
safe_execute setup_git_config "Failed to setup git configuration" || return 1
safe_execute setup_github_auth "Failed to setup GitHub authentication" || return 1
safe_execute test_github_access "Failed to test GitHub access" || return 1
safe_execute setup_aws_credentials "Failed to setup AWS credentials" || return 1
# Repository initialization
safe_execute initialize_submodules "Failed to initialize submodules" || return 1
safe_execute setup_terraform_config "Failed to setup Terraform configuration" || return 1
# Init submodules
safe_execute run_initialization_script "Failed to run initialization script" || return 1
# Terraform operations
safe_execute initialize_terraform "Failed to initialize Terraform" || return 1
# Conditionally run tests based on SCRIPT_RUN_TEST variable
if [[ "$SCRIPT_RUN_TEST" == "true" ]]; then
log_info "============================================================"
log_info "EXECUTING APPLICATION TESTS"
log_info "============================================================"
safe_execute run_application_tests "Application tests failed" || return 1
else
log_info "============================================================"
log_info "SKIPPING APPLICATION TESTS"
log_info "============================================================"
log_warning "Test execution is disabled"
log_info "To enable tests, set: SCRIPT_RUN_TEST=true"
echo ""
fi
case $OPERATION in
"plan")
safe_execute terraform_plan "Terraform plan failed" || return 1
;;
"apply")
safe_execute terraform_apply "Terraform apply failed" || return 1
;;
*)
log_error "Unsupported operation: $OPERATION"
echo "Supported operations: plan, apply"
return 1
;;
esac
# Cleanup (always run, ignore errors)
cleanup_sensitive_files || true
log_success "============================================================"
log_success "Atlantis $OPERATION completed successfully for $ENVIRONMENT in workspace $WORKSPACE!"
echo ""
echo -e "${CYAN}${BOLD}Thank you for using Atlantis Schema Deploy${NC}"
echo -e "${BLUE}© $SCRIPT_DATE $SCRIPT_COMPANY${NC}"
return 0
}
# Cleanup function for trap
cleanup_on_exit() {
cleanup_sensitive_files || true
if [ $EXIT_CODE -eq 0 ]; then
log_success "Script completed successfully"
else
log_error "Script failed with exit code: $EXIT_CODE"
fi
exit $EXIT_CODE
}
# ==============================================================================
# MAIN SCRIPT EXECUTION
# ==============================================================================
# Parse arguments first
if ! parse_arguments "$@"; then
EXIT_CODE=1
exit $EXIT_CODE
fi
# Validate environment
if ! validate_environment; then
EXIT_CODE=1
exit $EXIT_CODE
fi
# Set trap for cleanup on exit
trap cleanup_on_exit EXIT INT TERM
# Execute main function and capture result
if main; then
EXIT_CODE=0
log_success "Main execution completed successfully"
else
EXIT_CODE=1
log_error "Main execution failed"
fi
# The trap will handle the final exit with the correct code