1+ #! /bin/bash
2+ set -o errexit
3+ set -o nounset
4+ set -o pipefail
5+
6+ set -x
7+
8+ # For disconnected or otherwise unreachable environments, we want to
9+ # have steps use an HTTP(S) proxy to reach the API server. This proxy
10+ # configuration file should export HTTP_PROXY, HTTPS_PROXY, and NO_PROXY
11+ # environment variables, as well as their lowercase equivalents (note
12+ # that libcurl doesn't recognize the uppercase variables).
13+ if test -f " ${SHARED_DIR} /proxy-conf.sh"
14+ then
15+ # shellcheck disable=SC1091
16+ source " ${SHARED_DIR} /proxy-conf.sh"
17+ fi
18+
19+ # logger function prints standard logs
20+ logger () {
21+ local level=" $1 "
22+ local message=" $2 "
23+ local timestamp
24+
25+ # Generate a timestamp for the log entry
26+ timestamp=$( date +" %Y-%m-%d %H:%M:%S" )
27+
28+ # Print the log message with the level and timestamp
29+ echo " [$timestamp ] [$level ] $message "
30+ }
31+
32+ switch_aws_credentials () {
33+ local mode=" $1 "
34+ if [[ " $mode " == " shared" ]]; then
35+ export AWS_SHARED_CREDENTIALS_FILE=" ${CLUSTER_PROFILE_DIR} /.awscred_shared_account"
36+ logger " INFO" " Using shared AWS account(B)."
37+ else
38+ export AWS_SHARED_CREDENTIALS_FILE=" ${CLUSTER_PROFILE_DIR} /.awscred"
39+ logger " INFO" " Using default AWS account(A)."
40+ fi
41+ }
42+
43+ REGION=" ${REGION:- $LEASED_RESOURCE } "
44+ if [ -z " $REGION " ]; then
45+ logger " Error" " REGION is not set and LEASED_RESOURCE is empty."
46+ exit 1
47+ fi
48+
49+ # Export for AWS CLI
50+ export AWS_DEFAULT_REGION=" $REGION "
51+ echo " Using AWS region: $AWS_DEFAULT_REGION "
52+
53+ switch_aws_credentials default
54+ AWS_ACCOUNT_A_ARN=$( aws sts get-caller-identity | jq -r ' .Arn' )
55+ AWS_ACCOUNT_A_ID=$( echo " $AWS_ACCOUNT_A_ARN " | awk -F " :" ' {print $5}' ) || return 1
56+ export AWS_ACCOUNT_A_ID
57+ INSTANCE_ID=$( oc get nodes -o jsonpath=' {.items[0].spec.providerID}' | cut -d' /' -f5)
58+ AWS_ACCOUNT_A_VPC_ID=$( aws ec2 describe-instances --instance-ids " $INSTANCE_ID " \
59+ --output json | jq -r ' .Reservations[0].Instances[0].VpcId' )
60+ AWS_ACCOUNT_A_VPC_CIDR=$( aws ec2 describe-vpcs \
61+ --vpc-ids " $AWS_ACCOUNT_A_VPC_ID " \
62+ --output json | jq -r ' .Vpcs[0].CidrBlock' )
63+ switch_aws_credentials shared
64+ AWS_ACCOUNT_B_ARN=$( aws sts get-caller-identity | jq -r ' .Arn' )
65+ AWS_ACCOUNT_B_ID=$( echo " $AWS_ACCOUNT_B_ARN " | awk -F " :" ' {print $5}' ) || return 1
66+ export AWS_ACCOUNT_B_ID
67+ CLUSTER_NAME=" $( jq -r .clusterName " ${SHARED_DIR} /metadata.json" ) "
68+
69+
70+ # Get the VPC ID from the shared account
71+ AWS_ACCOUNT_B_VPC_ID=$( cat " ${SHARED_DIR} /vpc_id" )
72+
73+ # Creating a region-wide EFS filesystem in Account B
74+ ACROSS_ACCOUNT_FS_ID=$( aws efs create-file-system --creation-token ci-cross-account-token \
75+ --region " ${AWS_DEFAULT_REGION} " \
76+ --encrypted | jq -r ' .FileSystemId' )
77+ logger " INFO" " Created efs volume FileSystemId:$ACROSS_ACCOUNT_FS_ID "
78+
79+ # Prepare the security groups on Account B to allow NFS traffic to EFS
80+ AWS_ACCOUNT_B_VPC_CIDR=$( aws ec2 describe-vpcs \
81+ --vpc-ids " $AWS_ACCOUNT_B_VPC_ID " \
82+ --output json | jq -r ' .Vpcs[0].CidrBlock' )
83+ SECURITY_GROUP_ID=$( aws ec2 describe-security-groups --filters Name=vpc-id,Values=" $AWS_ACCOUNT_B_VPC_ID " | jq -r ' .SecurityGroups[].GroupId' )
84+ aws ec2 authorize-security-group-ingress \
85+ --group-id " $SECURITY_GROUP_ID " \
86+ --protocol tcp \
87+ --port 2049 \
88+ --cidr " $AWS_ACCOUNT_A_VPC_CIDR " | jq .
89+
90+ # Configure a region-wide Mount Target for EFS (this will create a mount point in each subnet of your VPC by default)
91+ aws efs create-mount-target --file-system-id " $ACROSS_ACCOUNT_FS_ID " \
92+ --subnet-id " ${AWS_ACCOUNT_B_VPC_ID} " \
93+ --security-groups " ${SECURITY_GROUP_ID} " \
94+ --region " ${AWS_DEFAULT_REGION} " \
95+ --tag-specifications " ResourceType=mount-target,Tags=[{Key=Name,Value=ci-cross-account-mount-target}]" \
96+ --client-token ci-cross-account-token
97+ AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS=$( tr -d " [],'" < " ${SHARED_DIR} /private_subnet_ids" )
98+ for SUBNET in $AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS ; do \
99+ MOUNT_TARGET=$( aws efs create-mount-target --file-system-id " $ACROSS_ACCOUNT_FS_ID " \
100+ --subnet-id " $SUBNET " \
101+ --region " $AWS_DEFAULT_REGION " \
102+ | jq -r ' .MountTargetId' ) ; \
103+ logger " INFO" " Created $MOUNT_TARGET for $SUBNET " ; \
104+ done
105+
106+ # Create VPC peering between the Red Hat OpenShift cluster VPC in AWS account A and the AWS EFS VPC in AWS account B
107+ PEER_REQUEST_ID=$( aws ec2 create-vpc-peering-connection \
108+ --vpc-id " ${ACCOUNT_B_VPC_ID} " \
109+ --peer-vpc-id " ${ACCOUNT_A_VPC_ID} " \
110+ --peer-owner-id " ${AWS_ACCOUNT_A_ID} " \
111+ --output json | jq -r ' .VpcPeeringConnection.VpcPeeringConnectionId' )
112+
113+ switch_aws_credentials default
114+ aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
115+ SUBNETS=$( aws ec2 describe-subnets \
116+ --filters Name=vpc-id,Values=" ${AWS_ACCOUNT_A_VPC_ID} " \
117+ | jq -r ' .Subnets[].SubnetId' )
118+
119+ for subnet in $SUBNETS ; do
120+ # Get route table associated with this subnet (specific or main)
121+ RTB=$( aws ec2 describe-route-tables \
122+ --filters Name=association.subnet-id,Values=" $subnet " \
123+ | jq -r ' .RouteTables[0].RouteTableId' )
124+
125+ if [ " $RTB " == " null" ]; then
126+ # No subnet-specific route table, fall back to main route table
127+ RTB=$( aws ec2 describe-route-tables \
128+ --filters Name=vpc-id,Values=" ${AWS_ACCOUNT_A_VPC_ID} " Name=association.main,Values=true \
129+ | jq -r ' .RouteTables[0].RouteTableId' )
130+ fi
131+
132+ # Check if the route table has a route to an Internet Gateway
133+ HAS_IGW=$( aws ec2 describe-route-tables --route-table-ids " $RTB " \
134+ | jq -e ' .RouteTables[0].Routes[] | select(.GatewayId | startswith("igw-"))' > /dev/null && echo " yes" || echo " no" )
135+
136+ # If no IGW, it's private
137+ if [ " $HAS_IGW " == " no" ]; then
138+ ROUTE_TABLE_ID=$( aws ec2 describe-route-tables --filters " Name=association.subnet-id,Values=${SUBNET} " --query ' RouteTables[*].RouteTableId' | jq -r ' .[0]' )
139+ aws ec2 create-route --route-table-id " ${ROUTE_TABLE_ID} " --destination-cidr-block " ${AWS_ACCOUNT_B_VPC_CIDR} " --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
140+ logger " INFO" " Created route for $SUBNET to peering-connection in account A"
141+ fi
142+ done
143+
144+ switch_aws_credentials shared
145+ for SUBNET in $AWS_ACCOUNT_B_PRIVATE_SUBNET_IDS ; do
146+ ROUTE_TABLE_ID=$( aws ec2 describe-route-tables --filters " Name=association.subnet-id,Values=${SUBNET} " --query ' RouteTables[*].RouteTableId' | jq -r ' .[0]' )
147+ aws ec2 create-route --route-table-id " ${ROUTE_TABLE_ID} " --destination-cidr-block " ${AWS_ACCOUNT_A_VPC_CIDR} " --vpc-peering-connection-id " ${PEER_REQUEST_ID} "
148+ logger " INFO" " Created route for $SUBNET to peering-connection in account B"
149+ done
150+
151+ sleep 6h
152+
153+ cat << EOF > "${SHARED_DIR} "/EfsPolicyInAccountB.json
154+ {
155+ "Version": "2012-10-17",
156+ "Statement": [
157+ {
158+ "Sid": "VisualEditor0",
159+ "Effect": "Allow",
160+ "Action": [
161+ "ec2:DescribeNetworkInterfaces",
162+ "ec2:DescribeSubnets"
163+ ],
164+ "Resource": "*"
165+ },
166+ {
167+ "Sid": "VisualEditor1",
168+ "Effect": "Allow",
169+ "Action": [
170+ "elasticfilesystem:DescribeMountTargets",
171+ "elasticfilesystem:DeleteAccessPoint",
172+ "elasticfilesystem:ClientMount",
173+ "elasticfilesystem:DescribeAccessPoints",
174+ "elasticfilesystem:ClientWrite",
175+ "elasticfilesystem:ClientRootAccess",
176+ "elasticfilesystem:DescribeFileSystems",
177+ "elasticfilesystem:CreateAccessPoint"
178+ ],
179+ "Resource": [
180+ "arn:aws:elasticfilesystem:*:${AWS_ACCOUNT_B_ID} :access-point/*",
181+ "arn:aws:elasticfilesystem:*:${AWS_ACCOUNT_B_ID} :file-system/*"
182+ ]
183+ }
184+ ]
185+ }
186+ EOF
187+
188+ cat << EOF > "${SHARED_DIR} "/TrustPolicyInAccountB.json
189+ {
190+ "Version": "2012-10-17",
191+ "Statement": [
192+ {
193+ "Effect": "Allow",
194+ "Principal": {
195+ "AWS": "arn:aws:iam::${AWS_ACCOUNT_A_ID} :root"
196+ },
197+ "Action": "sts:AssumeRole",
198+ "Condition": {}
199+ }
200+ ]
201+ }
202+ EOF
203+
204+ ACCOUNT_B_POLICY=$( aws iam create-policy --policy-name " ${CLUSTER_NAME} -efs-csi" \
205+ --policy-document file://" ${SHARED_DIR} " /efs-policy.json \
206+ --query ' Policy.Arn' --output text) || \
207+ logger " INFO" " Created efs policy $ACCOUNT_B_POLICY in account B"
208+
209+ # Create Role for the EFS CSI Driver Operator
210+ ACCOUNT_B_ROLE_ARN=$( aws iam create-role \
211+ --role-name " ${CLUSTER_NAME} -aws-efs-csi-operator" \
212+ --assume-role-policy-document file://" ${SHARED_DIR} " /TrustPolicy.json \
213+ --query " Role.Arn" --output text)
214+ logger " INFO" " Created efs csi driver role $ACCOUNT_B_ROLE_ARN in account B"
215+
216+ aws iam attach-role-policy \
217+ --role-name " ${CLUSTER_NAME} -aws-efs-csi-operator" \
218+ --policy-arn " ${ACCOUNT_B_POLICY} "
219+ logger " INFO" " Attach the Policies to the Role in account B"
0 commit comments