├── .gitignore ├── install-config.yaml ├── ARM64.md ├── CLUSTER_CERTS.md ├── PERF_ENHANCEMENTS.md ├── EXTRA_STORAGE.md ├── README.md ├── fix-instance-id.sh ├── sno-for-100.sh ├── adjust-single-node.sh └── adjust-single-node-4.16.sh /.gitignore: -------------------------------------------------------------------------------- 1 | cluster/ 2 | .openshift_install.log 3 | .env 4 | .run/ 5 | .vscode/ 6 | *.ignore -------------------------------------------------------------------------------- /install-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | baseDomain: sandbox.acme.com 3 | compute: 4 | - name: worker 5 | hyperthreading: Enabled 6 | replicas: 0 7 | architecture: amd64 8 | controlPlane: 9 | name: master 10 | replicas: 1 11 | architecture: amd64 12 | hyperthreading: Enabled 13 | platform: 14 | aws: 15 | type: m6a.2xlarge 16 | rootVolume: 17 | size: 150 18 | type: gp3 19 | metadata: 20 | name: my-cluster 21 | platform: 22 | aws: 23 | region: ap-southeast-1 24 | userTags: 25 | owner: "ateam@acme.com" 26 | team: "ateam" 27 | usage: "Dev" 28 | description: "Product Development and Demo environment for OpenShift" 29 | pullSecret: '{"auths":{"cloud.openshift.com":{"auth":...}}}' 30 | sshKey: 'ssh-rsa ...' 31 | -------------------------------------------------------------------------------- /ARM64.md: -------------------------------------------------------------------------------- 1 | # ARM64 2 | 3 | You can install arm64 based OpenShift by using an ARM based jumphost (this is the default behavior - see below for overrides). 4 | 5 | I used [the instructions from here](https://www.redhat.com/sysadmin/vm-arm64-fedora) to spin up an arm64 based vm on fedora. 6 | 7 | ```bash 8 | dnf install qemu-system-aarch64 9 | cd /var/lib/libvirt/images 10 | wget https://dl.fedoraproject.org/pub/fedora/linux/releases/40/Spins/aarch64/images/Fedora-Minimal-40-1.14.aarch64.raw.xz -O f40.xz && unxz f40.xz 11 | 12 | virt-install -v --name fedora-40-aarch64 --ram 4096 \ 13 | --disk path=f40,cache=none --nographics --os-variant fedora40 \ 14 | --import --arch aarch64 --vcpus 4 15 | ``` 16 | 17 | Install the prerequisites in your vm. 18 | 19 | ```bash 20 | dnf install git yq tar python-pip wget unzip cloud-utils-growpart podman 21 | pip install awscli --user 22 | ``` 23 | 24 | (Optional) grow jumphost vm size 25 | 26 | ```bash 27 | qemu-img resize -f raw f40 +20G 28 | growpart /dev/vda 3 29 | resize2fs /dev/vda3 30 | ``` 31 | 32 | And you should be good to go ! 33 | 34 | ```bash 35 | export INSTANCE_TYPE=m7g.2xlarge 36 | ``` 37 | 38 | ## Override Architecture 39 | 40 | You can override the install target architecture and jumphost os flavour by directly setting: 41 | 42 | ```bash 43 | export OS_FLAVOR=linux 44 | export OS_ARCH=arm64 45 | ``` 46 | 47 | For example if running on ARM MAC.OSX but want to install an X86_64 SNO: 48 | 49 | ```bash 50 | export OS_FLAVOR=mac 51 | export OS_ARCH=amd64 52 | ``` 53 | -------------------------------------------------------------------------------- /CLUSTER_CERTS.md: -------------------------------------------------------------------------------- 1 | ### Configure Lets Encrypt 2 | 3 | I use acme.sh all the time for generating Lets Encrypt certs, its easy and fast. The only downside is its also a bit of manual labour. 4 | 5 | 1. Create _caa.your-domain_ entries in your two Route53 hosted zones. 6 | 7 | Enter this as the **Record name** 8 | 9 | ```bash 10 | caa 11 | ``` 12 | 13 | Enter this as the **Value** 14 | 15 | ```bash 16 | 0 issuewild "letsencrypt.org;" 17 | ``` 18 | 19 | 2. On the command line, export your AWS credentials. 20 | 21 | ```bash 22 | export AWS_ACCESS_KEY_ID= 23 | export AWS_SECRET_ACCESS_KEY= 24 | ``` 25 | 26 | 3. Grab the cluster api and wildcard domain and export them as environment variables. We will create a Let's Encrypt cert with Subject Alternate names for these domains. 27 | 28 | ```bash 29 | export LE_API=$(oc whoami --show-server | cut -f 2 -d ':' | cut -f 3 -d '/' | sed 's/-api././') 30 | export LE_WILDCARD=$(oc get ingresscontroller default -n openshift-ingress-operator -o jsonpath='{.status.domain}') 31 | ``` 32 | 33 | 4. Clone the fabulous **acme.sh** git repo to your local machine. 34 | 35 | ```bash 36 | cd ~/git 37 | git clone https://github.com/Neilpang/acme.sh.git 38 | ``` 39 | 40 | 5. Now run the acme shell script to create your certificate requests. 41 | 42 | ```bash 43 | ~/git/acme.sh/acme.sh --issue --dns dns_aws -d ${LE_API} -d *.${LE_WILDCARD} --dnssleep 100 --force --insecure 44 | ``` 45 | 46 | 6. Once complete, your certificates will be downloaded and available in your home directory. 47 | 48 | 7. We can now configure the default OpenShift ingress router to use them. 49 | 50 | ```bash 51 | oc -n openshift-ingress delete secret router-certs 52 | oc -n openshift-ingress create secret tls router-certs --cert=/home/$USER/.acme.sh/${LE_API}/fullchain.cer --key=/home/$USER/.acme.sh/${LE_API}/${LE_API}.key 53 | oc -n openshift-ingress-operator patch ingresscontroller default --patch '{"spec": { "defaultCertificate": { "name": "router-certs"}}}' --type=merge 54 | ``` 55 | -------------------------------------------------------------------------------- /PERF_ENHANCEMENTS.md: -------------------------------------------------------------------------------- 1 | ## Performance Enhancements 2 | 3 | Try out some performance enhancements for your SNO cluster. 4 | 5 | - Encapsulating mount namespaces 6 | - https://docs.openshift.com/container-platform/4.13/scalability_and_performance/optimization/optimizing-cpu-usage.html 7 | 8 | - Configuring crun as the default container runtime 9 | - https://docs.openshift.com/container-platform/4.13/scalability_and_performance/ztp_far_edge/ztp-reference-cluster-configuration-for-vdu.html#ztp-sno-du-configuring-crun-container-runtime_sno-configure-for-vdu 10 | 11 | - 500 pods 12 | 13 | Run this post install. 14 | 15 | ```bash 16 | cat </cluster/auth/kubeconfig 69 | export CLUSTER_NAME=my-cluster 70 | export BASE_DOMAIN=sandbox.acme.com 71 | ``` 72 | 73 | 4. Adjust AWS Objects 74 | 75 | Dry run (no changes, just perform lookups). Do this first to check output, as the script may over select. 76 | 77 | ```bash 78 | ./adjust-single-node.sh 79 | ``` 80 | 81 | DoIt ! 82 | 83 | ```bash 84 | ./adjust-single-node.sh -d 85 | ``` 86 | 87 | Note: there is a v4.16+ version of this script as well. 88 | 89 | 5. Check 90 | 91 | It may take a couple of minutes for SNO to settle down (authentication, ingress operators become available). 92 | You should now be able to login to your cluster OK. Check `oc get co` to make sure cluster operators are healthy. 93 | Check the router ELB has the instance associated (this will be temporary until you run the fix instance id script). 94 | 95 | ## Convert SNO to SPOT pricing 96 | 97 | This portion does the actual conversion to persistent spot instance pricing. 98 | 99 | ```bash 100 | export INSTANCE_ID=$(aws ec2 describe-instances \ 101 | --query "Reservations[].Instances[].InstanceId" \ 102 | --filters "Name=tag-value,Values=$CLUSTER_NAME-*-master-0" "Name=instance-state-name,Values=running" \ 103 | --output text) 104 | ``` 105 | 106 | ```bash 107 | ec2-spot-converter --stop-instance --instance-id $INSTANCE_ID 108 | ``` 109 | 110 | There are several other options for the tool not used here but which could be invoked: 111 | 112 | * max spot price 113 | * change instance type 114 | * delete the AMI when it completes 115 | * change the encryption key for the root volume 116 | * convert from spot back to on-demand 117 | 118 | Documentation of the settings and how to use them are on the ec2-spot-converter tool's [homepage](https://github.com/jcjorel/ec2-spot-converter). 119 | 120 | ## Fix the internal node instance references 121 | 122 | After converting to spot, there are a few references to the old instance ID in the cluster which must be remedied so the operators function correctly for the life of the cluster. 123 | 124 | 1. Export Env.Vars 125 | 126 | ```bash 127 | export AWS_PROFILE=default 128 | export AWS_DEFAULT_REGION=ap-southeast-1 129 | export BASE_DOMAIN=sandbox.acme.com #replace by a real domain and make sure you have a Hosted Zone for it at Route 53! 130 | export CLUSTER_NAME=my-cluster 131 | export KUBECONFIG=/cluster/auth/kubeconfig 132 | ``` 133 | 134 | 2. Fix internal node instance references 135 | 136 | Dry run (no changes, just perform lookups). Do this first to check. 137 | 138 | ```bash 139 | ./fix-instance-id.sh 140 | ``` 141 | 142 | DoIt ! 143 | 144 | ```bash 145 | ./fix-instance-id.sh -d 146 | ``` 147 | 148 | 3. Check 149 | 150 | It may take a couple of minutes for SNO to settle after restarting (authentication, ingress operators become available). 151 | You should now be able to login to your cluster OK. Check `oc get co` to make sure cluster operators are healthy. 152 | Check the router ELB has the instance associated OK, this should be done automatically now by the node. 153 | 154 | ## Delete SNO instance 155 | 156 | 1. If you no longer need your instance, to remove all related aws objects just run inside your `$RUNDIR`. 157 | 158 | ```bash 159 | openshift-install destroy cluster --dir=cluster 160 | ``` 161 | 162 | ## Day#2 Enhancements 163 | 164 | You may want to try out these common day.2 enhancements for your SNO on SPOT instance. 165 | 166 | - [Extra Storage](EXTRA_STORAGE.md) 167 | - [Performance Enhancements](PERF_ENHANCEMENTS.md) 168 | - [Cluster Route Certificates with Let's Encrypt](CLUSTER_CERTS.md) 169 | -------------------------------------------------------------------------------- /fix-instance-id.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- coding: UTF-8 -*- 3 | 4 | # aws cli v2 - https://github.com/aws/aws-cli/issues/4992 5 | export AWS_PAGER="" 6 | 7 | RED='\033[0;31m' 8 | GREEN='\033[0;32m' 9 | ORANGE='\033[38;5;214m' 10 | NC='\033[0m' # No Color 11 | # env vars 12 | DRYRUN=${DRYRUN:-} 13 | KUBECONFIG=${KUBECONFIG:-} 14 | BASE_DOMAIN=${BASE_DOMAIN:-} 15 | CLUSTER_NAME=${CLUSTER_NAME:-} 16 | readonly RUN_DIR=$(pwd) 17 | # prog vars 18 | region= 19 | zone= 20 | instance_id= 21 | 22 | find_region() { 23 | if [ ! -z "$AWS_DEFAULT_REGION" ]; then 24 | region="$AWS_DEFAULT_REGION" 25 | else 26 | region=$(aws configure get region) 27 | fi 28 | if [ -z "$region" ]; then 29 | echo -e "🕱${RED}Failed - could not find aws region ?${NC}" 30 | exit 1 31 | else 32 | echo "🌴 Region set to $region" 33 | fi 34 | } 35 | 36 | find_zone() { 37 | if [ ! -z "$AWS_DEFAULT_ZONES" ]; then 38 | zone=$(echo ${AWS_DEFAULT_ZONES} | tr -d '[]') 39 | fi 40 | if [ -z "$zone" ]; then 41 | echo -e "🕱${RED}Failed - could not find aws zone ?${NC}" 42 | exit 1 43 | else 44 | echo "🌴 Zone set to $zone" 45 | fi 46 | } 47 | 48 | find_instance_id() { 49 | local tag_value="$1" 50 | instance_id=$(aws ec2 describe-instances \ 51 | --region=${region} \ 52 | --query "Reservations[].Instances[].InstanceId" \ 53 | --filters "Name=tag-value,Values=$tag_value" "Name=instance-state-name,Values=running" \ 54 | --output text) 55 | if [ -z "$instance_id" ]; then 56 | echo -e "🕱${RED}Failed - could not find instance id associated with tag: $tag_value ?${NC}" 57 | exit 1 58 | else 59 | echo "🌴 InstanceId set to $instance_id" 60 | fi 61 | } 62 | 63 | restart_instance() { 64 | set -o pipefail 65 | aws ec2 stop-instances \ 66 | ${DRYRUN:---dry-run} \ 67 | --region=${region} \ 68 | --instance-ids=$instance_id 2>&1 | tee /tmp/aws-error-file 69 | if [ "$?" != 0 ]; then 70 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 71 | echo -e "${GREEN}Ignoring - restart_instance stop instance - dry run set${NC}" 72 | else 73 | echo -e "🕱${RED}Failed - could not stop $instance_id ?${NC}" 74 | exit 1 75 | fi 76 | else 77 | echo -e "${GREEN} -> restart_instance stopping [ $instance_id ] OK${NC}" 78 | fi 79 | 80 | echo -e "${GREEN} -> wait instance-stopped ... [ $instance_id ]${NC}" 81 | aws ec2 wait instance-stopped \ 82 | ${DRYRUN:---dry-run} \ 83 | --region=${region} \ 84 | --instance-ids $instance_id 85 | 86 | if [ ! -z "$DRYRUN" ]; then 87 | sleep 120 # fix me spot restart is not elegant 88 | echo -e "${GREEN} -> instance stopped [ $instance_id ] OK${NC}" 89 | fi 90 | 91 | aws ec2 start-instances \ 92 | ${DRYRUN:---dry-run} \ 93 | --region=${region} \ 94 | --instance-ids=$instance_id 2>&1 | tee /tmp/aws-error-file 95 | if [ "$?" != 0 ]; then 96 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 97 | echo -e "${GREEN}Ignoring - restart_instance start instance - dry run set${NC}" 98 | else 99 | until [ "$?" == 0 ] 100 | do 101 | echo -e "${GREEN} -> trying to start-instances ... [ $instance_id ]${NC}" 102 | ((i=i+1)) 103 | if [ $i -gt 5 ]; then 104 | echo -e "🕱${RED}Failed - could not start $instance_id ?${NC}" 105 | exit 1 106 | fi 107 | sleep 10 108 | aws ec2 start-instances \ 109 | ${DRYRUN:---dry-run} \ 110 | --region=${region} \ 111 | --instance-ids=$instance_id 2>&1 | tee /tmp/aws-error-file 112 | done 113 | echo -e "${GREEN} -> restart_instance starting [ $instance_id ] OK${NC}" 114 | fi 115 | else 116 | echo -e "${GREEN} -> restart_instance starting [ $instance_id ] OK${NC}" 117 | fi 118 | set +o pipefail 119 | } 120 | 121 | find_vpc_id() { 122 | local tag_value="$1" 123 | vpc_id=$(aws ec2 describe-vpcs --region=${region} \ 124 | --query "Vpcs[].VpcId" \ 125 | --filters "Name=tag-value,Values=$tag_value" \ 126 | --output text) 127 | if [ -z "$vpc_id" ]; then 128 | echo -e "🕱${RED}Failed - could not find vpc id associated with tag: $tag_value ?${NC}" 129 | exit 1 130 | else 131 | echo "🌴 VpcId set to $vpc_id" 132 | fi 133 | } 134 | 135 | find_router_lb() { 136 | query='LoadBalancerDescriptions[?VPCId==`'${vpc_id}'`]|[].LoadBalancerName' 137 | router_load_balancer=$(aws elb describe-load-balancers \ 138 | --region=${region} \ 139 | --query $query \ 140 | --output text) 141 | if [ -z "$router_load_balancer" ]; then 142 | echo -e "🕱${RED}Warning - could not find router load balancer ?${NC}" 143 | exit 1 144 | else 145 | echo "🌴 RouterLoadBalancer set to $router_load_balancer" 146 | fi 147 | } 148 | 149 | associate_router_instance() { 150 | if [ -z "$DRYRUN" ]; then 151 | echo -e "${GREEN}Ignoring - associate_router_instance - dry run set${NC}" 152 | return 153 | fi 154 | if [ ! -z "$router_load_balancer" ]; then 155 | aws elb register-instances-with-load-balancer \ 156 | --load-balancer-name $router_load_balancer \ 157 | --instances $instance_id \ 158 | --region=${region} 159 | if [ "$?" != 0 ]; then 160 | echo -e "🕱${RED}Failed - could not associate router lb $router_load_balancer with instance $instance_id ?${NC}" 161 | exit 1 162 | else 163 | echo -e "${GREEN} -> associate_router_instance [ $router_load_balancer, $instance_id ] OK${NC}" 164 | fi 165 | fi 166 | } 167 | 168 | find_node_providerid() { 169 | node_provider_id=$(${RUN_DIR}/oc get nodes -o jsonpath='{.items[0].spec.providerID}') 170 | if [ -z "$node_provider_id" ]; then 171 | echo -e "🕱${RED}Failed - could not find openshift node providerid ?${NC}" 172 | exit 1 173 | else 174 | echo "🌴 NodeProviderId set to $node_provider_id" 175 | fi 176 | } 177 | 178 | update_providerid_on_node() { 179 | if [ -z "$DRYRUN" ]; then 180 | echo -e "${GREEN}Ignoring - update_providerid_on_node - dry run set${NC}" 181 | return 182 | fi 183 | # pre 4.19 184 | ${RUN_DIR}/oc -n default debug -T $(${RUN_DIR}/oc get node -o name) -- chroot /host bash -c "sed -i \"s|aws:///.*|aws:///$region/$instance_id\\\"|\" /etc/systemd/system/kubelet.service.d/20-aws-providerid.conf" 185 | # post 4.19+ 186 | ${RUN_DIR}/oc -n default debug -T $(${RUN_DIR}/oc get node -o name) -- chroot /host bash -c "sed -i \"s|aws:///.*|aws:///$zone/$instance_id|\" /etc/kubernetes/node.env" 187 | } 188 | 189 | delete_node() { 190 | if [ -z "$DRYRUN" ]; then 191 | echo -e "${GREEN}Ignoring - delete_node - dry run set${NC}" 192 | return 193 | fi 194 | ${RUN_DIR}/oc delete $(${RUN_DIR}/oc get node -o name) 195 | } 196 | 197 | patch_machine_load_balancers() { 198 | if [ -z "$DRYRUN" ]; then 199 | echo -e "${GREEN}Ignoring - patch_machine_load_balancers - dry run set${NC}" 200 | return 201 | fi 202 | ${RUN_DIR}/oc -n openshift-machine-api patch $(${RUN_DIR}/oc -n openshift-machine-api get machine.machine.openshift.io -l machine.openshift.io/cluster-api-machine-role=master -o name) -p '{"spec":{"providerSpec":{"value": {"loadBalancers" : []}}}}' --type=merge 203 | if [ "$?" != 0 ]; then 204 | echo -e "🕱${RED}Failed to patch_machine_load_balancers ?${NC}" 205 | exit 1 206 | fi 207 | echo -e "${GREEN} -> patch_machine_load_balancers OK${NC}" 208 | } 209 | 210 | patch_network_load_balancer() { 211 | if [ -z "$DRYRUN" ]; then 212 | echo -e "${GREEN}Ignoring - patch_network_load_balancer - dry run set${NC}" 213 | return 214 | fi 215 | ${RUN_DIR}/oc -n openshift-ingress-operator patch ingresscontroller default --type=merge --patch ' 216 | apiVersion: operator.openshift.io/v1 217 | kind: IngressController 218 | metadata: 219 | name: default 220 | namespace: openshift-ingress-operator 221 | spec: 222 | endpointPublishingStrategy: 223 | loadBalancer: 224 | scope: External 225 | providerParameters: 226 | type: AWS 227 | aws: 228 | type: NLB 229 | type: LoadBalancerService' 230 | if [ "$?" != 0 ]; then 231 | echo -e "🕱${RED}Failed to patch_network_load_balancer ?${NC}" 232 | exit 1 233 | fi 234 | echo -e "${GREEN} -> patch_network_load_balancer OK${NC}" 235 | } 236 | 237 | delete_classic_load_balancers() { 238 | if [ -z "$DRYRUN" ]; then 239 | echo -e "${GREEN}Ignoring - delete_classic_load_balancers - dry run set${NC}" 240 | return 241 | fi 242 | query='LoadBalancerDescriptions[?VPCId==`'${vpc_id}'`]|[].LoadBalancerName' 243 | load_balancers_name=$(aws elb describe-load-balancers \ 244 | --region=${region} \ 245 | --query $query \ 246 | --output text) 247 | if [ -z "$load_balancers_name" ]; then 248 | echo -e "💀${ORANGE}Warning - could not find load balancers by name - continuing, they may have been deleted already ?${NC}" 249 | return 250 | else 251 | echo "🌴 LoadBalancersName set to $load_balancers_name" 252 | fi 253 | if [ ! -z "$load_balancers_name" ]; then 254 | for x in $load_balancers_name; do 255 | aws elb delete-load-balancer \ 256 | --region=${region} \ 257 | --load-balancer-name $x 258 | if [ "$?" != 0 ]; then 259 | echo -e "🕱${RED}Failed - could not delete load balancer $x ?${NC}" 260 | exit 1 261 | else 262 | echo -e "${GREEN} -> delete_classic_load_balancers [ $x ] OK${NC}" 263 | fi 264 | done 265 | fi 266 | } 267 | 268 | wait_for_openshift_api() { 269 | local i=0 270 | HOST=https://api.${CLUSTER_NAME}.${BASE_DOMAIN}:6443/healthz 271 | until [ $(curl -k -s -o /dev/null -w %{http_code} ${HOST}) = "200" ] 272 | do 273 | echo -e "${GREEN}Waiting for 200 response from openshift api ${HOST}.${NC}" 274 | sleep 5 275 | ((i=i+1)) 276 | if [ $i -gt 100 ]; then 277 | echo -e "🕱${RED}Failed - OpenShift api ${HOST} never ready?.${NC}" 278 | exit 1 279 | fi 280 | done 281 | } 282 | 283 | approve_all_certificates() { 284 | if [ -z "$DRYRUN" ]; then 285 | echo -e "${GREEN}Ignoring - approve_all_certificates - dry run set${NC}" 286 | return 287 | fi 288 | local i=0 289 | ${RUN_DIR}/oc get csr 290 | until [ "$?" == 0 ] 291 | do 292 | echo -e "${GREEN}Waiting for 0 rc from oc commands.${NC}" 293 | ((i=i+1)) 294 | if [ $i -gt 100 ]; then 295 | echo -e "🕱${RED}Failed - oc never ready?.${NC}" 296 | exit 1 297 | fi 298 | sleep 5 299 | ${RUN_DIR}/oc get csr 300 | done 301 | ${RUN_DIR}/oc get csr -o name | xargs ${RUN_DIR}/oc adm certificate approve 302 | if [ "$?" != 0 ]; then 303 | echo -e "🕱${RED}Failed to approve ?${NC}" 304 | exit 1 305 | fi 306 | echo -e "${GREEN} -> approve_csr OK${NC}" 307 | } 308 | 309 | # do it all 310 | all() { 311 | echo "🌴 BASE_DOMAIN set to $BASE_DOMAIN" 312 | echo "🌴 CLUSTER_NAME set to $CLUSTER_NAME" 313 | echo "🌴 KUBECONFIG set to $KUBECONFIG" 314 | 315 | find_region 316 | find_zone 317 | find_instance_id "$CLUSTER_NAME-*-master-0" 318 | find_vpc_id "$CLUSTER_NAME-*-vpc" 319 | 320 | find_router_lb 321 | associate_router_instance 322 | 323 | wait_for_openshift_api 324 | find_node_providerid 325 | if [[ "$node_provider_id" != *"$instance_id"* ]]; then 326 | update_providerid_on_node 327 | delete_node 328 | restart_instance 329 | wait_for_openshift_api 330 | else 331 | echo -e "${GREEN} -> $instance_id already set on node $node_provider_id, so not redoing it. OK${NC}" 332 | fi 333 | patch_machine_load_balancers 334 | patch_network_load_balancer 335 | delete_classic_load_balancers 336 | approve_all_certificates 337 | } 338 | 339 | usage() { 340 | cat <&1 341 | usage: $0 [ -d ] 342 | 343 | Fix SNO Instance Id's 344 | -d do it ! no dry run - else we print out whats going to happen and any non desructive lookups 345 | 346 | Optional arguments if not set in environment: 347 | 348 | -b BASE_DOMAIN - openshift base domain (or export BASE_DOMAIN env var) 349 | -c CLUSTER_NAME - openshift cluster name (or export CLUSTER_NAME env var) 350 | -k KUBECONFIG - full path to the kubeconfig file 351 | 352 | This script is rerunnable. 353 | 354 | Environment Variables: 355 | Export these in your environment. 356 | 357 | AWS_PROFILE use a pre-configured aws profile (~/.aws/config and ~/.aws/credentials) 358 | 359 | OR export these in your environment: 360 | 361 | AWS_ACCESS_KEY_ID 362 | AWS_SECRET_ACCESS_KEY 363 | AWS_DEFAULT_REGION 364 | 365 | Optionally if not set on command line: 366 | 367 | BASE_DOMAIN 368 | CLUSTER_NAME 369 | KUBECONFIG 370 | 371 | EOF 372 | exit 1 373 | } 374 | 375 | while getopts db:c:k: opts; do 376 | case $opts in 377 | b) 378 | BASE_DOMAIN=$OPTARG 379 | ;; 380 | c) 381 | CLUSTER_NAME=$OPTARG 382 | ;; 383 | d) 384 | DRYRUN="--no-dry-run" 385 | ;; 386 | k) 387 | KUBECONFIG=$OPTARG 388 | ;; 389 | *) 390 | usage 391 | ;; 392 | esac 393 | done 394 | 395 | shift `expr $OPTIND - 1` 396 | 397 | # Check for EnvVars 398 | [ ! -z "$AWS_PROFILE" ] && echo "🌴 Using AWS_PROFILE: $AWS_PROFILE" 399 | [ -z "$BASE_DOMAIN" ] && echo "🕱 Error: must supply BASE_DOMAIN in env or cli" && exit 1 400 | [ -z "$CLUSTER_NAME" ] && echo "🕱 Error: must supply CLUSTER_NAME in env or cli" && exit 1 401 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_ACCESS_KEY_ID" ] && echo "🕱 Error: AWS_ACCESS_KEY_ID not set in env" && exit 1 402 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_SECRET_ACCESS_KEY" ] && echo "🕱 Error: AWS_SECRET_ACCESS_KEY not set in env" && exit 1 403 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_DEFAULT_REGION" ] && echo "🕱 Error: AWS_DEFAULT_REGION not set in env" && exit 1 404 | [ -z "$KUBECONFIG" ] && [ -z "KUBECONFIG" ] && echo "🕱 Error: KUBECONFIG not set in env or cli" && exit 1 405 | 406 | all 407 | 408 | echo -e "\n🌻${GREEN}AWS SNO Reconfigured OK.${NC}🌻\n" 409 | exit 0 410 | -------------------------------------------------------------------------------- /sno-for-100.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- coding: UTF-8 -*- 3 | 4 | # aws cli v2 - https://github.com/aws/aws-cli/issues/4992 5 | export AWS_PAGER="" 6 | readonly RED='\033[0;31m' 7 | readonly GREEN='\033[0;32m' 8 | readonly ORANGE='\033[38;5;214m' 9 | readonly NC='\033[0m' # No Color 10 | readonly RUN_DIR=$(pwd) 11 | export KUBECONFIG=$RUN_DIR/cluster/auth/kubeconfig 12 | 13 | # env vars 14 | DRYRUN=${DRYRUN:-} 15 | BASE_DOMAIN=${BASE_DOMAIN:-} 16 | CLUSTER_NAME=${CLUSTER_NAME:-} 17 | PULL_SECRET=${PULL_SECRET:-} 18 | SSH_KEY=${SSH_KEY:-} 19 | ROOT_VOLUME_SIZE=${ROOT_VOLUME_SIZE:-100} 20 | INSTANCE_TYPE=${INSTANCE_TYPE:-"m5a.2xlarge"} 21 | OPENSHIFT_VERSION=${OPENSHIFT_VERSION:-"stable"} 22 | SKIP_SPOT=${SKIP_SPOT:-} 23 | OS_FLAVOR=${OS_FLAVOR:-} 24 | OS_ARCH=${OS_ARCH:-} 25 | # prog vars 26 | region= 27 | instance_id= 28 | architecture= 29 | 30 | # sanity environment check section 31 | system_os_flavor=linux 32 | system_os_arch=$(uname -m) 33 | min_bash_version=4 34 | 35 | # override OS flavor and architecture if set 36 | if [ ! -z "$OS_FLAVOR" ] && [ ! -z "$OS_ARCH" ]; then 37 | system_os_flavor=$OS_FLAVOR 38 | system_os_arch=$OS_ARCH 39 | else 40 | # detect OS flavor 41 | uname_s=$(uname | tr '[:upper:]' '[:lower:]') 42 | case "$uname_s" in 43 | darwin*) 44 | system_os_flavor=mac 45 | min_bash_version=5 46 | echo -e "${ORANGE}\nmacOS ${system_os_arch} DETECTED${NC}\n" 47 | ;; 48 | cygwin*|msys*|mingw*) 49 | system_os_flavor=windows 50 | min_bash_version=4 51 | echo "CYGWIN/MSYS ${system_os_arch} DETECTED - WILL TRY TO ADJUST PATHS" 52 | ;; 53 | linux*) 54 | system_os_flavor=linux 55 | min_bash_version=4 56 | ;; 57 | esac 58 | 59 | # set architecture for install-config 60 | if [[ "$system_os_flavor" == "windows" ]]; then 61 | architecture=amd64 62 | fi 63 | [[ "$system_os_arch" == "x86_64" ]] && architecture=amd64 64 | [[ "$system_os_arch" == "aarch64" ]] && architecture=arm64 65 | fi 66 | 67 | # bash version check 68 | bash_version=$(bash -c 'echo ${BASH_VERSINFO[0]}') 69 | [ "${bash_version}" -ge $min_bash_version ] || { echo "ERROR: BASH VERSION NOT SUPPORTED - PLEASE UPGRADE YOUR BASH INSTALLATION - ABORTING"; exit 1; } 70 | 71 | # sanity check tools 72 | command -v yq &> /dev/null || { echo >&2 'ERROR: yq not installed. Please install yq tool to continue - Aborting'; exit 1; } 73 | command -v tar &> /dev/null || { echo >&2 'ERROR: tar not installed. Please install tar tool to continue - Aborting'; exit 1; } 74 | command -v curl &> /dev/null || { echo >&2 'ERROR: curl not installed. Please install curl tool to continue - Aborting'; exit 1; } 75 | 76 | # choose sed implementation 77 | SED='sed' 78 | if [[ "$system_os_flavor" == "mac" ]]; then 79 | if command -v gsed &> /dev/null; then 80 | SED='gsed' 81 | elif command -v sed &> /dev/null && "$SED" --version 2>/dev/null | head -1 | grep -qi 'gnu'; then 82 | SED='sed' 83 | else 84 | echo >&2 'ERROR: GNU sed not found. Please install gsed (GNU sed 4.8+). Aborting' 85 | exit 2 86 | fi 87 | else 88 | command -v sed &> /dev/null || { echo >&2 'ERROR: sed not installed. Please install sed 4.2 (or later) to continue - Aborting'; exit 1; } 89 | fi 90 | 91 | [ ! -z "$system_os_flavor" ] && echo "🌴 Using system_os_flavor: $system_os_flavor" 92 | [ ! -z "$system_os_arch" ] && echo "🌴 Using system_os_arch: $system_os_arch" 93 | # sanity environment check section end 94 | 95 | find_region() { 96 | if [ ! -z "$AWS_DEFAULT_REGION" ]; then 97 | region="$AWS_DEFAULT_REGION" 98 | else 99 | region=$(aws configure get region) 100 | fi 101 | if [ -z "$region" ]; then 102 | echo -e "🕱${RED}Failed - could not find aws region ?${NC}" 103 | exit 1 104 | else 105 | echo "🌴 Region set to $region" 106 | fi 107 | } 108 | 109 | find_instance_id() { 110 | local tag_value="$1" 111 | instance_id=$(aws ec2 describe-instances \ 112 | --region=${region} \ 113 | --query "Reservations[].Instances[].InstanceId" \ 114 | --filters "Name=tag-value,Values=$tag_value" "Name=instance-state-name,Values=running" \ 115 | --output text) 116 | if [ -z "$instance_id" ]; then 117 | echo -e "🕱${RED}Failed - could not find instance id associated with tag: $tag_value ?${NC}" 118 | exit 1 119 | else 120 | echo "🌴 InstanceId set to $instance_id" 121 | fi 122 | } 123 | 124 | generate_dynamodb() { 125 | if [ -z "$DRYRUN" ]; then 126 | echo -e "${GREEN}Ignoring - generate_dynamodb - dry run set${NC}" 127 | return 128 | fi 129 | set -o pipefail 130 | ${RUN_DIR}/ec2-spot-converter --generate-dynamodb-table 2>&1 | tee /tmp/aws-error-file 131 | if [ "$?" != 0 ]; then 132 | if grep -E -q "Table already exists" /tmp/aws-error-file; then 133 | echo -e "${GREEN}Ignoring - dynamodb table already exists${NC}" 134 | else 135 | echo -e "🕱${RED}Failed - to run generate_dynamodb ?${NC}" 136 | exit 1 137 | fi 138 | else 139 | echo -e "${GREEN} -> generate_dynamodb ran OK${NC}" 140 | fi 141 | set +o pipefail 142 | } 143 | 144 | prepare_install_config() { 145 | "$SED" -i "s|baseDomain:.*|baseDomain: $BASE_DOMAIN|" ${RUN_DIR}/install-config.yaml 146 | "$SED" -i "s| architecture:.*| architecture: $architecture|" ${RUN_DIR}/install-config.yaml 147 | "$SED" -i "s|^ type:.*| type: $INSTANCE_TYPE|" ${RUN_DIR}/install-config.yaml 148 | "$SED" -i "s|^ size:.*| size: $ROOT_VOLUME_SIZE|" ${RUN_DIR}/install-config.yaml 149 | "$SED" -i "s|^ name:.*| name: $CLUSTER_NAME|" ${RUN_DIR}/install-config.yaml 150 | "$SED" -i "s| region:.*| region: $region|" ${RUN_DIR}/install-config.yaml 151 | "$SED" -i "s|pullSecret:.*|pullSecret: '$PULL_SECRET'|" ${RUN_DIR}/install-config.yaml 152 | "$SED" -i "s|sshKey:.*|sshKey: '$SSH_KEY'|" ${RUN_DIR}/install-config.yaml 153 | if [ ! -z "$AWS_DEFAULT_ZONES" ]; then 154 | yq e '.controlPlane.platform.aws.zones = env(AWS_DEFAULT_ZONES)' -i ${RUN_DIR}/install-config.yaml 155 | fi 156 | cp ${RUN_DIR}/install-config.yaml ${RUN_DIR}/cluster/install-config.yaml 157 | } 158 | 159 | check_if_install_complete() { 160 | if [ ! -r "$RUN_DIR/cluster/auth/kubeconfig" ]; then 161 | return 1 162 | fi 163 | export KUBECONFIG="$RUN_DIR/cluster/auth/kubeconfig" 164 | $RUN_DIR/openshift-install wait-for bootstrap-complete --dir=$RUN_DIR/cluster 165 | if [ "$?" != 0 ]; then 166 | return 1 167 | fi 168 | $RUN_DIR/openshift-install wait-for install-complete --dir=$RUN_DIR/cluster 169 | if [ "$?" != 0 ]; then 170 | return 1 171 | fi 172 | return 0 173 | } 174 | 175 | install_openshift() { 176 | if [ -z "$DRYRUN" ]; then 177 | echo -e "${GREEN}Ignoring - install_openshift - dry run set${NC}" 178 | return 179 | fi 180 | 181 | if [ ! -d "$RUN_DIR/cluster" ]; then 182 | mkdir -p cluster 183 | prepare_install_config 184 | fi 185 | 186 | if [ check_if_install_complete != 0 ]; then 187 | echo "🌴 Installing OpenShift..." 188 | ${RUN_DIR}/openshift-install create cluster --dir=$RUN_DIR/cluster 189 | echo "$?" 190 | fi 191 | 192 | if [ ! -r "$RUN_DIR/cluster/auth/kubeconfig" ]; then 193 | echo -e "🕱${RED}Failed - install_openshift expecting a readable kubeconfig ?${NC}" 194 | exit 1 195 | fi 196 | export KUBECONFIG="$RUN_DIR/cluster/auth/kubeconfig" 197 | } 198 | 199 | adjust_single_node() { 200 | echo "🌴 Running adjust-single-node.sh ..." 201 | ${RUN_DIR}/adjust-single-node.sh 202 | if [ "$?" != 0 ]; then 203 | echo -e "🕱${RED}Failed - to run adjust-single-node.sh ?${NC}" 204 | exit 1 205 | else 206 | echo "🌴 adjust-single-node.sh ran OK" 207 | fi 208 | } 209 | 210 | adjust_single_node_416() { 211 | echo "🌴 Running adjust-single-node-4.16.sh ..." 212 | ${RUN_DIR}/adjust-single-node-4.16.sh 213 | if [ "$?" != 0 ]; then 214 | echo -e "🕱${RED}Failed - to run adjust-single-node-4.16.sh ?${NC}" 215 | exit 1 216 | else 217 | echo "🌴 adjust-single-node-4.16.sh ran OK" 218 | fi 219 | } 220 | 221 | ec2_spot_converter() { 222 | echo "🌴 Running ec2-spot-converter ..." 223 | if [ -z "$DRYRUN" ]; then 224 | echo -e "${GREEN}Ignoring - ec2_spot_converter - dry run set${NC}" 225 | return 226 | fi 227 | if [ ! -z "$SKIP_SPOT" ]; then 228 | echo -e "${GREEN}Ignoring - ec2_spot_converter - skip spot set${NC}" 229 | return 230 | fi 231 | set -o pipefail 232 | ${RUN_DIR}/ec2-spot-converter --stop-instance --instance-id $instance_id 2>&1 | tee /tmp/aws-error-file 233 | if [ "$?" != 0 ]; then 234 | if grep -E -q "is already a Spot instance" /tmp/aws-error-file; then 235 | echo -e "${GREEN}Ignoring - $instance_id is already a spot instance${NC}" 236 | else 237 | echo -e "🕱${RED}Failed - to run ec2-spot-converter ?${NC}" 238 | exit 1 239 | fi 240 | else 241 | echo -e "${GREEN} -> ec2-spot-converter ran OK${NC}" 242 | echo -e "${GREEN} -> \t run ec2-spot-converter again to clean up the generated ami and its snapshot...${NC}" 243 | ${RUN_DIR}/ec2-spot-converter --delete-ami --instance-id $instance_id 2>&1 | tee /tmp/aws-error-file 244 | fi 245 | set +o pipefail 246 | } 247 | 248 | fix_instance_id() { 249 | echo "🌴 Running fix-instance-id.sh ..." 250 | ${RUN_DIR}/fix-instance-id.sh 251 | if [ "$?" != 0 ]; then 252 | echo -e "🕱${RED}Failed - to run fix-instance-id.sh ?${NC}" 253 | exit 1 254 | else 255 | echo "🌴 fix-instance-id.sh ran OK" 256 | fi 257 | } 258 | 259 | download_adjust_single_node() { 260 | local ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/eformat/sno-for-100/main/adjust-single-node.sh -o ${RUN_DIR}/adjust-single-node.sh) 261 | if [ "$ret" != "200" ]; then 262 | echo -e "🕱${RED}Failed - to download adjust-single-node.sh ?.${NC}" 263 | return $ret 264 | fi 265 | chmod u+x ${RUN_DIR}/adjust-single-node.sh 266 | } 267 | 268 | download_adjust_single_node_416() { 269 | local ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/eformat/sno-for-100/main/adjust-single-node-4.16.sh -o ${RUN_DIR}/adjust-single-node-4.16.sh) 270 | if [ "$ret" != "200" ]; then 271 | echo -e "🕱${RED}Failed - to download adjust-single-node-4.16.sh ?.${NC}" 272 | return $ret 273 | fi 274 | chmod u+x ${RUN_DIR}/adjust-single-node-4.16.sh 275 | } 276 | 277 | download_fix_instance_id() { 278 | local ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/eformat/sno-for-100/main/fix-instance-id.sh -o ${RUN_DIR}/fix-instance-id.sh) 279 | if [ "$ret" != "200" ]; then 280 | echo -e "🕱${RED}Failed - to download fix-instance-id.sh ?.${NC}" 281 | return $ret 282 | fi 283 | chmod u+x ${RUN_DIR}/fix-instance-id.sh 284 | } 285 | 286 | download_ec2_converter() { 287 | local version=`curl https://raw.githubusercontent.com/jcjorel/ec2-spot-converter/master/VERSION.txt` 288 | local ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/jcjorel/ec2-spot-converter/master/releases/ec2-spot-converter-${version} -o ${RUN_DIR}/ec2-spot-converter) 289 | if [ "$ret" != "200" ]; then 290 | echo -e "🕱${RED}Failed - to download ec2-spot-converter ?.${NC}" 291 | return $ret 292 | fi 293 | #patch script header to use /usr/bin/env instead of referring to /usr/bin/python3 directly 294 | #this avoid issues with multiple python present in the path 295 | "$SED" -i 's|\#\!/usr/bin/python3|\#\!/usr/bin/env python3|' ${RUN_DIR}/ec2-spot-converter 296 | chmod u+x ${RUN_DIR}/ec2-spot-converter 297 | #ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/jcjorel/ec2-spot-converter/master/requirements.txt -o ${RUN_DIR}/requirements.txt) 298 | #if [ "$ret" != "200" ]; then 299 | # echo -e "🕱${RED}Failed - to download ec2-spot-converter requirements file ?.${NC}" 300 | # return $ret 301 | #fi 302 | # newer versions break with 303 | echo "boto3==1.36.15" > ${RUN_DIR}/requirements.txt 304 | /usr/bin/env python3 -m pip install -r ${RUN_DIR}/requirements.txt 305 | } 306 | 307 | download_openshift_installer() { 308 | local ret=$(curl -L --write-out "%{http_code}" https://mirror.openshift.com/pub/openshift-v4/${system_os_arch}/clients/ocp/${OPENSHIFT_VERSION}/openshift-install-${system_os_flavor}.tar.gz -o ${RUN_DIR}/openshift-install-${system_os_flavor}.tar.gz) 309 | if [ "$ret" != "200" ]; then 310 | echo -e "🕱${RED}Failed - to download openshift-install-${system_os_flavor}.tar.gz ?.${NC}" 311 | return $ret 312 | fi 313 | tar xzvf openshift-install-${system_os_flavor}.tar.gz 314 | if [ "$?" != 0 ]; then 315 | echo -e "🕱${RED}Failed - to unzip openshift-install-${system_os_flavor}.tar.gz ?${NC}" 316 | exit 1 317 | fi 318 | chmod u+x ${RUN_DIR}/openshift-install 319 | } 320 | 321 | download_openshift_cli() { 322 | local ret=$(curl -L --write-out "%{http_code}" https://mirror.openshift.com/pub/openshift-v4/${system_os_arch}/clients/ocp/${OPENSHIFT_VERSION}/openshift-client-${system_os_flavor}.tar.gz -o ${RUN_DIR}/openshift-client-${system_os_flavor}.tar.gz) 323 | if [ "$ret" != "200" ]; then 324 | echo -e "🕱${RED}Failed - to download openshift-client-${system_os_flavor}.tar.gz ?.${NC}" 325 | return $ret 326 | fi 327 | tar xzvf openshift-client-${system_os_flavor}.tar.gz 328 | if [ "$?" != 0 ]; then 329 | echo -e "🕱${RED}Failed - to unzip openshift-client-${system_os_flavor}.tar.gz ?${NC}" 330 | fi 331 | chmod u+x ${RUN_DIR}/oc 332 | } 333 | 334 | download_install_config() { 335 | local ret=$(curl --write-out "%{http_code}" https://raw.githubusercontent.com/eformat/sno-for-100/main/install-config.yaml -o ${RUN_DIR}/install-config.yaml) 336 | if [ "$ret" != "200" ]; then 337 | echo -e "🕱${RED}Failed - to download install-config.yaml ?.${NC}" 338 | return $ret 339 | fi 340 | } 341 | 342 | version() { 343 | echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; 344 | } 345 | 346 | # do it all 347 | all() { 348 | find_region 349 | generate_dynamodb 350 | 351 | install_openshift 352 | 353 | if [ ! -z "$SKIP_SPOT" ]; then 354 | echo -e "${GREEN}Install complete - skip spot set${NC}" 355 | return 356 | fi 357 | 358 | if [ "$OPENSHIFT_VERSION" == "stable" ] || [ $(version "$OPENSHIFT_VERSION") > $(version "4.15.99") ]; then 359 | adjust_single_node_416 360 | else 361 | adjust_single_node 362 | fi 363 | 364 | find_instance_id "$CLUSTER_NAME-*-master-0" 365 | ec2_spot_converter 366 | 367 | fix_instance_id 368 | } 369 | 370 | 371 | usage() { 372 | cat <&1 373 | usage: $0 [ -d -b -c -p -s -v -t -o ] 374 | 375 | Provision a SNO for 100 spot instace. By default dry-run is ON ! You must set -d to doIt™️ 376 | -d do it ! no dry run - else we print out whats going to happen and any non desructive lookups 377 | 378 | Optional arguments if not set in environment: 379 | 380 | -b BASE_DOMAIN - openshift base domain (or export BASE_DOMAIN env var) 381 | -c CLUSTER_NAME - openshift cluster name (or export CLUSTER_NAME env var) 382 | -p PULL_SECRET - openshift pull secret (or export PULL_SECRET env var) e.g PULL_SECRET=\$(cat ~/Downloads/pull-secret) 383 | -s SSH_KEY - openshift ssh key (or export SSH_KEY env var) e.g. SSH_KEY=\$(cat ~/.ssh/id_rsa.pub) 384 | -v ROOT_VOLUME_SIZE - root vol size in GB (or export ROOT_VOLUME_SIZE env var, default: 100) 385 | -t INSTANCE_TYPE - instance type (or export INSTANCE_TYPE, default: m5a.2xlarge) 386 | -o OPENSHIFT_VERSION - OpenShift Version (or export OPENSHIFT_VERSION env var default: stable) 387 | 388 | This script is rerunnable. It will download the artifacts it needs to run into the current working directory. 389 | 390 | Environment Variables: 391 | Export these in your environment. 392 | 393 | AWS_PROFILE use a pre-configured aws profile (~/.aws/config and ~/.aws/credentials) 394 | 395 | OR export these in your environment: 396 | 397 | AWS_ACCESS_KEY_ID 398 | AWS_SECRET_ACCESS_KEY 399 | AWS_DEFAULT_REGION 400 | AWS_DEFAULT_ZONES (Optional array list - e.g ["us-east-2b","us-east-2c"]) 401 | 402 | Optionally if not set on command line: 403 | 404 | BASE_DOMAIN 405 | CLUSTER_NAME 406 | PULL_SECRET 407 | SSH_KEY 408 | ROOT_VOLUME_SIZE 409 | INSTANCE_TYPE 410 | OPENSHIFT_VERSION 411 | 412 | Example: 413 | 414 | export AWS_PROFILE=rhpds 415 | export AWS_DEFAULT_REGION=us-east-2 416 | export AWS_DEFAULT_ZONES=["us-east-2a"] 417 | export CLUSTER_NAME=foo-sno 418 | export BASE_DOMAIN=demo.redhatlabs.dev 419 | export PULL_SECRET=\$(cat ~/tmp/pull-secret) 420 | export SSH_KEY=\$(cat ~/.ssh/id_rsa.pub) 421 | 422 | mkdir my-run && cd my-run 423 | $0 -d 424 | 425 | EOF 426 | exit 1 427 | } 428 | 429 | while getopts b:c:p:s:v:t:o:d opt; do 430 | case $opt in 431 | b) 432 | export BASE_DOMAIN=$OPTARG 433 | ;; 434 | c) 435 | export CLUSTER_NAME=$OPTARG 436 | ;; 437 | d) 438 | export DRYRUN="--no-dry-run" 439 | ;; 440 | p) 441 | export PULL_SECRET=$OPTARG 442 | ;; 443 | s) 444 | export SSH_KEY=$OPTARG 445 | ;; 446 | v) 447 | export ROOT_VOLUME_SIZE=$OPTARG 448 | ;; 449 | t) 450 | export INSTANCE_TYPE=$OPTARG 451 | ;; 452 | o) 453 | export OPENSHIFT_VERSION=$OPTARG 454 | ;; 455 | *) 456 | usage 457 | ;; 458 | esac 459 | done 460 | 461 | shift `expr $OPTIND - 1` 462 | 463 | # Check for EnvVars 464 | [ ! -z "$AWS_PROFILE" ] && echo "🌴 Using AWS_PROFILE: $AWS_PROFILE" 465 | [ -z "$BASE_DOMAIN" ] && echo "🕱 Error: must supply BASE_DOMAIN in env or cli" && exit 1 466 | [ -z "$CLUSTER_NAME" ] && echo "🕱 Error: must supply CLUSTER_NAME in env or cli" && exit 1 467 | [ -z "$PULL_SECRET" ] && echo "🕱 Error: must supply PULL_SECRET in env or cli" && exit 1 468 | [ -z "$SSH_KEY" ] && echo "🕱 Error: must supply SSH_KEY in env or cli" && exit 1 469 | [ -z "$INSTANCE_TYPE" ] && echo "🕱 Error: must supply INSTANCE_TYPE in env or cli" && exit 1 470 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_ACCESS_KEY_ID" ] && echo "🕱 Error: AWS_ACCESS_KEY_ID not set in env" && exit 1 471 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_SECRET_ACCESS_KEY" ] && echo "🕱 Error: AWS_SECRET_ACCESS_KEY not set in env" && exit 1 472 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_DEFAULT_REGION" ] && echo "🕱 Error: AWS_DEFAULT_REGION not set in env" && exit 1 473 | 474 | # Download tools if the do not exist 475 | [ ! -r "$RUN_DIR/install-config.yaml" ] && echo -e "💀${ORANGE}: install-config.yaml not found downloading${NC}" && download_install_config 476 | [ ! -r "$RUN_DIR/adjust-single-node.sh" ] && echo -e "💀${ORANGE}: adjust-single-node.sh not found downloading${NC}" && download_adjust_single_node 477 | [ ! -r "$RUN_DIR/adjust-single-node-4.16.sh" ] && echo -e "💀${ORANGE}: adjust-single-node-4.16.sh not found downloading${NC}" && download_adjust_single_node_416 478 | [ ! -r "$RUN_DIR/fix-instance-id.sh" ] && echo -e "💀${ORANGE}: fix-instance-id.sh not found downloading${NC}" && download_fix_instance_id 479 | [ ! -r "$RUN_DIR/ec2-spot-converter" ] && echo -e "💀${ORANGE}: ec2-spot-converter not found downloading${NC}" && download_ec2_converter 480 | [ ! -r "$RUN_DIR/openshift-install-${system_os_flavor}.tar.gz" ] && echo -e "💀${ORANGE}: openshift-install-${system_os_flavor}.tar.gz not found downloading${NC}" && download_openshift_installer 481 | [ ! -r "$RUN_DIR/openshift-client-${system_os_flavor}.tar.gz" ] && echo -e "💀${ORANGE}: openshift-client-${system_os_flavor}.tar.gz not found downloading${NC}" && download_openshift_cli 482 | 483 | all 484 | 485 | echo -e "\n🌻${GREEN}AWS SNO Reconfigured OK.${NC}🌻\n" 486 | exit 0 487 | -------------------------------------------------------------------------------- /adjust-single-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- coding: UTF-8 -*- 3 | 4 | # aws cli v2 - https://github.com/aws/aws-cli/issues/4992 5 | export AWS_PAGER="" 6 | 7 | RED='\033[0;31m' 8 | GREEN='\033[0;32m' 9 | ORANGE='\033[38;5;214m' 10 | NC='\033[0m' # No Color 11 | # env vars 12 | DRYRUN=${DRYRUN:-} 13 | BASE_DOMAIN=${BASE_DOMAIN:-} 14 | CLUSTER_NAME=${CLUSTER_NAME:-} 15 | # prog vars 16 | region= 17 | instance_id= 18 | vpc_id= 19 | master_sg= 20 | eip= 21 | eip_alloc= 22 | public_route_table_id= 23 | private_route_table_ids= 24 | public_hosted_zone= 25 | private_hosted_zone= 26 | private_instance_ip= 27 | nat_gateways= 28 | network_load_balancers= 29 | router_load_balancer= 30 | 31 | find_region() { 32 | if [ ! -z "$AWS_DEFAULT_REGION" ]; then 33 | region="$AWS_DEFAULT_REGION" 34 | else 35 | region=$(aws configure get region) 36 | fi 37 | if [ -z "$region" ]; then 38 | echo -e "🕱${RED}Failed - could not find aws region ?${NC}" 39 | exit 1 40 | else 41 | echo "🌴 Region set to $region" 42 | fi 43 | } 44 | 45 | find_instance_id() { 46 | local tag_value="$1" 47 | instance_id=$(aws ec2 describe-instances \ 48 | --region=${region} \ 49 | --query "Reservations[].Instances[].InstanceId" \ 50 | --filters "Name=tag-value,Values=$tag_value" "Name=instance-state-name,Values=running" \ 51 | --output text) 52 | if [ -z "$instance_id" ]; then 53 | echo -e "🕱${RED}Failed - could not find instance id associated with tag: $tag_value ?${NC}" 54 | exit 1 55 | else 56 | echo "🌴 InstanceId set to $instance_id" 57 | fi 58 | } 59 | 60 | find_vpc_id() { 61 | local tag_value="$1" 62 | vpc_id=$(aws ec2 describe-vpcs --region=${region} \ 63 | --query "Vpcs[].VpcId" \ 64 | --filters "Name=tag-value,Values=$tag_value" \ 65 | --output text) 66 | if [ -z "$vpc_id" ]; then 67 | echo -e "🕱${RED}Failed - could not find vpc id associated with tag: $tag_value ?${NC}" 68 | exit 1 69 | else 70 | echo "🌴 VpcId set to $vpc_id" 71 | fi 72 | } 73 | 74 | find_master_sg() { 75 | local tag_value="$1" 76 | master_sg=$(aws ec2 describe-security-groups \ 77 | --region=${region} \ 78 | --query "SecurityGroups[].GroupId" \ 79 | --filters "Name=vpc-id,Values=${vpc_id}" \ 80 | --filters "Name=tag-value,Values=$tag_value" \ 81 | --output text) 82 | if [ -z $vpc_id ]; then 83 | echo -e "🕱${RED}Failed - could not find master security group associated with vpc: $vpc_id and tag: $tag_value ?${NC}" 84 | exit 1 85 | else 86 | echo "🌴 MasterSecurityGroup set to $master_sg" 87 | fi 88 | } 89 | 90 | update_master_sg() { 91 | set -o pipefail 92 | # update master security group: allow 6443 (tcp) 93 | aws ec2 authorize-security-group-ingress \ 94 | --region=${region} \ 95 | ${DRYRUN:---dry-run} \ 96 | --group-id ${master_sg} \ 97 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 6443, "ToPort":6443, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 98 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 99 | if [ "$?" != 0 ]; then 100 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 101 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 102 | else 103 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 104 | exit 1 105 | fi 106 | fi 107 | # update master security group: allow 30000 to 32767 (tcp & udp) from 0.0.0.0/0 for nodeport services 108 | aws ec2 authorize-security-group-ingress \ 109 | --region=${region} \ 110 | ${DRYRUN:---dry-run} \ 111 | --group-id ${master_sg} \ 112 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 30000, "ToPort":32767, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "udp", "FromPort": 30000, "ToPort":32767, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 113 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 114 | if [ "$?" != 0 ]; then 115 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 116 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 117 | else 118 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 119 | exit 1 120 | fi 121 | fi 122 | # update master security group - add rules that were attached to elb to master from 0.0.0.0/0 to 443, 80, echo 123 | aws ec2 authorize-security-group-ingress \ 124 | --region=${region} \ 125 | ${DRYRUN:---dry-run} \ 126 | --group-id ${master_sg} \ 127 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 443, "ToPort":443, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "tcp", "FromPort": 80, "ToPort":80, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "icmp", "FromPort": 8, "ToPort": -1,"IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 128 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 129 | if [ "$?" != 0 ]; then 130 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 131 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 132 | else 133 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 134 | exit 1 135 | fi 136 | fi 137 | echo -e "${GREEN} -> update_master_sg OK${NC}" 138 | set +o pipefail 139 | } 140 | 141 | tag_eip() { 142 | IFS=$'\n' read -d '' -r -a lines < <(aws ec2 describe-tags --filters "Name=resource-id,Values=$instance_id" --output text) 143 | if [ ! -z "$lines" ]; then 144 | set -o pipefail 145 | for line in "${lines[@]}"; do 146 | read -r type key resourceid resourcetype value <<< "$line" 147 | aws ec2 create-tags --region=${region} --resources ${eip_alloc} --tags Key="$key",Value="$value" 148 | done 149 | echo -e "${GREEN} -> tag_eip [ $eip, $eip_alloc ] OK${NC}" 150 | set +o pipefail 151 | else 152 | echo -e "💀${ORANGE}Warning - tag_eip - could not find any tags for new eip ?${NC}" 153 | fi 154 | } 155 | 156 | find_or_allocate_eip() { 157 | set -o pipefail 158 | # check if we have sno-100 eip already 159 | read -r eip eip_alloc < <(aws ec2 describe-addresses \ 160 | --region=${region} \ 161 | --query "Addresses[].[PublicIp, AllocationId]" \ 162 | --filters "Name=tag-value,Values=sno-100" \ 163 | --output text) 164 | if [ -z "$eip" ]; then 165 | # allocate a new elastic ip address 166 | aws ec2 allocate-address \ 167 | --domain vpc \ 168 | ${DRYRUN:---dry-run} \ 169 | --region=${region} \ 170 | --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 171 | if [ "$?" != 0 ]; then 172 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 173 | echo -e "${GREEN}Ignoring - allocate_eip - dry run set ${NC}" 174 | else 175 | echo -e "🕱${RED}Failed - problem allocating elastic ip ?${NC}" 176 | exit 1 177 | fi 178 | else 179 | read -r eip eip_alloc < <(aws ec2 describe-addresses \ 180 | --region=${region} \ 181 | --query "Addresses[].[PublicIp, AllocationId]" \ 182 | --filters "Name=tag-value,Values=sno-100" \ 183 | --output text) 184 | if [ -z "$eip" ]; then 185 | echo -e "🕱${RED}Failed - problem finding allocated elastic ip ?${NC}" 186 | exit 1 187 | fi 188 | tag_eip 189 | echo -e "${GREEN} -> allocate_eip [ $eip, $eip_alloc ] OK${NC}" 190 | fi 191 | else 192 | echo -e "${GREEN} -> found existing eip [ $eip, $eip_alloc ] OK${NC}" 193 | fi 194 | set +o pipefail 195 | } 196 | 197 | associate_eip() { 198 | set -o pipefail 199 | aws ec2 associate-address \ 200 | --region=${region} \ 201 | ${DRYRUN:---dry-run} \ 202 | --allocation-id ${eip_alloc:-a1000} \ 203 | --instance-id $instance_id 2>&1 | tee /tmp/aws-error-file 204 | if [ "$?" != 0 ]; then 205 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 206 | echo -e "${GREEN}Ignoring - associate_eip - dry run set ${NC}" 207 | else 208 | echo -e "🕱${RED}Failed - could not associate eip $eip with instance $instance_id ?${NC}" 209 | exit 1 210 | fi 211 | else 212 | echo -e "${GREEN} -> associate_eip [ $eip, $eip_alloc, $instance_id ] OK${NC}" 213 | fi 214 | set +o pipefail 215 | } 216 | 217 | find_public_route_table() { 218 | local tag_value="$1" 219 | public_route_table_id=$(aws ec2 describe-route-tables --region=${region} \ 220 | --query "RouteTables[].RouteTableId" \ 221 | --filters "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 222 | --output text) 223 | if [ -z "$public_route_table_id" ]; then 224 | echo -e "🕱${RED}Failed - could not find public route table id tag: $tag_value ?${NC}" 225 | exit 1 226 | else 227 | echo "🌴 PublicRouteTableId set to $public_route_table_id" 228 | fi 229 | } 230 | 231 | find_private_route_tables() { 232 | local tag_value="$1" 233 | private_route_table_ids=$(aws ec2 describe-route-tables --region=${region} \ 234 | --query "RouteTables[].Associations[].RouteTableAssociationId" \ 235 | --filters "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 236 | --output text) 237 | if [ -z "$private_route_table_ids" ]; then 238 | echo -e "💀${ORANGE}Warning - could not find private route table assoc ids for tag: $tag_value - continuing, they may have been deleted already ?${NC}" 239 | else 240 | echo "🌴 PrivateRouteTableIds set to $private_route_table_ids" 241 | fi 242 | } 243 | 244 | update_private_routes() { 245 | set -o pipefail 246 | if [ ! -z "$private_route_table_ids" ]; then 247 | for x in $private_route_table_ids; do 248 | aws ec2 replace-route-table-association \ 249 | ${DRYRUN:---dry-run} \ 250 | --association-id $x \ 251 | --route-table-id $public_route_table_id \ 252 | --region=${region} 2>&1 | tee /tmp/aws-error-file 253 | if [ "$?" != 0 ]; then 254 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 255 | echo -e "${GREEN}Ignoring - update_private_routes - dry run set${NC}" 256 | else 257 | echo -e "🕱${RED}Failed - could not associate private $x with public route table $public_route_table_id ?${NC}" 258 | exit 1 259 | fi 260 | else 261 | echo -e "${GREEN} -> update_private_routes [ $x, $public_route_table_id ] OK${NC}" 262 | fi 263 | done 264 | fi 265 | set +o pipefail 266 | } 267 | 268 | find_public_route53_hostedzone() { 269 | local dns_name="$1" 270 | public_hosted_zone=$(aws route53 list-hosted-zones-by-name \ 271 | --dns-name "$dns_name" \ 272 | --max-items 1 \ 273 | --query "HostedZones[].[Id]" \ 274 | --output text) 275 | if [ -z "$public_hosted_zone" ]; then 276 | echo -e "🕱${RED}Failed - could not find public route53 hostedzone for: $dns_name ?${NC}" 277 | exit 1 278 | else 279 | echo "🌴 PublicHostedZone set to $public_hosted_zone" 280 | fi 281 | } 282 | 283 | update_route53_public() { 284 | if [ -z "$DRYRUN" ]; then 285 | echo -e "${GREEN}Ignoring - update_route53_public - dry run set${NC}" 286 | return 287 | fi 288 | local dns_name="$1" 289 | for x in "api"; do 290 | cat << EOF > /tmp/route53_policy 291 | { 292 | "Changes": [ 293 | { 294 | "Action": "UPSERT", 295 | "ResourceRecordSet": { 296 | "Name": "$x.$dns_name", 297 | "Type": "A", 298 | "TTL": 300, 299 | "ResourceRecords": [ 300 | { 301 | "Value": "$eip" 302 | } 303 | ] 304 | } 305 | } 306 | ] 307 | } 308 | EOF 309 | 310 | aws route53 change-resource-record-sets \ 311 | --region=${region} \ 312 | --hosted-zone-id $(echo ${public_hosted_zone} | sed 's/\/hostedzone\///g') \ 313 | --change-batch file:///tmp/route53_policy 314 | if [ "$?" != 0 ]; then 315 | echo -e "🕱${RED}Failed - could not update route53 public record for $x.$dns_name, $eip ?${NC}" 316 | exit 1 317 | else 318 | echo -e "${GREEN} -> update_route53_public[ $x.$dns_name, $eip ] OK${NC}" 319 | fi 320 | done 321 | } 322 | 323 | find_private_route53_hostedzone() { 324 | local dns_name="$1" 325 | private_hosted_zone=$(aws route53 list-hosted-zones-by-name \ 326 | --dns-name "$dns_name" \ 327 | --max-items 1 \ 328 | --query "HostedZones[].[Id]" \ 329 | --output text) 330 | if [ -z "$private_hosted_zone" ]; then 331 | echo -e "🕱${RED}Failed - could not find private route53 hostedzone for: $dns_name ?${NC}" 332 | exit 1 333 | else 334 | echo "🌴 PrivateHostedZone set to $private_hosted_zone" 335 | fi 336 | } 337 | 338 | find_instance_private_ip_address() { 339 | private_instance_ip=$(aws ec2 describe-instances \ 340 | --region=${region} \ 341 | --filters "Name=instance-id,Values=$instance_id" \ 342 | --query 'Reservations[*].Instances[*].[PrivateIpAddress]' \ 343 | --output text) 344 | if [ -z "$private_instance_ip" ]; then 345 | echo -e "🕱${RED}Failed - could not find private instance ip address for $instance_id ?${NC}" 346 | exit 1 347 | else 348 | echo "🌴 PrivateInstanceIp set to $private_instance_ip" 349 | fi 350 | } 351 | 352 | update_route53_private() { 353 | local dns_name="$1" 354 | if [ -z "$DRYRUN" ]; then 355 | echo -e "${GREEN}Ignoring - update_route53_private - dry run set${NC}" 356 | return 357 | fi 358 | for x in "api-int" "api"; do 359 | cat << EOF > /tmp/route53_policy 360 | { 361 | "Changes": [ 362 | { 363 | "Action": "UPSERT", 364 | "ResourceRecordSet": { 365 | "Name": "$x.$dns_name", 366 | "Type": "A", 367 | "TTL": 300, 368 | "ResourceRecords": [ 369 | { 370 | "Value": "$private_instance_ip" 371 | } 372 | ] 373 | } 374 | } 375 | ] 376 | } 377 | EOF 378 | 379 | aws route53 change-resource-record-sets \ 380 | --region=${region} \ 381 | --hosted-zone-id $(echo ${private_hosted_zone} | sed 's/\/hostedzone\///g') \ 382 | --change-batch file:///tmp/route53_policy 383 | if [ "$?" != 0 ]; then 384 | echo -e "🕱${RED}Failed - could not associate eip $eip with instance $instance_id ?${NC}" 385 | exit 1 386 | else 387 | echo -e "${GREEN} -> associate_eip [ $eip, $eip_alloc, $instance_id ] OK${NC}" 388 | fi 389 | done 390 | } 391 | 392 | find_nat_gateways() { 393 | local tag_value="$1" 394 | nat_gateways=$(aws ec2 describe-nat-gateways --region=${region} \ 395 | --query="NatGateways[].NatGatewayId" \ 396 | --filter "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 397 | --output text) 398 | if [ -z "$nat_gateways" ]; then 399 | echo -e "💀${ORANGE}Warning - could not find nat gateways for tag: $tag_value in vpc $vpcid - continuing, they may have been deleted already ?${NC}" 400 | else 401 | echo "🌴 NatGateways set to $nat_gateways" 402 | fi 403 | } 404 | 405 | delete_nat_gateways() { 406 | set -o pipefail 407 | if [ ! -z "$nat_gateways" ]; then 408 | for x in $nat_gateways; do 409 | aws ec2 delete-nat-gateway \ 410 | ${DRYRUN:---dry-run} \ 411 | --nat-gateway-id $x \ 412 | --region=${region} 2>&1 | tee /tmp/aws-error-file 413 | if [ "$?" != 0 ]; then 414 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 415 | echo -e "${GREEN}Ignoring - delete_nat_gateways - dry run set${NC}" 416 | else 417 | echo -e "🕱${RED}Failed - could not delete nat gateway $x ?${NC}" 418 | exit 1 419 | fi 420 | else 421 | echo -e "${GREEN} -> delete_nat_gateways [ $x ] OK${NC}" 422 | fi 423 | done 424 | fi 425 | set +o pipefail 426 | } 427 | 428 | wait_for_nat_gateway_delete() { 429 | set -o pipefail 430 | if [ ! -z "$nat_gateways" ]; then 431 | aws ec2 wait nat-gateway-deleted \ 432 | ${DRYRUN:---dry-run} \ 433 | --nat-gateway-id $x \ 434 | --region=${region} 2>&1 | tee /tmp/aws-error-file 435 | if [ "$?" != 0 ]; then 436 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 437 | echo -e "${GREEN}Ignoring - wait_for_nat_gateway_delete - dry run set${NC}" 438 | else 439 | echo -e "🕱${RED}Failed - waiting for nat gateways to delete failed - $nat_gateways ?${NC}" 440 | exit 1 441 | fi 442 | else 443 | echo -e "${GREEN} -> wait_for_nat_gateway_delete [ $nat_gateways ] OK${NC}" 444 | fi 445 | fi 446 | if [ ! -z "$DRYRUN" ]; then 447 | sleep 30 # try to avoid auth failure when we release eip next 448 | fi 449 | set +o pipefail 450 | } 451 | 452 | release_eips() { 453 | local tag_value="$1" 454 | IFS=$'\n' read -d '' -r -a lines < <(aws ec2 describe-addresses \ 455 | --region=${region} \ 456 | --query "Addresses[].[PublicIp,AllocationId]" \ 457 | --filters "Name=tag-value,Values=$tag_value" \ 458 | --output text) 459 | if [ ! -z "$lines" ]; then 460 | set -o pipefail 461 | for line in "${lines[@]}"; do 462 | read -r ip alloc_id <<< "$line" 463 | aws ec2 release-address \ 464 | ${DRYRUN:---dry-run} \ 465 | --region=${region} \ 466 | --allocation-id $alloc_id 2>&1 | tee /tmp/aws-error-file 467 | if [ "$?" != 0 ]; then 468 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 469 | echo -e "${GREEN}Ignoring - release_eips - dry run set${NC}" 470 | else 471 | echo -e "🕱${RED}Failed - could not release eip $ip $alloc_id ?${NC}" 472 | exit 1 473 | fi 474 | else 475 | echo -e "${GREEN} -> release_eips [ $ip, $alloc_id ] OK${NC}" 476 | fi 477 | done 478 | set +o pipefail 479 | else 480 | echo -e "💀${ORANGE}Warning - could not find any eips to release - continuing, they may have been deleted already ?${NC}" 481 | fi 482 | } 483 | 484 | find_network_load_balancers() { 485 | query='LoadBalancers[?VpcId==`'${vpc_id}'`]|[].LoadBalancerArn' 486 | network_load_balancers=$(aws elbv2 describe-load-balancers --region=${region} \ 487 | --query $query \ 488 | --output text) 489 | if [ -z "$network_load_balancers" ]; then 490 | echo -e "💀${ORANGE}Warning - could not find load balancers - continuing, they may have been deleted already ?${NC}" 491 | else 492 | echo "🌴 NetworkLoadBalancers set to $network_load_balancers" 493 | fi 494 | } 495 | 496 | delete_network_load_balancers() { 497 | if [ -z "$DRYRUN" ]; then 498 | echo -e "${GREEN}Ignoring - delete_load_balancers - dry run set${NC}" 499 | return 500 | fi 501 | if [ ! -z "$network_load_balancers" ]; then 502 | for x in $network_load_balancers; do 503 | aws elbv2 delete-load-balancer \ 504 | --region=${region} \ 505 | --load-balancer-arn $x 506 | if [ "$?" != 0 ]; then 507 | echo -e "🕱${RED}Failed - could not delete load balancer $x ?${NC}" 508 | exit 1 509 | else 510 | echo -e "${GREEN} -> delete_load_balancers [ $x ] OK${NC}" 511 | fi 512 | done 513 | fi 514 | } 515 | 516 | delete_target_groups() { 517 | if [ -z "$DRYRUN" ]; then 518 | echo -e "${GREEN}Ignoring - delete_target_groups - dry run set${NC}" 519 | return 520 | fi 521 | query='TargetGroups|[].TargetGroupArn' 522 | target_groups=$(aws elbv2 describe-target-groups --query $query) 523 | if [ ! -z "$target_groups" ]; then 524 | echo $target_groups | jq '.[]' | while read -r tg; do aws elbv2 delete-target-group --target-group-arn $(echo $tg | tr -d '"'); done 525 | if [ "$?" != 0 ]; then 526 | echo -e "🕱${RED}Failed - could not delete target groups $target_groups ?${NC}" 527 | exit 1 528 | else 529 | echo -e "${GREEN} -> delete_target_groups [ $target_groups ] OK${NC}" 530 | fi 531 | fi 532 | } 533 | 534 | # do it all 535 | all() { 536 | echo "🌴 BASE_DOMAIN set to $BASE_DOMAIN" 537 | echo "🌴 CLUSTER_NAME set to $CLUSTER_NAME" 538 | 539 | # find ids 540 | find_region 541 | find_instance_id "$CLUSTER_NAME-*-master-0" 542 | find_vpc_id "$CLUSTER_NAME-*-vpc" 543 | find_master_sg "$CLUSTER_NAME-*-master-sg" 544 | 545 | # updates 546 | update_master_sg 547 | find_or_allocate_eip 548 | associate_eip 549 | 550 | find_public_route_table "$CLUSTER_NAME-*-public" 551 | find_private_route_tables "$CLUSTER_NAME-*-private-*" 552 | update_private_routes 553 | 554 | find_public_route53_hostedzone "$BASE_DOMAIN" 555 | update_route53_public "$CLUSTER_NAME.$BASE_DOMAIN" 556 | find_instance_private_ip_address 557 | find_private_route53_hostedzone "$CLUSTER_NAME.$BASE_DOMAIN" 558 | update_route53_private "$CLUSTER_NAME.$BASE_DOMAIN" 559 | 560 | find_nat_gateways "$CLUSTER_NAME-*-nat-*" 561 | delete_nat_gateways 562 | wait_for_nat_gateway_delete 563 | release_eips "$CLUSTER_NAME-*-eip-*" 564 | 565 | find_network_load_balancers 566 | delete_network_load_balancers 567 | delete_target_groups 568 | } 569 | 570 | usage() { 571 | cat <&1 572 | usage: $0 [ -d -b -c ] 573 | 574 | Adjust SNO instance networking. By default dry-run is ON ! You must set -d to doIt™️ 575 | -d do it ! no dry run - else we print out whats going to happen and any non desructive lookups 576 | 577 | Optional arguments if not set in environment: 578 | 579 | -b BASE_DOMAIN - openshift base domain (or export BASE_DOMAIN env var) 580 | -c CLUSTER_NAME - openshift cluster name (or export CLUSTER_NAME env var) 581 | 582 | This script is rerunnable. 583 | 584 | Environment Variables: 585 | Export these in your environment. 586 | 587 | AWS_PROFILE use a pre-configured aws profile (~/.aws/config and ~/.aws/credentials) 588 | 589 | OR export these in your environment: 590 | 591 | AWS_ACCESS_KEY_ID 592 | AWS_SECRET_ACCESS_KEY 593 | AWS_DEFAULT_REGION 594 | 595 | Optionally if not set on command line: 596 | 597 | BASE_DOMAIN 598 | CLUSTER_NAME 599 | 600 | EOF 601 | exit 1 602 | } 603 | 604 | while getopts b:c:d opt; do 605 | case $opt in 606 | b) 607 | BASE_DOMAIN=$OPTARG 608 | ;; 609 | c) 610 | CLUSTER_NAME=$OPTARG 611 | ;; 612 | d) 613 | DRYRUN="--no-dry-run" 614 | ;; 615 | *) 616 | usage 617 | ;; 618 | esac 619 | done 620 | 621 | shift `expr $OPTIND - 1` 622 | 623 | # Check for EnvVars 624 | [ ! -z "$AWS_PROFILE" ] && echo "🌴 Using AWS_PROFILE: $AWS_PROFILE" 625 | [ -z "$BASE_DOMAIN" ] && echo "🕱 Error: must supply BASE_DOMAIN in env or cli" && exit 1 626 | [ -z "$CLUSTER_NAME" ] && echo "🕱 Error: must supply CLUSTER_NAME in env or cli" && exit 1 627 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_ACCESS_KEY_ID" ] && echo "🕱 Error: AWS_ACCESS_KEY_ID not set in env" && exit 1 628 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_SECRET_ACCESS_KEY" ] && echo "🕱 Error: AWS_SECRET_ACCESS_KEY not set in env" && exit 1 629 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_DEFAULT_REGION" ] && echo "🕱 Error: AWS_DEFAULT_REGION not set in env" && exit 1 630 | 631 | all 632 | 633 | echo -e "\n🌻${GREEN}AWS SNO Reconfigured OK.${NC}🌻\n" 634 | exit 0 635 | -------------------------------------------------------------------------------- /adjust-single-node-4.16.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- coding: UTF-8 -*- 3 | 4 | # aws cli v2 - https://github.com/aws/aws-cli/issues/4992 5 | export AWS_PAGER="" 6 | 7 | RED='\033[0;31m' 8 | GREEN='\033[0;32m' 9 | ORANGE='\033[38;5;214m' 10 | NC='\033[0m' # No Color 11 | # env vars 12 | DRYRUN=${DRYRUN:-} 13 | BASE_DOMAIN=${BASE_DOMAIN:-} 14 | CLUSTER_NAME=${CLUSTER_NAME:-} 15 | # prog vars 16 | region= 17 | instance_id= 18 | vpc_id= 19 | master_sg= 20 | eip= 21 | eip_alloc= 22 | public_route_table_id= 23 | private_route_table_id= 24 | public_hosted_zone= 25 | private_hosted_zone= 26 | private_instance_ip= 27 | nat_gateways= 28 | network_load_balancers= 29 | router_load_balancer= 30 | zones= 31 | 32 | find_region() { 33 | if [ ! -z "$AWS_DEFAULT_REGION" ]; then 34 | region="$AWS_DEFAULT_REGION" 35 | else 36 | region=$(aws configure get region) 37 | fi 38 | if [ -z "$region" ]; then 39 | echo -e "🕱${RED}Failed - could not find aws region ?${NC}" 40 | exit 1 41 | else 42 | echo "🌴 Region set to $region" 43 | fi 44 | } 45 | 46 | find_instance_id() { 47 | local tag_value="$1" 48 | instance_id=$(aws ec2 describe-instances \ 49 | --region=${region} \ 50 | --query "Reservations[].Instances[].InstanceId" \ 51 | --filters "Name=tag-value,Values=$tag_value" "Name=instance-state-name,Values=running" \ 52 | --output text) 53 | if [ -z "$instance_id" ]; then 54 | echo -e "🕱${RED}Failed - could not find instance id associated with tag: $tag_value ?${NC}" 55 | exit 1 56 | else 57 | echo "🌴 InstanceId set to $instance_id" 58 | fi 59 | } 60 | 61 | find_vpc_id() { 62 | local tag_value="$1" 63 | vpc_id=$(aws ec2 describe-vpcs --region=${region} \ 64 | --query "Vpcs[].VpcId" \ 65 | --filters "Name=tag-value,Values=$tag_value" \ 66 | --output text) 67 | if [ -z "$vpc_id" ]; then 68 | echo -e "🕱${RED}Failed - could not find vpc id associated with tag: $tag_value ?${NC}" 69 | exit 1 70 | else 71 | echo "🌴 VpcId set to $vpc_id" 72 | fi 73 | } 74 | 75 | find_master_sg() { 76 | tag_value="$CLUSTER_NAME-*-master" 77 | 78 | master_sg=$(aws ec2 describe-security-groups \ 79 | --region=${region} \ 80 | --query "SecurityGroups[].GroupId" \ 81 | --filters "Name=vpc-id,Values=${vpc_id}" \ 82 | --filters "Name=tag-value,Values=$tag_value" \ 83 | --output text) 84 | 85 | if [ -z $master_sg ]; then 86 | tag_value="$CLUSTER_NAME-*-node" 87 | 88 | master_sg=$(aws ec2 describe-security-groups \ 89 | --region=${region} \ 90 | --query "SecurityGroups[].GroupId" \ 91 | --filters "Name=vpc-id,Values=${vpc_id}" \ 92 | --filters "Name=tag-value,Values=$tag_value" \ 93 | --output text) 94 | fi 95 | 96 | if [ -z $master_sg ]; then 97 | echo -e "🕱${RED}Failed - could not find master/node security group associated with vpc: $vpc_id and tag: $tag_value ?${NC}" 98 | exit 1 99 | else 100 | echo "🌴 MasterSecurityGroup set to $master_sg" 101 | fi 102 | } 103 | 104 | update_master_sg() { 105 | set -o pipefail 106 | # update master security group: allow 6443 (tcp) 107 | aws ec2 authorize-security-group-ingress \ 108 | --region=${region} \ 109 | ${DRYRUN:---dry-run} \ 110 | --group-id ${master_sg} \ 111 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 6443, "ToPort":6443, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 112 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 113 | if [ "$?" != 0 ]; then 114 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 115 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 116 | else 117 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 118 | exit 1 119 | fi 120 | fi 121 | # update master security group: allow 30000 to 32767 (tcp & udp) from 0.0.0.0/0 for nodeport services 122 | aws ec2 authorize-security-group-ingress \ 123 | --region=${region} \ 124 | ${DRYRUN:---dry-run} \ 125 | --group-id ${master_sg} \ 126 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 30000, "ToPort":32767, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "udp", "FromPort": 30000, "ToPort":32767, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 127 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 128 | if [ "$?" != 0 ]; then 129 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 130 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 131 | else 132 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 133 | exit 1 134 | fi 135 | fi 136 | # update master security group - add rules that were attached to elb to master from 0.0.0.0/0 to 443, 80, echo 137 | aws ec2 authorize-security-group-ingress \ 138 | --region=${region} \ 139 | ${DRYRUN:---dry-run} \ 140 | --group-id ${master_sg} \ 141 | --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 443, "ToPort":443, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "tcp", "FromPort": 80, "ToPort":80, "IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]},{"IpProtocol": "icmp", "FromPort": 8, "ToPort": -1,"IpRanges": [{"CidrIp": "0.0.0.0/0","Description": "sno-100"}]}]' \ 142 | --tag-specifications "ResourceType=security-group-rule,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 143 | if [ "$?" != 0 ]; then 144 | if egrep -q "InvalidPermission.Duplicate|DryRunOperation" /tmp/aws-error-file; then 145 | echo -e "${GREEN}Ignoring - master security group rule already exists or dry-run set ?${NC}" 146 | else 147 | echo -e "🕱${RED}Failed - problem authorizing master securty group setting ?${NC}" 148 | exit 1 149 | fi 150 | fi 151 | echo -e "${GREEN} -> update_master_sg OK${NC}" 152 | set +o pipefail 153 | } 154 | 155 | tag_eip() { 156 | IFS=$'\n' read -d '' -r -a lines < <(aws ec2 describe-tags --filters "Name=resource-id,Values=$instance_id" --output text) 157 | if [ ! -z "$lines" ]; then 158 | set -o pipefail 159 | for line in "${lines[@]}"; do 160 | read -r type key resourceid resourcetype value <<< "$line" 161 | aws ec2 create-tags --region=${region} --resources ${eip_alloc} --tags Key="$key",Value="$value" 162 | done 163 | echo -e "${GREEN} -> tag_eip [ $eip, $eip_alloc ] OK${NC}" 164 | set +o pipefail 165 | else 166 | echo -e "💀${ORANGE}Warning - tag_eip - could not find any tags for new eip ?${NC}" 167 | fi 168 | } 169 | 170 | find_or_allocate_eip() { 171 | set -o pipefail 172 | # check if we have sno-100 eip already 173 | read -r eip eip_alloc < <(aws ec2 describe-addresses \ 174 | --region=${region} \ 175 | --query "Addresses[].[PublicIp, AllocationId]" \ 176 | --filters "Name=tag-value,Values=sno-100" \ 177 | --output text) 178 | if [ -z "$eip" ]; then 179 | # allocate a new elastic ip address 180 | aws ec2 allocate-address \ 181 | --domain vpc \ 182 | ${DRYRUN:---dry-run} \ 183 | --region=${region} \ 184 | --tag-specifications "ResourceType=elastic-ip,Tags=[{Key=sno-100,Value=sno-100}]" 2>&1 | tee /tmp/aws-error-file 185 | if [ "$?" != 0 ]; then 186 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 187 | echo -e "${GREEN}Ignoring - allocate_eip - dry run set ${NC}" 188 | else 189 | echo -e "🕱${RED}Failed - problem allocating elastic ip ?${NC}" 190 | exit 1 191 | fi 192 | else 193 | read -r eip eip_alloc < <(aws ec2 describe-addresses \ 194 | --region=${region} \ 195 | --query "Addresses[].[PublicIp, AllocationId]" \ 196 | --filters "Name=tag-value,Values=sno-100" \ 197 | --output text) 198 | if [ -z "$eip" ]; then 199 | echo -e "🕱${RED}Failed - problem finding allocated elastic ip ?${NC}" 200 | exit 1 201 | fi 202 | tag_eip 203 | echo -e "${GREEN} -> allocate_eip [ $eip, $eip_alloc ] OK${NC}" 204 | fi 205 | else 206 | echo -e "${GREEN} -> found existing eip [ $eip, $eip_alloc ] OK${NC}" 207 | fi 208 | set +o pipefail 209 | } 210 | 211 | associate_eip() { 212 | set -o pipefail 213 | aws ec2 associate-address \ 214 | --region=${region} \ 215 | ${DRYRUN:---dry-run} \ 216 | --allocation-id ${eip_alloc:-a1000} \ 217 | --instance-id $instance_id 2>&1 | tee /tmp/aws-error-file 218 | if [ "$?" != 0 ]; then 219 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 220 | echo -e "${GREEN}Ignoring - associate_eip - dry run set ${NC}" 221 | else 222 | echo -e "🕱${RED}Failed - could not associate eip $eip with instance $instance_id ?${NC}" 223 | exit 1 224 | fi 225 | else 226 | echo -e "${GREEN} -> associate_eip [ $eip, $eip_alloc, $instance_id ] OK${NC}" 227 | fi 228 | set +o pipefail 229 | } 230 | 231 | find_public_route_table() { 232 | local tag_value="$1" 233 | public_route_table_id=$(aws ec2 describe-route-tables --region=${region} \ 234 | --query "RouteTables[].RouteTableId" \ 235 | --filters "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 236 | --output text) 237 | if [ -z "$public_route_table_id" ]; then 238 | echo -e "🕱${RED}Failed - could not find public route table id tag: $tag_value ?${NC}" 239 | exit 1 240 | else 241 | echo "🌴 PublicRouteTableId set to $public_route_table_id" 242 | fi 243 | } 244 | 245 | find_private_route_table() { 246 | local tag_value="$1" 247 | private_route_table_id=$(aws ec2 describe-route-tables --region=${region} \ 248 | --query "RouteTables[].Associations[].RouteTableAssociationId" \ 249 | --filters "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 250 | --output text) 251 | if [ -z "$private_route_table_id" ]; then 252 | echo -e "💀${ORANGE}Warning - could not find private route table assoc id for tag: $tag_value - continuing, they may have been deleted already ?${NC}" 253 | else 254 | echo "🌴 PrivateRouteTableId set to $private_route_table_id" 255 | fi 256 | } 257 | 258 | update_private_route() { 259 | set -o pipefail 260 | if [ ! -z "$private_route_table_id" ]; then 261 | aws ec2 replace-route-table-association \ 262 | ${DRYRUN:---dry-run} \ 263 | --association-id $private_route_table_id \ 264 | --route-table-id $public_route_table_id \ 265 | --region=${region} 2>&1 | tee /tmp/aws-error-file 266 | if [ "$?" != 0 ]; then 267 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 268 | echo -e "${GREEN}Ignoring - update_private_route - dry run set${NC}" 269 | else 270 | echo -e "🕱${RED}Failed - could not associate private $private_route_table_id with public route table $public_route_table_id ?${NC}" 271 | exit 1 272 | fi 273 | else 274 | echo -e "${GREEN} -> update_private_route [ $private_route_table_id, $public_route_table_id ] OK${NC}" 275 | fi 276 | fi 277 | set +o pipefail 278 | } 279 | 280 | update_all_private_routes() { 281 | echo $zones | jq '.[]' | while read -r zone; do 282 | zn=$(echo $zone | tr -d '"') 283 | find_public_route_table "$CLUSTER_NAME-*-public-$zn" 284 | find_private_route_table "$CLUSTER_NAME-*-private-$zn" 285 | update_private_route 286 | done 287 | } 288 | 289 | find_public_route53_hostedzone() { 290 | local dns_name="$1" 291 | public_hosted_zone=$(aws route53 list-hosted-zones-by-name \ 292 | --dns-name "$dns_name" \ 293 | --max-items 1 \ 294 | --query "HostedZones[].[Id]" \ 295 | --output text) 296 | if [ -z "$public_hosted_zone" ]; then 297 | echo -e "🕱${RED}Failed - could not find public route53 hostedzone for: $dns_name ?${NC}" 298 | exit 1 299 | else 300 | echo "🌴 PublicHostedZone set to $public_hosted_zone" 301 | fi 302 | } 303 | 304 | update_route53_public() { 305 | if [ -z "$DRYRUN" ]; then 306 | echo -e "${GREEN}Ignoring - update_route53_public - dry run set${NC}" 307 | return 308 | fi 309 | local dns_name="$1" 310 | for x in "api"; do 311 | cat << EOF > /tmp/route53_policy 312 | { 313 | "Changes": [ 314 | { 315 | "Action": "UPSERT", 316 | "ResourceRecordSet": { 317 | "Name": "$x.$dns_name", 318 | "Type": "A", 319 | "TTL": 300, 320 | "ResourceRecords": [ 321 | { 322 | "Value": "$eip" 323 | } 324 | ] 325 | } 326 | } 327 | ] 328 | } 329 | EOF 330 | 331 | aws route53 change-resource-record-sets \ 332 | --region=${region} \ 333 | --hosted-zone-id $(echo ${public_hosted_zone} | sed 's/\/hostedzone\///g') \ 334 | --change-batch file:///tmp/route53_policy 335 | if [ "$?" != 0 ]; then 336 | echo -e "🕱${RED}Failed - could not update route53 public record for $x.$dns_name, $eip ?${NC}" 337 | exit 1 338 | else 339 | echo -e "${GREEN} -> update_route53_public[ $x.$dns_name, $eip ] OK${NC}" 340 | fi 341 | done 342 | } 343 | 344 | find_private_route53_hostedzone() { 345 | local dns_name="$1" 346 | private_hosted_zone=$(aws route53 list-hosted-zones-by-name \ 347 | --dns-name "$dns_name" \ 348 | --max-items 1 \ 349 | --query "HostedZones[].[Id]" \ 350 | --output text) 351 | if [ -z "$private_hosted_zone" ]; then 352 | echo -e "🕱${RED}Failed - could not find private route53 hostedzone for: $dns_name ?${NC}" 353 | exit 1 354 | else 355 | echo "🌴 PrivateHostedZone set to $private_hosted_zone" 356 | fi 357 | } 358 | 359 | find_instance_private_ip_address() { 360 | private_instance_ip=$(aws ec2 describe-instances \ 361 | --region=${region} \ 362 | --filters "Name=instance-id,Values=$instance_id" \ 363 | --query 'Reservations[*].Instances[*].[PrivateIpAddress]' \ 364 | --output text) 365 | if [ -z "$private_instance_ip" ]; then 366 | echo -e "🕱${RED}Failed - could not find private instance ip address for $instance_id ?${NC}" 367 | exit 1 368 | else 369 | echo "🌴 PrivateInstanceIp set to $private_instance_ip" 370 | fi 371 | } 372 | 373 | update_route53_private() { 374 | local dns_name="$1" 375 | if [ -z "$DRYRUN" ]; then 376 | echo -e "${GREEN}Ignoring - update_route53_private - dry run set${NC}" 377 | return 378 | fi 379 | for x in "api-int" "api"; do 380 | cat << EOF > /tmp/route53_policy 381 | { 382 | "Changes": [ 383 | { 384 | "Action": "UPSERT", 385 | "ResourceRecordSet": { 386 | "Name": "$x.$dns_name", 387 | "Type": "A", 388 | "TTL": 300, 389 | "ResourceRecords": [ 390 | { 391 | "Value": "$private_instance_ip" 392 | } 393 | ] 394 | } 395 | } 396 | ] 397 | } 398 | EOF 399 | 400 | aws route53 change-resource-record-sets \ 401 | --region=${region} \ 402 | --hosted-zone-id $(echo ${private_hosted_zone} | sed 's/\/hostedzone\///g') \ 403 | --change-batch file:///tmp/route53_policy 404 | if [ "$?" != 0 ]; then 405 | echo -e "🕱${RED}Failed - could not associate eip $eip with instance $instance_id ?${NC}" 406 | exit 1 407 | else 408 | echo -e "${GREEN} -> associate_eip [ $eip, $eip_alloc, $instance_id ] OK${NC}" 409 | fi 410 | done 411 | } 412 | 413 | find_nat_gateways() { 414 | local tag_value="$1" 415 | nat_gateways=$(aws ec2 describe-nat-gateways --region=${region} \ 416 | --query="NatGateways[].NatGatewayId" \ 417 | --filter "Name=tag-value,Values=$tag_value" "Name=vpc-id,Values=${vpc_id}" \ 418 | --output text) 419 | if [ -z "$nat_gateways" ]; then 420 | echo -e "💀${ORANGE}Warning - could not find nat gateways for tag: $tag_value in vpc $vpcid - continuing, they may have been deleted already ?${NC}" 421 | else 422 | echo "🌴 NatGateways set to $nat_gateways" 423 | fi 424 | } 425 | 426 | delete_nat_gateways() { 427 | set -o pipefail 428 | if [ ! -z "$nat_gateways" ]; then 429 | for x in $nat_gateways; do 430 | aws ec2 delete-nat-gateway \ 431 | ${DRYRUN:---dry-run} \ 432 | --nat-gateway-id $x \ 433 | --region=${region} 2>&1 | tee /tmp/aws-error-file 434 | if [ "$?" != 0 ]; then 435 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 436 | echo -e "${GREEN}Ignoring - delete_nat_gateways - dry run set${NC}" 437 | else 438 | echo -e "🕱${RED}Failed - could not delete nat gateway $x ?${NC}" 439 | exit 1 440 | fi 441 | else 442 | echo -e "${GREEN} -> delete_nat_gateways [ $x ] OK${NC}" 443 | fi 444 | done 445 | fi 446 | set +o pipefail 447 | } 448 | 449 | wait_for_nat_gateway_delete() { 450 | set -o pipefail 451 | if [ ! -z "$nat_gateways" ]; then 452 | aws ec2 wait nat-gateway-deleted \ 453 | ${DRYRUN:---dry-run} \ 454 | --nat-gateway-id $x \ 455 | --region=${region} 2>&1 | tee /tmp/aws-error-file 456 | if [ "$?" != 0 ]; then 457 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 458 | echo -e "${GREEN}Ignoring - wait_for_nat_gateway_delete - dry run set${NC}" 459 | else 460 | echo -e "🕱${RED}Failed - waiting for nat gateways to delete failed - $nat_gateways ?${NC}" 461 | exit 1 462 | fi 463 | else 464 | echo -e "${GREEN} -> wait_for_nat_gateway_delete [ $nat_gateways ] OK${NC}" 465 | fi 466 | fi 467 | if [ ! -z "$DRYRUN" ]; then 468 | sleep 30 # try to avoid auth failure when we release eip next 469 | fi 470 | set +o pipefail 471 | } 472 | 473 | release_eips() { 474 | local tag_value="$1" 475 | IFS=$'\n' read -d '' -r -a lines < <(aws ec2 describe-addresses \ 476 | --region=${region} \ 477 | --query "Addresses[].[PublicIp,AllocationId]" \ 478 | --filters "Name=tag-value,Values=$tag_value" \ 479 | --output text) 480 | if [ ! -z "$lines" ]; then 481 | set -o pipefail 482 | for line in "${lines[@]}"; do 483 | read -r ip alloc_id <<< "$line" 484 | aws ec2 release-address \ 485 | ${DRYRUN:---dry-run} \ 486 | --region=${region} \ 487 | --allocation-id $alloc_id 2>&1 | tee /tmp/aws-error-file 488 | if [ "$?" != 0 ]; then 489 | if egrep -q "DryRunOperation" /tmp/aws-error-file; then 490 | echo -e "${GREEN}Ignoring - release_eips - dry run set${NC}" 491 | else 492 | echo -e "🕱${RED}Failed - could not release eip $ip $alloc_id ?${NC}" 493 | exit 1 494 | fi 495 | else 496 | echo -e "${GREEN} -> release_eips [ $ip, $alloc_id ] OK${NC}" 497 | fi 498 | done 499 | set +o pipefail 500 | else 501 | echo -e "💀${ORANGE}Warning - could not find any eips to release - continuing, they may have been deleted already ?${NC}" 502 | fi 503 | } 504 | 505 | find_network_load_balancers() { 506 | query='LoadBalancers[?VpcId==`'${vpc_id}'`]|[].LoadBalancerArn' 507 | network_load_balancers=$(aws elbv2 describe-load-balancers --region=${region} \ 508 | --query $query \ 509 | --output text) 510 | if [ -z "$network_load_balancers" ]; then 511 | echo -e "💀${ORANGE}Warning - could not find load balancers - continuing, they may have been deleted already ?${NC}" 512 | else 513 | echo "🌴 NetworkLoadBalancers set to $network_load_balancers" 514 | fi 515 | } 516 | 517 | delete_network_load_balancers() { 518 | if [ -z "$DRYRUN" ]; then 519 | echo -e "${GREEN}Ignoring - delete_load_balancers - dry run set${NC}" 520 | return 521 | fi 522 | if [ ! -z "$network_load_balancers" ]; then 523 | for x in $network_load_balancers; do 524 | aws elbv2 delete-load-balancer \ 525 | --region=${region} \ 526 | --load-balancer-arn $x 527 | if [ "$?" != 0 ]; then 528 | echo -e "🕱${RED}Failed - could not delete load balancer $x ?${NC}" 529 | exit 1 530 | else 531 | echo -e "${GREEN} -> delete_load_balancers [ $x ] OK${NC}" 532 | fi 533 | done 534 | fi 535 | } 536 | 537 | delete_target_groups() { 538 | if [ -z "$DRYRUN" ]; then 539 | echo -e "${GREEN}Ignoring - delete_target_groups - dry run set${NC}" 540 | return 541 | fi 542 | query='TargetGroups|[].TargetGroupArn' 543 | target_groups=$(aws elbv2 describe-target-groups --query $query) 544 | if [ ! -z "$target_groups" ]; then 545 | echo $target_groups | jq '.[]' | while read -r tg; do aws elbv2 delete-target-group --target-group-arn $(echo $tg | tr -d '"'); done 546 | if [ "$?" != 0 ]; then 547 | echo -e "🕱${RED}Failed - could not delete target groups $target_groups ?${NC}" 548 | exit 1 549 | else 550 | echo -e "${GREEN} -> delete_target_groups [ $target_groups ] OK${NC}" 551 | fi 552 | fi 553 | } 554 | 555 | find_zones() { 556 | if [ -z "$DRYRUN" ]; then 557 | echo -e "${GREEN}Ignoring - find_zones - dry run set${NC}" 558 | return 559 | fi 560 | query='AvailabilityZones[].ZoneName' 561 | zones=$(aws ec2 describe-availability-zones --query $query) 562 | if [ -z "$zones" ]; then 563 | echo -e "🕱${RED}Failed - could not find zones ?${NC}" 564 | exit 1 565 | fi 566 | echo -e "${GREEN} -> find_zones [ $zones ] OK${NC}" 567 | } 568 | 569 | # do it all 570 | all() { 571 | echo "🌴 BASE_DOMAIN set to $BASE_DOMAIN" 572 | echo "🌴 CLUSTER_NAME set to $CLUSTER_NAME" 573 | 574 | # find ids 575 | find_region 576 | find_zones 577 | find_instance_id "$CLUSTER_NAME-*-master-0" 578 | find_vpc_id "$CLUSTER_NAME-*-vpc" 579 | find_master_sg 580 | 581 | # updates 582 | update_master_sg 583 | find_or_allocate_eip 584 | associate_eip 585 | update_all_private_routes 586 | 587 | find_public_route53_hostedzone "$BASE_DOMAIN" 588 | update_route53_public "$CLUSTER_NAME.$BASE_DOMAIN" 589 | find_instance_private_ip_address 590 | find_private_route53_hostedzone "$CLUSTER_NAME.$BASE_DOMAIN" 591 | update_route53_private "$CLUSTER_NAME.$BASE_DOMAIN" 592 | 593 | find_nat_gateways "$CLUSTER_NAME-*-nat" 594 | delete_nat_gateways 595 | wait_for_nat_gateway_delete 596 | release_eips "$CLUSTER_NAME-*-eip-*" 597 | 598 | find_network_load_balancers 599 | delete_network_load_balancers 600 | delete_target_groups 601 | } 602 | 603 | usage() { 604 | cat <&1 605 | usage: $0 [ -d -b -c ] 606 | 607 | Adjust SNO instance networking. By default dry-run is ON ! You must set -d to doIt™️ 608 | -d do it ! no dry run - else we print out whats going to happen and any non desructive lookups 609 | 610 | Optional arguments if not set in environment: 611 | 612 | -b BASE_DOMAIN - openshift base domain (or export BASE_DOMAIN env var) 613 | -c CLUSTER_NAME - openshift cluster name (or export CLUSTER_NAME env var) 614 | 615 | This script is rerunnable. 616 | 617 | Environment Variables: 618 | Export these in your environment. 619 | 620 | AWS_PROFILE use a pre-configured aws profile (~/.aws/config and ~/.aws/credentials) 621 | 622 | OR export these in your environment: 623 | 624 | AWS_ACCESS_KEY_ID 625 | AWS_SECRET_ACCESS_KEY 626 | AWS_DEFAULT_REGION 627 | 628 | Optionally if not set on command line: 629 | 630 | BASE_DOMAIN 631 | CLUSTER_NAME 632 | 633 | EOF 634 | exit 1 635 | } 636 | 637 | while getopts b:c:d opt; do 638 | case $opt in 639 | b) 640 | BASE_DOMAIN=$OPTARG 641 | ;; 642 | c) 643 | CLUSTER_NAME=$OPTARG 644 | ;; 645 | d) 646 | DRYRUN="--no-dry-run" 647 | ;; 648 | *) 649 | usage 650 | ;; 651 | esac 652 | done 653 | 654 | shift `expr $OPTIND - 1` 655 | 656 | # Check for EnvVars 657 | [ ! -z "$AWS_PROFILE" ] && echo "🌴 Using AWS_PROFILE: $AWS_PROFILE" 658 | [ -z "$BASE_DOMAIN" ] && echo "🕱 Error: must supply BASE_DOMAIN in env or cli" && exit 1 659 | [ -z "$CLUSTER_NAME" ] && echo "🕱 Error: must supply CLUSTER_NAME in env or cli" && exit 1 660 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_ACCESS_KEY_ID" ] && echo "🕱 Error: AWS_ACCESS_KEY_ID not set in env" && exit 1 661 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_SECRET_ACCESS_KEY" ] && echo "🕱 Error: AWS_SECRET_ACCESS_KEY not set in env" && exit 1 662 | [ -z "$AWS_PROFILE" ] && [ -z "$AWS_DEFAULT_REGION" ] && echo "🕱 Error: AWS_DEFAULT_REGION not set in env" && exit 1 663 | 664 | all 665 | 666 | echo -e "\n🌻${GREEN}AWS SNO Reconfigured OK.${NC}🌻\n" 667 | exit 0 668 | --------------------------------------------------------------------------------