Skip to content

Commit 03347a7

Browse files
committed
Draft: aws efs cross account workflow
1 parent b8d0736 commit 03347a7

12 files changed

+314
-8
lines changed

ci-operator/config/openshift/csi-operator/openshift-csi-operator-main.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,14 @@ tests:
224224
workflow: openshift-e2e-aws-csi-extended
225225
- as: aws-efs-operator-e2e
226226
steps:
227-
cluster_profile: aws-2
227+
cluster_profile: aws-qe
228228
dependencies:
229229
OO_INDEX: ci-index-aws-efs-csi-driver-operator-bundle
230230
env:
231231
OO_CHANNEL: stable
232232
TEST_SUITE: openshift/csi
233-
workflow: openshift-e2e-aws-csi-efs
233+
workflow: openshift-e2e-aws-csi-efs-cross-account
234+
timeout: 8h0m0s
234235
- as: aws-efs-operator-e2e-extended
235236
optional: true
236237
run_if_changed: ^(Dockerfile\.aws-efs|assets\/overlays\/aws-efs\/.*|pkg\/driver\/aws-efs\/.*|pkg\/operator\/.*)

ci-operator/config/openshift/openshift-tests-private/openshift-openshift-tests-private-release-4.19__amd64-nightly.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,17 @@ tests:
208208
test:
209209
- chain: openshift-e2e-test-qe-destructive
210210
workflow: cucushift-installer-rehearse-aws-ipi-disconnected-private-cco-manual-security-token-service-private-s3-with-ep-sts-ec2-elb
211-
- as: aws-ipi-efa-pg-mini-perm-f7
211+
- as: aws-ipi-efs-cross-account-f7
212212
cron: 36 16 2,9,16,23 * *
213213
steps:
214214
cluster_profile: aws-1-qe
215+
dependency_overrides:
216+
OO_INDEX: quay.io/openshift-qe-optional-operators/aosqe-index:v4.19
215217
env:
216-
AWS_INSTALL_USE_MINIMAL_PERMISSIONS: "yes"
217218
BASE_DOMAIN: qe.devcluster.openshift.com
218219
test:
219220
- chain: openshift-e2e-test-qe
220-
workflow: cucushift-installer-rehearse-aws-ipi-efa-pg
221+
workflow: openshift-e2e-aws-csi-efs-cross-account
221222
- as: aws-ipi-efa-pg-mini-perm-f28-destructive
222223
cron: 28 13 27 * *
223224
steps:

ci-operator/jobs/openshift/csi-operator/openshift-csi-operator-main-presubmits.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ presubmits:
88
cluster: build03
99
context: ci/prow/aws-efs-operator-e2e
1010
decorate: true
11+
decoration_config:
12+
timeout: 8h0m0s
1113
labels:
1214
ci-operator.openshift.io/cloud: aws
13-
ci-operator.openshift.io/cloud-cluster-profile: aws-2
15+
ci-operator.openshift.io/cloud-cluster-profile: aws-qe
1416
ci.openshift.io/generator: prowgen
1517
pj-rehearse.openshift.io/can-be-rehearsed: "true"
1618
name: pull-ci-openshift-csi-operator-main-aws-efs-operator-e2e

ci-operator/jobs/openshift/openshift-tests-private/openshift-openshift-tests-private-release-4.19-periodics.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17741,7 +17741,7 @@ periodics:
1774117741
ci.openshift.io/generator: prowgen
1774217742
job-release: "4.19"
1774317743
pj-rehearse.openshift.io/can-be-rehearsed: "true"
17744-
name: periodic-ci-openshift-openshift-tests-private-release-4.19-amd64-nightly-aws-ipi-efa-pg-mini-perm-f7
17744+
name: periodic-ci-openshift-openshift-tests-private-release-4.19-amd64-nightly-aws-ipi-efs-cross-account-f7
1774517745
spec:
1774617746
containers:
1774717747
- args:
@@ -17751,7 +17751,7 @@ periodics:
1775117751
- --oauth-token-path=/usr/local/github-credentials/oauth
1775217752
- --report-credentials-file=/etc/report/credentials
1775317753
- --secret-dir=/secrets/ci-pull-credentials
17754-
- --target=aws-ipi-efa-pg-mini-perm-f7
17754+
- --target=aws-ipi-efs-cross-account-f7
1775517755
- --variant=amd64-nightly
1775617756
command:
1775717757
- ci-operator
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../OWNERS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"path": "openshift/e2e/aws/csi/efs/cross-account/openshift-e2e-aws-csi-efs-cross-account-workflow.yaml",
3+
"owners": {
4+
"approvers": [
5+
"storage-approvers"
6+
],
7+
"reviewers": [
8+
"storage-reviewers"
9+
]
10+
}
11+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
workflow:
2+
as: openshift-e2e-aws-csi-efs-cross-account
3+
steps:
4+
pre:
5+
- chain: ipi-aws-pre
6+
- ref: aws-provision-vpc-shared
7+
- ref: storage-create-csi-shared-aws-efs
8+
- ref: optional-operators-subscribe
9+
- ref: storage-conf-wait-for-csi-driver
10+
- ref: storage-conf-storageclass-set-default-storageclass
11+
test:
12+
- ref: openshift-e2e-test
13+
post:
14+
- chain: ipi-aws-post
15+
- ref: storage-destroy-csi-aws-efs
16+
- ref: aws-deprovision-stacks
17+
env:
18+
ENABLE_SHARED_VPC: "yes"
19+
TEST_SUITE: openshift/csi
20+
TEST_CSI_DRIVER_MANIFEST: manifest-aws-efs.yaml
21+
TEST_OCP_CSI_DRIVER_MANIFEST: ocp-manifest-aws-efs.yaml
22+
VPC_CIDR: 172.20.0.0/16
23+
REQUIRED_DEFAULT_STORAGECLASS: efs-sc
24+
CLUSTERCSIDRIVER: efs.csi.aws.com
25+
OO_PACKAGE: aws-efs-csi-driver-operator
26+
OO_CHANNEL: stable
27+
OO_INSTALL_NAMESPACE: openshift-cluster-csi-drivers
28+
OO_TARGET_NAMESPACES: '!all'
29+
TRUECONDITIONS: AWSEFSDriverControllerServiceControllerAvailable AWSEFSDriverNodeServiceControllerAvailable
30+
31+
documentation: |-
32+
The Openshift E2E AWS `csi` workflow executes the `openshift/csi` end-to-end test suite on AWS EFS CSI driver that was installed during cluster setup.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../OWNERS
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"path": "storage/create/csi-shared-aws-efs/storage-create-csi-shared-aws-efs-ref.yaml",
3+
"owners": {
4+
"approvers": [
5+
"storage-approvers"
6+
],
7+
"reviewers": [
8+
"storage-reviewers"
9+
]
10+
}
11+
}

0 commit comments

Comments
 (0)