1
+ #! /bin/bash
2
+ set -eux
3
+
4
+ # Input parameters with defaults:
5
+ # Default to 1 (Git storage) if no parameter is passed.
6
+ is_s3_storage=${1:- " 1" }
7
+ # Output file path for unreachable services JSON
8
+ network_validation_file=${2:- " /tmp/.network_validation.json" }
9
+
10
+ # Function to write unreachable services to a JSON file
11
+ write_unreachable_services_to_file () {
12
+ local value=" $1 "
13
+ local file=" $network_validation_file "
14
+
15
+ # Create the file if it doesn't exist
16
+ if [ ! -f " $file " ]; then
17
+ touch " $file " || {
18
+ echo " Failed to create $file " >&2
19
+ return 0
20
+ }
21
+ fi
22
+
23
+ # Check file is writable
24
+ if [ ! -w " $file " ]; then
25
+ echo " Error: $file is not writable" >&2
26
+ return 0
27
+ fi
28
+
29
+ # Write JSON object with UnreachableServices key and the comma-separated list value
30
+ jq -n --arg value " $value " ' {"UnreachableServices": $value}' > " $file "
31
+ }
32
+
33
+ # Configure AWS CLI region using environment variable REGION_NAME
34
+ aws configure set region " ${REGION_NAME} "
35
+ echo " Successfully configured region to ${REGION_NAME} "
36
+
37
+ # Metadata file location containing DataZone info
38
+ sourceMetaData=/opt/ml/metadata/resource-metadata.json
39
+
40
+ # Extract necessary DataZone metadata fields via jq
41
+ dataZoneDomainId=$( jq -r ' .AdditionalMetadata.DataZoneDomainId' < " $sourceMetaData " )
42
+ dataZoneProjectId=$( jq -r ' .AdditionalMetadata.DataZoneProjectId' < " $sourceMetaData " )
43
+ dataZoneEndPoint=$( jq -r ' .AdditionalMetadata.DataZoneEndpoint' < " $sourceMetaData " )
44
+ dataZoneDomainRegion=$( jq -r ' .AdditionalMetadata.DataZoneDomainRegion' < " $sourceMetaData " )
45
+ s3Path=$( jq -r ' .AdditionalMetadata.ProjectS3Path' < " $sourceMetaData " )
46
+
47
+ # Extract bucket name, fallback to empty string if not found
48
+ s3ValidationBucket=$( echo " ${s3Path:- } " | sed -E ' s#s3://([^/]+).*#\1#' )
49
+
50
+ # Call AWS CLI list-connections, including endpoint if specified
51
+ if [ -n " $dataZoneEndPoint " ]; then
52
+ response=$( aws datazone list-connections \
53
+ --endpoint-url " $dataZoneEndPoint " \
54
+ --domain-identifier " $dataZoneDomainId " \
55
+ --project-identifier " $dataZoneProjectId " \
56
+ --region " $dataZoneDomainRegion " )
57
+ else
58
+ response=$( aws datazone list-connections \
59
+ --domain-identifier " $dataZoneDomainId " \
60
+ --project-identifier " $dataZoneProjectId " \
61
+ --region " $dataZoneDomainRegion " )
62
+ fi
63
+
64
+ # Extract each connection item as a compact JSON string
65
+ connection_items=$( echo " $response " | jq -c ' .items[]' )
66
+
67
+ # Required AWS Services for Compute connections and Git
68
+ # Initialize SERVICE_COMMANDS with always-needed STS and S3 checks
69
+ declare -A SERVICE_COMMANDS=(
70
+ [" STS" ]=" aws sts get-caller-identity"
71
+ [" S3" ]=" aws s3api list-objects --bucket \" $s3ValidationBucket \" --max-items 1"
72
+ )
73
+
74
+ # Track connection types found for conditional checks
75
+ declare -A seen_types=()
76
+
77
+ # Iterate over each connection to populate service commands conditionally
78
+ while IFS= read -r item; do
79
+ # Extract connection type
80
+ type=$( echo " $item " | jq -r ' .type' )
81
+ seen_types[" $type " ]=1
82
+
83
+ # For SPARK connections, check for Glue and EMR properties
84
+ if [[ " $type " == " SPARK" ]]; then
85
+ # If sparkGlueProperties present, add Glue check
86
+ if echo " $item " | jq -e ' .props.sparkGlueProperties' > /dev/null; then
87
+ SERVICE_COMMANDS[" Glue" ]=" aws glue get-databases --max-items 1"
88
+ fi
89
+
90
+ # Check for emr-serverless in sparkEmrProperties.computeArn for EMR Serverless check
91
+ emr_arn=$( echo " $item " | jq -r ' .props.sparkEmrProperties.computeArn // empty' )
92
+ if [[ " $emr_arn " == * " emr-serverless" * && " $emr_arn " == * " /applications/" * ]]; then
93
+ # Extract the application ID from the ARN
94
+ emr_app_id=$( echo " $emr_arn " | sed -E ' s#.*/applications/([^/]+)#\1#' )
95
+
96
+ # Only set the service command if the application ID is valid
97
+ if [[ -n " $emr_app_id " ]]; then
98
+ SERVICE_COMMANDS[" EMR Serverless" ]=" aws emr-serverless get-application --application-id \" $emr_app_id \" "
99
+ fi
100
+ fi
101
+ fi
102
+ done <<< " $connection_items"
103
+
104
+ # Add Athena if ATHENA connection found
105
+ [[ -n " ${seen_types["ATHENA"]} " ]] && SERVICE_COMMANDS[" Athena" ]=" aws athena list-data-catalogs --max-items 1"
106
+
107
+ # Add Redshift checks if REDSHIFT connection found
108
+ if [[ -n " ${seen_types["REDSHIFT"]} " ]]; then
109
+ SERVICE_COMMANDS[" Redshift Clusters" ]=" aws redshift describe-clusters --max-records 20"
110
+ SERVICE_COMMANDS[" Redshift Serverless" ]=" aws redshift-serverless list-namespaces --max-results 1"
111
+ fi
112
+
113
+ # If using Git Storage (S3 storage flag == 1), check CodeConnections connectivity
114
+ # Domain Execution role contains permissions for CodeConnections
115
+ if [[ " $is_s3_storage " == " 1" ]]; then
116
+ SERVICE_COMMANDS[" CodeConnections" ]=" aws codeconnections list-connections --max-results 1 --profile DomainExecutionRoleCreds"
117
+ fi
118
+
119
+ # Timeout (seconds) for each API call
120
+ api_time_out_limit=10
121
+ # Array to accumulate unreachable services
122
+ unreachable_services=()
123
+ # Create a temporary directory to store individual service results
124
+ temp_dir=$( mktemp -d)
125
+
126
+ # Launch all service API checks in parallel background jobs
127
+ for service in " ${! SERVICE_COMMANDS[@]} " ; do
128
+ {
129
+ # Run command with timeout, discard stdout/stderr
130
+ if timeout " ${api_time_out_limit} s" bash -c " ${SERVICE_COMMANDS[$service]} " > /dev/null 2>&1 ; then
131
+ # Success: write OK to temp file
132
+ echo " OK" > " $temp_dir /$service "
133
+ else
134
+ # Get exit code to differentiate timeout or other errors
135
+ exit_code=$?
136
+ if [ " $exit_code " -eq 124 ]; then
137
+ # Timeout exit code
138
+ echo " TIMEOUT" > " $temp_dir /$service "
139
+ else
140
+ # Other errors (e.g., permission denied)
141
+ echo " ERROR" > " $temp_dir /$service "
142
+ fi
143
+ fi
144
+ } &
145
+ done
146
+
147
+ # Wait for all background jobs to complete before continuing
148
+ wait
149
+
150
+ # Process each service's result file to identify unreachable ones
151
+ for service in " ${! SERVICE_COMMANDS[@]} " ; do
152
+ result_file=" $temp_dir /$service "
153
+ if [ -f " $result_file " ]; then
154
+ result=$( < " $result_file " )
155
+ if [[ " $result " == " TIMEOUT" ]]; then
156
+ echo " $service API did NOT resolve within ${api_time_out_limit} s. Marking as unreachable."
157
+ unreachable_services+=(" $service " )
158
+ elif [[ " $result " == " OK" ]]; then
159
+ echo " $service API is reachable."
160
+ else
161
+ echo " $service API returned an error (but not a timeout). Ignored for network check."
162
+ fi
163
+ else
164
+ echo " $service check did not produce a result file. Skipping."
165
+ fi
166
+ done
167
+
168
+ # Cleanup temporary directory
169
+ rm -rf " $temp_dir "
170
+
171
+ # Write unreachable services to file if any, else write empty string
172
+ if (( ${# unreachable_services[@]} > 0 )) ; then
173
+ joined_services=$( IFS=' ,' ; echo " ${unreachable_services[*]} " )
174
+ # Add spaces after commas for readability
175
+ joined_services_with_spaces=${joined_services// ,/ ,\ }
176
+ write_unreachable_services_to_file " $joined_services_with_spaces "
177
+ echo " Unreachable AWS Services: ${joined_services_with_spaces} "
178
+ else
179
+ write_unreachable_services_to_file " "
180
+ echo " All required AWS services reachable within ${api_time_out_limit} s"
181
+ fi
0 commit comments