From 550b490e693f9501f48b0f80d5902be2c3afecd1 Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Tue, 10 Jan 2023 16:36:14 +0100 Subject: [PATCH 1/6] Infrastructure Deployment API implementation --- README.md | 82 +++++++--- VERSION | 2 +- api/README.md | 51 +++++++ api/common.sh | 92 +++++++++++ api/deployer/bash-aws/main.sh | 158 +++++++++++++++++++ api/deployer/example/main.sh | 46 ++++++ common.sh | 73 --------- crc-cloud.sh | 280 ++++++++++++++-------------------- 8 files changed, 526 insertions(+), 258 deletions(-) create mode 100644 api/README.md create mode 100644 api/common.sh create mode 100644 api/deployer/bash-aws/main.sh create mode 100644 api/deployer/example/main.sh delete mode 100644 common.sh diff --git a/README.md b/README.md index a14f148f..215b3abd 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,49 @@ I stumbled upon OpenSpot (https://github.com/ksingh7/openspot) made by my collea Moreover the solution was based on CRC that creates a qemu VM to run the (single-node) cluster, so bare metal instances were needed and the startup time was too long for the purpose. We had a meeting and they gave me all the detailed instructions on how to run the qemu image directly on AWS standard EC2 instances and configure properly the OpenShift single-node cluster, only the code was missing.... -## Cloud Providers -For the moment only AWS is supported. Other will be added soon. +## Infrastructure Deployers + +In order to abstract the Infrastructure and the OpenShift Instance provisioning has been developed an **Infrastructure Deployer API**. If you're interested on how to implement a new Infrastructure Deployer please refer to the [documentation](api/deployer/README.md). + +### Available Deployers +| Name | Status| +--- | ---| +| bash-aws| Stable (Default)| + +### bash-aws + +This deployer is designed to deploy **CRC-Cloud** on AWS. It's build on top the AWS CLI v2 and it's logic relies on bash scripting. + +#### Prerequisites + +- create an access key for your AWS account and grab the *ACCESS_KEY_ID* and the *SECRET_ACCESS_KEY* (instructions can be found [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html)) +- AWS CLI Installed and in $PATH +
+
+ +The AWS instance type of choice is *c6in.2xlarge* with 8vcpu and 16 GB of RAM. +This instance will cost ~0.45$ per hour (price may vary depending from the region) and will take ~11 minutes to have a working cluster. +Increasing or decreasing the resources will affect the deployment time together with the price per hour. If you want to change instance type keep in mind that the minimum hardware requirements to run CRC (on which this solution is based) are 4vcpus and 8GB of RAM, please refer to the [documentation](https://developers.redhat.com/blog/2019/09/05/red-hat-openshift-4-on-your-laptop-introducing-red-hat-codeready-containers) for further informations. + + +#### CLI arguments +| Argument | Description | Mandatory | + --- | --- | --- | + | -a | AMI ID (Amazon Machine Image) from which the VM will be Instantiated | false | + | -i | EC2 Instance Type | false + +#### Container variables +| Variable | Description | Mandatory | +| --- | --- | --- | +| AWS_ACCESS_KEY_ID | AWS access key (infos [here](#bash-aws-deployer-prereq)) | true | +| AWS_SECRET_ACCESS_KEY | AWS secret access key (infos [here](#bash-aws-deployer-prereq)) | true | +| AWS_DEFAULT_REGION | AWS region where the cluster will be deployed ( currently us-west-2 is the only supported) | true | +| INSTANCE_TYPE | AWS EC2 Instance Type | false | +| AMI_ID | AMI ID (Amazon Machine Image) from which the VM will be Instantiated | false | +

+ **Note:** AWS AMIs (Amazon Machine Images) are regional resources so,for the moment, the only supported region is **us-west-2**. In the next few days the AMI will be copied to other regions, please be patient, it will take a while. ## Usage @@ -27,15 +66,9 @@ For the moment only AWS is supported. Other will be added soon. The basic requirements to run a single-node OpenShift cluster with **CRC-Cloud** are: - register a Red Hat account and get a pull secret from https://console.redhat.com/openshift/create/local -- create an access key for your AWS account and grab the *ACCESS_KEY_ID* and the *SECRET_ACCESS_KEY* (instructions can be found [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html)) -
-
-The AWS instance type of choice is *c6in.2xlarge* with 8vcpu and 16 GB of RAM. -This instance will cost ~0.45$ per hour (price may vary depending from the region) and will take ~11 minutes to have a working cluster. -Increasing or decreasing the resources will affect the deployment time together with the price per hour. If you want to change instance type keep in mind that the minimum hardware requirements to run CRC (on which this solution is based) are 4vcpus and 8GB of RAM, please refer to the [documentation](https://developers.redhat.com/blog/2019/09/05/red-hat-openshift-4-on-your-laptop-introducing-red-hat-codeready-containers) for further informations. -**WARNING:** Running VM instances will cost you **real money** so be extremely careful to verify that all the resources instantiated are **removed** once you're done and remember that you're running them at **your own risk and cost** +**WARNING:** Running VM instances on cloud will cost you **real money** so be extremely careful to verify that all the resources instantiated are **removed** once you're done and remember that you're running them at **your own risk and cost** @@ -57,7 +90,7 @@ Please **be careful** on deleting the working directory content because without **NOTE (podman only):** In order to make the mounted workdir read-write accessible from the container is need to change the SELinux security context related to the folder with the following command ```chcon -Rt svirt_sandbox_file_t ``` -#### Single node cluster creation +#### Single node cluster creation ([bash-aws](#bash-aws-deployer) Infrastructure deployer) ``` run -v :/workdir\ -e WORKING_MODE=C\ @@ -68,7 +101,7 @@ Please **be careful** on deleting the working directory content because without -ti quay.io/crcont/crc-cloud ``` -#### Single node cluster teardown +#### Single node cluster teardown ([bash-aws](#bash-aws-deployer) Infrastructure deployer) ``` run -v :/workdir\ -e WORKING_MODE=T\ @@ -82,6 +115,8 @@ Please **be careful** on deleting the working directory content because without #### Environment variables Environment variables will be passed to the container from the command line invocation with the ```-e VARIABLE=VALUE``` option that you can find above. +**NOTE:** Every deployer may have its own environment variables please refer to the [Infrastructure Deployer Section](#deployer) for further details. + ##### Mandatory Variables **Cluster creation** @@ -90,9 +125,7 @@ Environment variables will be passed to the container from the command line invo |---|---| | WORKING_MODE | C (creation mode) | | PULL_SECRET | base64 string of the Red Hat account pull secret ( it is recommended to use the command substitution to generate the string as described above) | -| AWS_ACCESS_KEY_ID | AWS access key (infos [here](#prereq)) | -| AWS_SECRET_ACCESS_KEY | AWS secret access key (infos [here](#prereq)) | -| AWS_DEFAULT_REGION | AWS region where the cluster will be deployed ( currently us-west-2 is the only supported) + **Cluster teardown** @@ -114,6 +147,8 @@ Environment variables will be passed to the container from the command line invo | PASS_KUBEADMIN | overrides the default password (kubeadmin) for kubeadmin account | | PASS_REDHAT | overrides the default password (redhat) for redhat account | | INSTANCE_TYPE | overrides the default AWS instance type (c6in.2xlarge, infos [here](#prereq)) | +| DEPLOYER_API | selects the infrastructure deployer ( please refer to [deployer API documentation](api/deployer/README.md) + @@ -122,13 +157,14 @@ Environment variables will be passed to the container from the command line invo To run **CRC-Cloud** from your command line you must be on Linux, be sure to have installed and configured the following programs in your box - bash (>=v4) -- AWS CLI - jq - md5sum - curl - head - ssh-keygen - GNU sed +- GNU grep +- cat - nc (netcat) - ssh client - scp @@ -148,15 +184,16 @@ at the end of the process the script will print the public address of the consol Below you'll find all the options available ``` -./crc-cloud.sh -C -p pull secret path [-d developer user password] [-k kubeadmin user password] [-r redhat user password] [-a AMI ID] [-t Instance type] +./crc-cloud.sh -C -p pull secret path [-D infrastructure_deployer] [-d developer user password] [-k kubeadmin user password] [-r redhat user password] [-a AMI ID] [-t Instance type] where: + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) -C Cluster Creation mode -p pull secret file path (download from https://console.redhat.com/openshift/create/local) - -d developer user password (optional, default: developer) - -k kubeadmin user password (optional, default: kubeadmin) - -r redhat user password (optional, default: redhat) - -a AMI ID (Amazon Machine Image) from which the VM will be Instantiated (optional, default: ami-0569ce8a44f2351be) - -i EC2 Instance Type (optional, default; c6in.2xlarge) + -d developer user password (optional, default: $PASS_DEVELOPER) + -k kubeadmin user password (optional, default: $PASS_KUBEADMIN) + -r redhat user password (optional, default: $PASS_REDHAT) + -a AMI ID (Amazon Machine Image) from which the VM will be Instantiated (optional, default: $AMI_ID) + -i EC2 Instance Type (optional, default; $INSTANCE_TYPE) -h show this help text ``` #### Single node cluster teardown @@ -165,7 +202,8 @@ To teardown the single node cluster the basic command is this will refer to the *latest* run found in ```/workspace```, if you have several run folders in your workspace, you can specify the one you want to teardown with the parameter ```-v ``` where `````` corresponds to the numeric folder name containing the metadata of the cluster that will be deleted ``` -./crc-cloud.sh -T [-v run id] +./crc-cloud.sh -T [-D infrastructure_deployer] [-v run id] + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) -T Cluster Teardown mode -v The Id of the run that is gonna be destroyed, corresponds with the numeric name of the folders created in workdir (optional, default: latest) -h show this help text diff --git a/VERSION b/VERSION index 6b3126ce..3a6a8d03 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.0 +v1.1 diff --git a/api/README.md b/api/README.md new file mode 100644 index 00000000..6b53e331 --- /dev/null +++ b/api/README.md @@ -0,0 +1,51 @@ +# CRC-Cloud Infrastructure Deployer API + +The Infrastructure Deployer API has been designed to abstract the Infrastructure provisioning from the OpenShift instance provisioning. The first version of CRC-Cloud was relying on AWS and AWS CLI, but as soon as the project was gaining interest, we started considering to support other cloud providers so we decided to implement this abstraction to easily implement other deployment technologies such as IaC tools like [Ansible](https://www.redhat.com/it/engage/delivery-with-ansible-20170906?sc_cid=7013a000002w14JAAQ&gclid=EAIaIQobChMIwLPlpZG9_AIVA5zVCh2EPw9VEAAYASAAEgJJXfD_BwE&gclsrc=aw.ds), [Terraform](https://terraform.io), [Pulumi](https://https://www.pulumi.com/) etc. + +## Plugin loading and API implementation +In order to be loaded, an Infrastructure Deployer Plugin must have a folder in ```api/deployer```. This folder must have the name of the plugin that will be passed to ```crc-cloud.sh``` with the ```-D``` option, so for example, if you want to create a plugin named ```my-deployer``` the plugin code and resources will be stored in ```/api/deployer/my-deployer```. +You can find an example implementation from which start to develop a new plugin in ```api/deployer/example``` (and you can even run it!!). +The plugin folder must contain a ```main.sh``` script that is the entrypoint of the plugin. The ```main.sh``` must implement the following methods: + +``` +deployer_create() { + #all the command line args will be passed to that function + pr_info "creates the infrastructure" + exit 0 +} + +deployer_teardown() { + #all the command line args will be passed to that function + pr_info "destroys infrastructure" + exit 0 +} + +deployer_get_eip() { + echo "return the external (public ip) of the VM" +} + +deployer_get_iip() { + echo "return internal ip (within the cloud infrastructure) of the VM" +} + +deployer_usage() { + echo "prints the usage of the infrastructure deployer plugin including all its cli parameters" +} +``` + +The **CRC-Cloud** engine will check if these methods are implemented and will eventually exit it they're not. +All the plugin specific resources (other scripts, IaC definitions etc.) should be kept into the plugin folder and the paths that refer to them must be valorized accordingly by the developer. + +## Global Variables + +The **CRC-Cloud** engine will expose to the plugin some variables that must be used to keep the logic consistent with the engine itself + +| Variable | Type| Description | +| --- | --- | --- | +| $CONTAINER | Boolean | It's valorized if the script is running inside a container | +| $RANDOM_SUFFIX | String | It's the random suffix applied to the resources created inside the cloud provider in order to avoid conflicts with other **CRC-Cloud** instances running in the same namespace (can be ignored if the deployment methods provides it's own logic) | +| $WORKDIR | String | That's the folder where all the deployment status infos must be stored (will be created by the engine) | + + ## *Private* methods names conventions + + In order to increase code readability, non interface method names (kinda private) must start with an underscore "_" \ No newline at end of file diff --git a/api/common.sh b/api/common.sh new file mode 100644 index 00000000..19c5a978 --- /dev/null +++ b/api/common.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +pr_info() { + if [[ $WORKING_MODE == "C" ]] + then + echo "[INF] $1" | (tee -a $LOG_FILE 2>/dev/null) + else + echo "[INF] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) + fi +} + +pr_error() { + if [[ $WORKING_MODE == "C" ]] + then + echo "[ERR] $1" | (tee -a $LOG_FILE 2>/dev/null) + else + echo "[ERR] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) + fi +} + +pr_end() { + if [[ $WORKING_MODE == "C" ]] + then + echo "[END] $1" | (tee -a $LOG_FILE 2>/dev/null) + else + echo "[END] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) + fi +} + +stop_if_failed(){ + EXIT_CODE=$1 + MESSAGE=$2 + if [[ $EXIT_CODE != 0 ]] + then + pr_error "$MESSAGE" + exit $EXIT_CODE + fi +} + +check_ssh(){ + $NC -z $1 $SSH_PORT > /dev/null 2>&1 + return $? +} + +wait_instance_readiness(){ + RES=1 + while [[ $RES != 0 ]] + do + check_ssh $1 + RES=$? + sleep 1 + pr_info "waiting sshd to become ready on $1, hang on...." + done +} + + +api_load_deployer() { + [ ! -d "$DEPLOYER_API_FOLDER/$1" ] && stop_if_failed 1 "Deployer API $1 folder not found in $DEPLOYER_API_FOLDER/$1, please refer api/README.md for API specifications" + [ ! -f "$DEPLOYER_API_FOLDER/$1/main.sh" ] && stop_if_failed 1 "main.sh not found for deployer $1 in folder $DEPLOYER_API_FOLDER/$1, please refer api/README.md for API specifications" + source $DEPLOYER_API_FOLDER/$1/main.sh + [[ ! `declare -F deployer_create` ]] &&\ + stop_if_failed 1 "deployer_create method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + + [[ ! `declare -F deployer_teardown` ]] &&\ + stop_if_failed 1 "deployer_teardown method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + + [[ ! `declare -F deployer_get_eip` ]] &&\ + stop_if_failed 1 "deployer_get_eip method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + + [[ ! `declare -F deployer_get_iip` ]] &&\ + stop_if_failed 1 "deployer_get_iip method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + + [[ ! `declare -F deployer_usage` ]] &&\ + stop_if_failed 1 "deployer_usage method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + pr_info "successfully loaded $1 deployer API" + + # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE + for i in `$CAT $DEPLOYER_API_FOLDER/$1/main.sh | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` + do + case "$i" in + deployer_create);; + deployer_teardown);; + deployer_get_eip);; + deployer_get_iip);; + deployer_usage);; + *) + [[ ${i::1} != '_' ]] && stop_if_failed 1 "$i() is not a valid private method name, non interface method must start with underscore '_'" + ;; + esac + done +} + diff --git a/api/deployer/bash-aws/main.sh b/api/deployer/bash-aws/main.sh new file mode 100644 index 00000000..2de25030 --- /dev/null +++ b/api/deployer/bash-aws/main.sh @@ -0,0 +1,158 @@ +# TO AVOID AWS CLI PAGINATION +export AWS_PAGER="" + +# CHECK CONTAINER VARS +if [ $CONTAINER ] +then + [[ -z $AWS_ACCESS_KEY_ID ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_SECRET_ACCESS_KEY ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_DEFAULT_REGION ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" +fi + +# CONSTS +INSTANCE_DESCRIPTION="instance_description.json" + +# CAN BE OVERRIDE BY CONTAINER ENV VARS +[ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" +[ -z $AMI_ID ] && AMI_ID="ami-0569ce8a44f2351be" + +# DEPENDENCIES CHECKS +AWS=`which aws 2>/dev/null` +[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" + +# PRIVATE METHODS +_get_ec2_instance_id() { + $JQ -r '.Instances[0].InstanceId' $1 +} + +_get_ec2_instance_public_ip(){ + INSTANCE_IP="" + while [ -z $INSTANCE_IP ] + do + INSTANCE_IP=`$AWS ec2 describe-instances --instance-ids $1 --query 'Reservations[*].Instances[*].PublicIpAddress' --output text` + done + echo "$INSTANCE_IP" +} + +_get_ec2_instance_private_ip(){ + $JQ -r '.Instances[0].PrivateIpAddress' $1 +} + +_create_ec2_resources() { + pr_info "creating EC2 resources" + RESOURCES_NAME="openspot-ng-$RANDOM_SUFFIX" + GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "openspot-ng security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` + stop_if_failed $? "failed to create EC2 security group" + # KEYPAIR (Created just because mandatory, will be swapped manually fore core user later on) + $AWS ec2 create-key-pair --key-name $RESOURCES_NAME --no-paginate + stop_if_failed $? "failed to create EC2 keypair" + # SG SETUP + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 22 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create SSH rule for security group" + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 6443 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create API rule for security group" + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 443 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create HTTPS rule for security group" + # CREATE INSTANCE + $AWS ec2 run-instances --no-paginate --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$RESOURCES_NAME}]" \ + --image-id $AMI_ID --instance-type $INSTANCE_TYPE --security-group-ids $GROUPID --key-name $RESOURCES_NAME > $WORKDIR/$INSTANCE_DESCRIPTION + + stop_if_failed $? "failed to launch EC2 instance" +} + +_destroy_ec2_resources() { + ID=$WORKDIR/$INSTANCE_DESCRIPTION + [ ! -f $ID ] && stop_if_failed 1 "Missing EC2 resource descriptor $INSTANCE_DESCRIPTION in $WORKDIR" + INSTANCE_ID=`$JQ -r '.Instances[0].InstanceId' $ID` + stop_if_failed $? "Failed to parse InstanceId from $ID" + KEY_NAME=`$JQ -r '.Instances[0].KeyName' $ID` + stop_if_failed $? "Failed to parse KeyName from $ID" + SG_ID=`$JQ -r '.Instances[0].SecurityGroups[0].GroupId' $ID` + stop_if_failed $? "Failed to parse SecurityGroupName from $ID" + AZ=`$JQ -r '.Instances[0].Placement.AvailabilityZone' $ID` + stop_if_failed $? "Failed to parse AvailabilityZone from $ID" + + # CHECK IF INSTANCE IS FOUND + $AWS ec2 describe-instance-status --instance-id $INSTANCE_ID > /dev/null 2>&1 + stop_if_failed $? "instance $INSTANCE_ID not found, is your AWS_PROFILE exported and working?" + # KILL INSTANCE + pr_info "terminating instance $INSTANCE_ID" + $AWS ec2 terminate-instances --instance-ids $INSTANCE_ID + stop_if_failed $? "failed to terminate instance $INSTANCE_ID" + # WAIT FOR INSTANCE TO TERMINATE + while [[ `$AWS ec2 describe-instance-status --instance-id $INSTANCE_ID | $JQ -r ".InstanceStatuses[0].SystemStatus.Status"` != null ]] + do + pr_info "waiting instance $INSTANCE_ID to shutdown, hang on...." + sleep 1 + done + + pr_info "removing security group $SG_ID" + TRY=0 + until `$AWS ec2 delete-security-group --group-id $SG_ID` + do + pr_info "waiting the security group to be removable try $TRY" + ((TRY++)) + [[ $TRY == $TEARDOWN_MAX_RETRIES ]] && stop_if_failed 1 "failed to remove $SG_ID, are you sure that this sg exists?!" + sleep 6 + done + pr_info "removed security group $SG_ID" + pr_info "removing keypair $KEY_NAME" + $AWS --region ${AZ::-1} ec2 delete-key-pair --key-name $KEY_NAME + stop_if_failed $? "failed to remove keypair $KEY_NAME" + pr_end "everything has been cleaned up!" + exit 0 + +} + +_parse_args() { + local opts='' + # NOTE ON ARGUMENTS: + # DUE TO A GETOPTS STRANGE BEHAVIOUR, IN ORDER TO ADD PLUGIN SPECIFIC ARGUMENTS + # IT IS *MANDATORY* TO CONCATENATE WITH THE MAIN SCRIPT ARGUMENT STRING ($BASE_OPTIONS) + while getopts "${BASE_OPTIONS}t:a:" option; do + case "$option" in + a) AMI_ID=$OPTARG;pr_info "provided ami-id: $AMI_ID";; + t) INSTANCE_TYPE=$OPTARG;pr_info "provided instance type: $INSTANCE_TYPE";; + :) printf "missing argument for -%s\n" "$OPTARG" >&2; usage;; + \?) + esac + done +} + + + +# INTERFACE METHODS +deployer_create() { + _parse_args $@ + _create_ec2_resources +} + +deployer_teardown() { + _destroy_ec2_resources +} + +deployer_get_eip() { + INSTANCE_ID=`_get_ec2_instance_id $WORKDIR/$INSTANCE_DESCRIPTION` + EIP=`_get_ec2_instance_public_ip $INSTANCE_ID` + echo $EIP +} + +deployer_get_iip() { + IIP=`_get_ec2_instance_private_ip $WORKDIR/$INSTANCE_DESCRIPTION` + echo $IIP +} + +deployer_usage() { + usage=" +Deployer Options : + +[-a AMI ID] [-t Instance type] +where: + -a AMI ID (Amazon Machine Image) from which the VM will be Instantiated (optional, default: $AMI_ID) + -i EC2 Instance Type (optional, default; $INSTANCE_TYPE) +" + echo "$usage" +} \ No newline at end of file diff --git a/api/deployer/example/main.sh b/api/deployer/example/main.sh new file mode 100644 index 00000000..d864bd7f --- /dev/null +++ b/api/deployer/example/main.sh @@ -0,0 +1,46 @@ +# THIS METHOD IS NOT PART OF THE INTERFACE HAS BEEN PUT HERE AS AN EXAMPLE ON HOW +# TO PARSE ADDITIONAL ARGUMENTS FOR A DEPLOYER PLUGIN +# NON INTERFACE METHODS *MUST* START WITH UNDERSCORE +_parse_args() { + local opts='' + #NOTE ON ARGUMENTS: + #DUE TO A GETOPTS STRANGE BEHAVIOUR, IN ORDER TO ADD PLUGIN SPECIFIC ARGUMENTS + #IT IS *MANDATORY* TO CONCATENATE WITH THE MAIN SCRIPT ARGUMENT STRING ($BASE_OPTIONS) + #IN THIS EXAMPLE WE ADDED -t AS A "DEPLOYER SPECIFIC" OPTION + while getopts "${BASE_OPTIONS}t:" option; do + case "$option" in + #example -t argument + t)echo "I'm -t argument $OPTARG";exit 0;; + :) printf "missing argument for -%s\n" "$OPTARG" >&2; usage;; + \?) + esac + done + echo $OPTIND + +} + +# INTERFACE METHODS + +deployer_create() { + #all the command line args will be passed to that function + pr_info "creates the infrastructure" + exit 0 +} + +deployer_teardown() { + #all the command line args will be passed to that function + pr_info "destroys infrastructure" + exit 0 +} + +deployer_get_eip() { + echo "return the external (public ip) of the VM" +} + +deployer_get_iip() { + echo "return internal ip (within the cloud infrastructure) of the VM" +} + +deployer_usage() { + echo "prints the usage of the deployer including all its cli parameters" +} \ No newline at end of file diff --git a/common.sh b/common.sh deleted file mode 100644 index 414b4267..00000000 --- a/common.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -pr_info() { - if [[ $WORKING_MODE == "C" ]] - then - echo "[INF] $1" | (tee -a $LOG_FILE 2>/dev/null) - else - echo "[INF] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) - fi -} - -pr_error() { - if [[ $WORKING_MODE == "C" ]] - then - echo "[ERR] $1" | (tee -a $LOG_FILE 2>/dev/null) - else - echo "[ERR] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) - fi -} - -pr_end() { - if [[ $WORKING_MODE == "C" ]] - then - echo "[END] $1" | (tee -a $LOG_FILE 2>/dev/null) - else - echo "[END] $1" | (tee -a $TEARDOWN_LOGFILE 2>/dev/null) - fi -} - -stop_if_failed(){ - EXIT_CODE=$1 - MESSAGE=$2 - if [[ $EXIT_CODE != 0 ]] - then - pr_error "$MESSAGE" - exit $EXIT_CODE - fi -} - -check_ssh(){ - $NC -z $1 $SSH_PORT > /dev/null 2>&1 - return $? -} - -wait_instance_readiness(){ - RES=1 - while [[ $RES != 0 ]] - do - check_ssh $1 - RES=$? - sleep 1 - pr_info "waiting sshd to become ready on $1, hang on...." - done -} - - -get_instance_id() { - $JQ -r '.Instances[0].InstanceId' $1 -} - -get_instance_public_ip(){ - INSTANCE_IP="" - while [ -z $INSTANCE_IP ] - do - INSTANCE_IP=`$AWS ec2 describe-instances --instance-ids $1 --query 'Reservations[*].Instances[*].PublicIpAddress' --output text` - done - echo "$INSTANCE_IP" -} - -get_instance_private_ip(){ - $JQ -r '.Instances[0].PrivateIpAddress' $1 -} - diff --git a/crc-cloud.sh b/crc-cloud.sh index 57fd56a8..dc250468 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -1,9 +1,9 @@ #!/bin/bash -source ./common.sh trap "cleanup" INT QUIT TERM SIGHUP SIGINT SIGTERM -## FUNCTIONS + +### FUNCTIONS cleanup() { pr_error "Interrupt received" local pids=$(jobs -pr) @@ -15,7 +15,7 @@ prepare_workdir() { mkdir $WORKDIR echo $RANDOM_SUFFIX > $RANDOM_SUFFIX_FILE rm -rf $WORKDIR_PATH/latest - #link only if not running in container (teardown will need mandatory run id) + # link only if not running in container (teardown will need mandatory run id) [[ ! $CONTAINER ]] && ln -s $(readlink -f $WORKDIR) $(readlink -f $WORKDIR_PATH)/latest pr_info "preparing working directory" } @@ -38,7 +38,7 @@ prepare_cluster_setup() { $SED "s#_IIP_#$IIP#" $TEMPLATES/cluster_setup.sh > $WORKDIR/cluster_setup.sh $SED -i "s#_EIP_#$EIP#g" $WORKDIR/cluster_setup.sh $SED -i "s#_RANDOM_SUFFIX_#$RANDOM_SUFFIX#g" $WORKDIR/cluster_setup.sh - #remove linebreaks (eg. running from podman -e PULL_SECRET="$(base64)" if used with quotes line breaks are introduced) + # remove linebreaks (eg. running from podman -e PULL_SECRET="$(base64)" if used with quotes line breaks are introduced) $SED -i "s#_PULL_SECRET_#${PULL_SECRET//$'\n'/}#g" $WORKDIR/cluster_setup.sh $SED -i "s#_PASS_DEVELOPER_#$PASS_DEVELOPER#g" $WORKDIR/cluster_setup.sh $SED -i "s#_PASS_KUBEADMIN_#$PASS_KUBEADMIN#g" $WORKDIR/cluster_setup.sh @@ -48,71 +48,6 @@ prepare_cluster_setup() { fi } -create_ec2_resources() { - pr_info "creating EC2 resources" - RESOURCES_NAME="crc-cloud-$RANDOM_SUFFIX" - GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "crc-cloud security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` - stop_if_failed $? "failed to create EC2 security group" - #KEYPAIR (Created just because mandatory, will be swapped manually fore core user later on) - $AWS ec2 create-key-pair --key-name $RESOURCES_NAME --no-paginate - stop_if_failed $? "failed to create EC2 keypair" - #SG SETUP - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 22 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create SSH rule for security group" - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 6443 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create API rule for security group" - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 443 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create HTTPS rule for security group" - #CREATE INSTANCE - $AWS ec2 run-instances --no-paginate --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$RESOURCES_NAME}]" \ - --image-id $AMI_ID --instance-type $INSTANCE_TYPE --security-group-ids $GROUPID --key-name $RESOURCES_NAME > $WORKDIR/$INSTANCE_DESCRIPTION - - stop_if_failed $? "failed to launch EC2 instance" -} - -destroy_ec2_resources() { - ID=$WORKDIR/$INSTANCE_DESCRIPTION - [ ! -f $ID ] && stop_if_failed 1 "Missing EC2 resource descriptor $INSTANCE_DESCRIPTION in $WORKDIR" - INSTANCE_ID=`$JQ -r '.Instances[0].InstanceId' $ID` - stop_if_failed $? "Failed to parse InstanceId from $ID" - KEY_NAME=`$JQ -r '.Instances[0].KeyName' $ID` - stop_if_failed $? "Failed to parse KeyName from $ID" - SG_ID=`$JQ -r '.Instances[0].SecurityGroups[0].GroupId' $ID` - stop_if_failed $? "Failed to parse SecurityGroupName from $ID" - AZ=`$JQ -r '.Instances[0].Placement.AvailabilityZone' $ID` - stop_if_failed $? "Failed to parse AvailabilityZone from $ID" - - #check if instance is found - $AWS ec2 describe-instance-status --instance-id $INSTANCE_ID > /dev/null 2>&1 - stop_if_failed $? "instance $INSTANCE_ID not found, is your AWS_PROFILE exported and working?" - #KILL INSTANCE - pr_info "terminating instance $INSTANCE_ID" - $AWS ec2 terminate-instances --instance-ids $INSTANCE_ID - stop_if_failed $? "failed to terminate instance $INSTANCE_ID" - #WAIT FOR INSTANCE TO TERMINATE - while [[ `$AWS ec2 describe-instance-status --instance-id $INSTANCE_ID | $JQ -r ".InstanceStatuses[0].SystemStatus.Status"` != null ]] - do - pr_info "waiting instance $INSTANCE_ID to shutdown, hang on...." - sleep 1 - done - - pr_info "removing security group $SG_ID" - TRY=0 - until `$AWS ec2 delete-security-group --group-id $SG_ID` - do - pr_info "waiting the security group to be removable try $TRY" - ((TRY++)) - [[ $TRY == $TEARDOWN_MAX_RETRIES ]] && stop_if_failed 1 "failed to remove $SG_ID, are you sure that this sg exists?!" - sleep 6 - done - pr_info "removed security group $SG_ID" - pr_info "removing keypair $KEY_NAME" - $AWS --region ${AZ::-1} ec2 delete-key-pair --key-name $KEY_NAME - stop_if_failed $? "failed to remove keypair $KEY_NAME" - pr_end "everything has been cleaned up!" - exit 0 - -} swap_ssh_key() { pr_info "changing default private key permissions to 400" @@ -125,7 +60,7 @@ swap_ssh_key() { stop_if_failed $? "failed to upload the public key on the machine @ $EIP" $SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "cat /home/core/id_rsa.pub > /home/core/.ssh/authorized_keys" stop_if_failed $? "failed to swap the key on the authorized_keys @ $EIP" - #after swapping on VM private key is replaced by the new one + # after swapping on VM private key is replaced by the new one PRIVATE_KEY=$WORKDIR/id_rsa } @@ -146,7 +81,7 @@ tail_cluster_setup() { do LINE=$($SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "sudo tail -n 1 /tmp/$RANDOM_SUFFIX.log") stop_if_failed $? "impossible to get the logs from $EIP" - #xargs used to remove leading space + # xargs used to remove leading space if [[ $LINE =~ "[ERR]" ]] then CLEANLINE=${LINE//"[ERR]"} @@ -181,14 +116,13 @@ set_cluster_infos() { cat <<< $($JQ '.console.port="443"' $CLUSTER_INFOS) > $CLUSTER_INFOS } + create () { SECONDS=0 prepare_workdir - create_ec2_resources - - INSTANCE_ID=`get_instance_id $WORKDIR/$INSTANCE_DESCRIPTION` - IIP=`get_instance_private_ip $WORKDIR/$INSTANCE_DESCRIPTION` - EIP=`get_instance_public_ip $INSTANCE_ID` + deployer_create $@ + IIP=`deployer_get_iip` + EIP=`deployer_get_eip` wait_instance_readiness $EIP swap_ssh_key @@ -205,7 +139,7 @@ create () { teardown() { WORKDIR="$WORKDIR_PATH/$TEARDOWN_RUN_ID" [ ! -d $WORKDIR ] && stop_if_failed 1 "$WORKDIR not found, please provide a correct path" - destroy_ec2_resources + deployer_teardown } set_workdir_dependent_variables() { @@ -216,15 +150,86 @@ set_workdir_dependent_variables() { CLUSTER_INFOS="$WORKDIR/$CLUSTER_INFOS_FILE" } +parse_args () { + while getopts $BASE_OPTIONS option; do + case "$option" in + h) usage;; + C) WORKING_MODE="C";pr_info "working mode: CREATE";; + D) DEPLOYER_API=$OPTARG;pr_info "deployer api: $OPTARG";; + T) WORKING_MODE="T";pr_info "working mode: TEARDOWN";; + p) PULL_SECRET_PATH=$OPTARG;pr_info "pull secret path: $OPTARG";; + d) PASS_DEVELOPER=$OPTARG;pr_info "developer user pass has been overridden";; + k) PASS_KUBEADMIN=$OPTARG;pr_info "kubeadmin user pass has been overridden";; + r) PASS_REDHAT=$OPTARG;pr_info "redhat user pass has been overridden";; + v) TEARDOWN_RUN_ID=$OPTARG;pr_info "tearing down run id: $OPTARG";; + :) printf "missing argument for -%s\n" "$OPTARG" >&2; usage;; + \?);; + esac + done + OPTIND=1 + OPTARG="" + option="" +} + +check_command_dependencies() { + ## COMMANDS CHECK + OS CHECK + [[ `uname` != "Linux" ]] && stop_if_failed 1 "sorry, but $(basename "$0") can be run only on Linux (for the moment), please run the containerized version" + CURL=`which curl 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: curl, please install it and try again" + JQ=`which jq 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: jq, please install it and try again" + MD5SUM=`which md5sum 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: md5sum, please install it and try again" + AWS=`which aws 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" + HEAD=`which head 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: head, please install it and try again" + SSHKEYGEN=`which ssh-keygen 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: ssh-keygen, please install it and try again" + SED=`which sed 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: GNU sed, please install it and try again" + NC=`which nc 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: nc (netcat), please install it and try again" + SSH=`which ssh 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: ssh, please install it and try again" + SCP=`which scp 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: scp, please install it and try again" + BASE64=`which base64 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: base64, please install it and try again" + CAT=`which cat 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: cat, please install it and try again" + GREP=`which grep 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: grep, please install it and try again" + FIGLET=`which figlet 2>/dev/null` + [[ $CONTAINER && ( $? != 0 ) ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: figlet (container mode only), please install it and try again" +} + +set_const() { + ## CONST + SSH_PORT="22" + RUN_TIMESTAMP=`date +%s` + INSTANCE_DESCRIPTION="instance_description.json" + RANDOM_SUFFIX=`echo $RANDOM | $MD5SUM | $HEAD -c 8` + PRIVATE_KEY="id_ecdsa_crc" + CLUSTER_INFOS_FILE="cluster_infos.json" + TEMPLATES="templates" + TEARDOWN_MAX_RETRIES=500 + CLUSTER_INFOS_TEMPLATE="$TEMPLATES/$CLUSTER_INFOS_FILE" + DEFAULT_DEPLOYER="bash-aws" + BASE_OPTIONS=":hCTp:D:d:k:r:v:" + ROOT_API_FOLDER="api" + DEPLOYER_API_FOLDER="$ROOT_API_FOLDER/deployer" + [[ $CONTAINER ]] && WORKDIR_PATH="/workdir" +} + usage() { - echo "" - echo "*********** crc-cloud ***********" - + echo -e "\n*********** CRC-Cloud ***********" usage=" Cluster Creation : -$(basename "$0") -C -p pull secret path [-d developer user password] [-k kubeadmin user password] [-r redhat user password] [-a AMI ID] [-t Instance type] +$(basename "$0") -C -p pull secret path [-D infrastructure_deployer] [-d developer user password] [-k kubeadmin user password] [-r redhat user password] where: + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) -C Cluster Creation mode -p pull secret file path (download from https://console.redhat.com/openshift/create/local) -d developer user password (optional, default: $PASS_DEVELOPER) @@ -236,129 +241,80 @@ where: Cluster Teardown: -$(basename "$0") -T [-v run id] +$(basename "$0") -T [-D infrastructure_deployer] [-v run id] + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) -T Cluster Teardown mode -v The Id of the run that is gonna be destroyed, corresponds with the numeric name of the folders created in workdir (optional, default: latest) -h show this help text " echo "$usage" + + echo -e "\n*********** Deployer: $DEPLOYER_API ***********" + deployer_usage exit 1 } +### END FUNCTIONS +### INIT +[[ $CONTAINER ]] && figlet -f slant -c "CRC-Cloud `cat VERSION`" && echo -e "\n\n" +check_command_dependencies +set_const -##COMMANDS CHECK + OS CHECK -[[ `uname` != "Linux" ]] && stop_if_failed 1 "sorry, but $(basename "$0") can be run only on Linux (for the moment), please run the containerized version" -CURL=`which curl 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: curl, please install it and try again" -JQ=`which jq 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: jq, please install it and try again" -MD5SUM=`which md5sum 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: md5sum, please install it and try again" -AWS=`which aws 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" -HEAD=`which head 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: head, please install it and try again" -SSHKEYGEN=`which ssh-keygen 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: ssh-keygen, please install it and try again" -SED=`which sed 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: GNU sed, please install it and try again" -NC=`which nc 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: nc (netcat), please install it and try again" -SSH=`which ssh 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: ssh, please install it and try again" -SCP=`which scp 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: scp, please install it and try again" -BASE64=`which base64 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: base64, please install it and try again" -FIGLET=`which figlet 2>/dev/null` -[[ $CONTAINER && ( $? != 0 ) ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: figlet (container mode only), please install it and try again" - - -##DEFAULT VALUES THAT CAN BE OVERRIDDEN BY ENV (podman/docker) +## IMPORT COMMON +source ./api/common.sh + +## DEFAULT VALUES THAT CAN BE OVERRIDDEN BY ENV (podman/docker) [ -z $PASS_DEVELOPER ] && PASS_DEVELOPER="developer" [ -z $KUBEADMIN ] && PASS_KUBEADMIN="kubeadmin" [ -z $PASS_REDHAT ] && PASS_REDHAT="redhat" -[ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" [ -z $WORKDIR_PATH ] && WORKDIR_PATH="workdir" [ -z $WORKING_MODE ] && WORKING_MODE="" [ -z $TEARDOWN_RUN_ID ] && TEARDOWN_RUN_ID="latest" +[ -z $DEPLOYER_API ] && DEPLOYER_API=$DEFAULT_DEPLOYER +## LOAD DEPLOYER API + +api_load_deployer $DEPLOYER_API +set_workdir_dependent_variables -##CONST -SSH_PORT="22" -RUN_TIMESTAMP=`date +%s` -INSTANCE_DESCRIPTION="instance_description.json" -RANDOM_SUFFIX=`echo $RANDOM | $MD5SUM | $HEAD -c 8` -PRIVATE_KEY="id_ecdsa_crc" -CLUSTER_INFOS_FILE="cluster_infos.json" -TEMPLATES="templates" -TEARDOWN_MAX_RETRIES=500 -CLUSTER_INFOS_TEMPLATE="$TEMPLATES/$CLUSTER_INFOS_FILE" -AMI_ID="ami-0569ce8a44f2351be" - -##ARGS -#collects args from commandline only if not in container otherwise variables are fed by -e VAR=VALUE +## PARSE & CHECK ARGS if [ $CONTAINER ] then - WORKDIR_PATH="/workdir" - set_workdir_dependent_variables - #check working mode + # check working mode [[ (-z $WORKING_MODE ) ]] && stop_if_failed 1 "WORKING_MODE environment variable must be set" [[ ( $WORKING_MODE != "C" ) && ( $WORKING_MODE != "T" ) ]] && \ stop_if_failed 1 "WORKING_MODE value must be either C (create) or T(teardown) $WORKING_MODE is not a valid value" - #check pull secret + # check pull secret [[ ($WORKING_MODE == "C") && ( -z $PULL_SECRET ) ]] && stop_if_failed 1 "PULL_SECRET environment variable must be set and must contain a valid base64 encoded pull_secret, please refer to the README.md" - #check workdir mount write permissions + # check workdir mount write permissions [[ ! -d $WORKDIR_PATH ]] && stop_if_failed 1 "please mount the workdir filesystem, refer to README.md for further instructions" [[ ! -w $WORKDIR_PATH ]] && \ stop_if_failed 1 "please grant write permissions to the host folder mounted as volume, please refer to README.md for further instructions" - #check AWS credentials + # check AWS credentials [[ -z $AWS_ACCESS_KEY_ID ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" [[ -z $AWS_SECRET_ACCESS_KEY ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" [[ -z $AWS_DEFAULT_REGION ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" [[ ( $WORKING_MODE == "T" ) && ($TEARDOWN_RUN_ID == "latest") ]] && stop_if_failed 1 "TEARDOWN_RUN_ID must be set in container mode. Please set this value with the run id that you want to teardown, refer to README.md for further instructions" else - set_workdir_dependent_variables - options=':h:CTp:d:k:r:a:t:v:' - while getopts $options option; do - case "$option" in - h) usage;; - C) WORKING_MODE="C";; - T) WORKING_MODE="T";; - p) PULL_SECRET_PATH=$OPTARG;; - d) PASS_DEVELOPER=$OPTARG;; - k) PASS_KUBEADMIN=$OPTARG;; - r) PASS_REDHAT=$OPTARG;; - a) AMI_ID=$OPTARG;; - t) INSTANCE_TYPE=$OPTARG;; - v) TEARDOWN_RUN_ID=$OPTARG;; - :) printf "missing argument for -%s\n" "$OPTARG" >&2; usage;; - \?) printf "illegal option: -%s\n" "$OPTARG" >&2; usage;; - esac - done - - ##VARIABLE SANITY CHECKS - - #WORKING MODE CHECK + parse_args $@ + ## VARIABLES SANITY CHECKS + # WORKING MODE CHECK [[ (-z $WORKING_MODE ) ]] && echo -e "\nERROR: Working mode must be set\n" && usage [[ ( $WORKING_MODE != "C" ) && ( $WORKING_MODE != "T" ) ]] && echo \ -e "\nERROR: Working mode Must be either -C (creation) or -T (teardown), not $WORKING_MODE\n" && usage - #CHECK MANDATORY ARGS FOR CREATION + # CHECK MANDATORY ARGS FOR CREATION [[ ($WORKING_MODE == "C" ) && ( ! "$PULL_SECRET_PATH" ) ]] && \ echo -e "\nERROR: in creation mode argument -p must be provided\n" && usage - #CHECK PULL SECRET PATH + # CHECK PULL SECRET PATH [[ ($WORKING_MODE == "C" ) && ( ! -f $PULL_SECRET_PATH ) ]] && \ echo -e "\nERROR: $PULL_SECRET_PATH pull secret file not found" && usage -fi - - +fi -##ENTRYPOINT: if everything is ok, run the script. -[[ $CONTAINER ]] && figlet -f slant -c "CRC-Cloud `cat VERSION`" && echo -e "\n\n" +## ENTRYPOINT: if everything is ok, run the script. if [[ $WORKING_MODE == "C" ]] then - create + create $@ elif [[ $WORKING_MODE == "T" ]] then teardown From f77b646d6c66abd69a4f06c8e59d71602d85f41c Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Wed, 11 Jan 2023 10:23:24 +0100 Subject: [PATCH 2/6] folder rename and reorg --- api/README.md | 4 ++-- api/common.sh | 21 ++++++++++--------- crc-cloud.sh | 20 ++++++------------ .../bash-aws => plugin/deployer/bash}/main.sh | 0 {api => plugin}/deployer/example/main.sh | 0 5 files changed, 19 insertions(+), 26 deletions(-) rename {api/deployer/bash-aws => plugin/deployer/bash}/main.sh (100%) rename {api => plugin}/deployer/example/main.sh (100%) diff --git a/api/README.md b/api/README.md index 6b53e331..db4a4f4c 100644 --- a/api/README.md +++ b/api/README.md @@ -3,8 +3,8 @@ The Infrastructure Deployer API has been designed to abstract the Infrastructure provisioning from the OpenShift instance provisioning. The first version of CRC-Cloud was relying on AWS and AWS CLI, but as soon as the project was gaining interest, we started considering to support other cloud providers so we decided to implement this abstraction to easily implement other deployment technologies such as IaC tools like [Ansible](https://www.redhat.com/it/engage/delivery-with-ansible-20170906?sc_cid=7013a000002w14JAAQ&gclid=EAIaIQobChMIwLPlpZG9_AIVA5zVCh2EPw9VEAAYASAAEgJJXfD_BwE&gclsrc=aw.ds), [Terraform](https://terraform.io), [Pulumi](https://https://www.pulumi.com/) etc. ## Plugin loading and API implementation -In order to be loaded, an Infrastructure Deployer Plugin must have a folder in ```api/deployer```. This folder must have the name of the plugin that will be passed to ```crc-cloud.sh``` with the ```-D``` option, so for example, if you want to create a plugin named ```my-deployer``` the plugin code and resources will be stored in ```/api/deployer/my-deployer```. -You can find an example implementation from which start to develop a new plugin in ```api/deployer/example``` (and you can even run it!!). +In order to be loaded, an Infrastructure Deployer Plugin must have a folder in ```plugin/deployer```. This folder must have the name of the plugin that will be passed to ```crc-cloud.sh``` with the ```-D``` option, so for example, if you want to create a plugin named ```my-deployer``` the plugin code and resources will be stored in ```/plugin/deployer/my-deployer```. +You can find an example implementation from which start to develop a new plugin in ```plugin/deployer/example``` (and you can even run it!!). The plugin folder must contain a ```main.sh``` script that is the entrypoint of the plugin. The ```main.sh``` must implement the following methods: ``` diff --git a/api/common.sh b/api/common.sh index 19c5a978..39eebe5f 100644 --- a/api/common.sh +++ b/api/common.sh @@ -55,27 +55,27 @@ wait_instance_readiness(){ api_load_deployer() { - [ ! -d "$DEPLOYER_API_FOLDER/$1" ] && stop_if_failed 1 "Deployer API $1 folder not found in $DEPLOYER_API_FOLDER/$1, please refer api/README.md for API specifications" - [ ! -f "$DEPLOYER_API_FOLDER/$1/main.sh" ] && stop_if_failed 1 "main.sh not found for deployer $1 in folder $DEPLOYER_API_FOLDER/$1, please refer api/README.md for API specifications" - source $DEPLOYER_API_FOLDER/$1/main.sh + [ ! -d "$PLUGIN_DEPLOYER_FOLDER/$1" ] && stop_if_failed 1 "Deployer API $1 folder not found in $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" + [ ! -f "$PLUGIN_DEPLOYER_FOLDER/$1/main.sh" ] && stop_if_failed 1 "main.sh not found for deployer $1 in folder $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" + source $PLUGIN_DEPLOYER_FOLDER/$1/main.sh [[ ! `declare -F deployer_create` ]] &&\ - stop_if_failed 1 "deployer_create method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_create method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" [[ ! `declare -F deployer_teardown` ]] &&\ - stop_if_failed 1 "deployer_teardown method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_teardown method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" [[ ! `declare -F deployer_get_eip` ]] &&\ - stop_if_failed 1 "deployer_get_eip method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_get_eip method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" [[ ! `declare -F deployer_get_iip` ]] &&\ - stop_if_failed 1 "deployer_get_iip method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_get_iip method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" [[ ! `declare -F deployer_usage` ]] &&\ - stop_if_failed 1 "deployer_usage method not found in main.sh implementation for $1 deployer api, please refer api/README.md for API specifications" - pr_info "successfully loaded $1 deployer API" + stop_if_failed 1 "deployer_usage method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE - for i in `$CAT $DEPLOYER_API_FOLDER/$1/main.sh | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` + for i in `$CAT $PLUGIN_DEPLOYER_FOLDER/$1/main.sh | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` do case "$i" in deployer_create);; @@ -88,5 +88,6 @@ api_load_deployer() { ;; esac done + pr_info "successfully loaded $1 deployer plugin" } diff --git a/crc-cloud.sh b/crc-cloud.sh index dc250468..0287a5ff 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -180,8 +180,6 @@ check_command_dependencies() { [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: jq, please install it and try again" MD5SUM=`which md5sum 2>/dev/null` [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: md5sum, please install it and try again" - AWS=`which aws 2>/dev/null` - [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" HEAD=`which head 2>/dev/null` [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: head, please install it and try again" SSHKEYGEN=`which ssh-keygen 2>/dev/null` @@ -215,10 +213,10 @@ set_const() { TEMPLATES="templates" TEARDOWN_MAX_RETRIES=500 CLUSTER_INFOS_TEMPLATE="$TEMPLATES/$CLUSTER_INFOS_FILE" - DEFAULT_DEPLOYER="bash-aws" + DEFAULT_DEPLOYER="bash" BASE_OPTIONS=":hCTp:D:d:k:r:v:" - ROOT_API_FOLDER="api" - DEPLOYER_API_FOLDER="$ROOT_API_FOLDER/deployer" + PLUGIN_FOLDER="plugin" + PLUGIN_DEPLOYER_FOLDER="$PLUGIN_FOLDER/deployer" [[ $CONTAINER ]] && WORKDIR_PATH="/workdir" } @@ -229,20 +227,18 @@ Cluster Creation : $(basename "$0") -C -p pull secret path [-D infrastructure_deployer] [-d developer user password] [-k kubeadmin user password] [-r redhat user password] where: - -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in plugin/deployer (please refer to the deployer documentation in api/README.md) -C Cluster Creation mode -p pull secret file path (download from https://console.redhat.com/openshift/create/local) -d developer user password (optional, default: $PASS_DEVELOPER) -k kubeadmin user password (optional, default: $PASS_KUBEADMIN) -r redhat user password (optional, default: $PASS_REDHAT) - -a AMI ID (Amazon Machine Image) from which the VM will be Instantiated (optional, default: $AMI_ID) - -i EC2 Instance Type (optional, default; $INSTANCE_TYPE) -h show this help text Cluster Teardown: $(basename "$0") -T [-D infrastructure_deployer] [-v run id] - -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in plugin/deployer (please refer to the deployer documentation in api/README.md) -T Cluster Teardown mode -v The Id of the run that is gonna be destroyed, corresponds with the numeric name of the folders created in workdir (optional, default: latest) -h show this help text @@ -260,7 +256,7 @@ $(basename "$0") -T [-D infrastructure_deployer] [-v run id] check_command_dependencies set_const -## IMPORT COMMON +## IMPORT API COMMON source ./api/common.sh ## DEFAULT VALUES THAT CAN BE OVERRIDDEN BY ENV (podman/docker) @@ -289,10 +285,6 @@ then [[ ! -d $WORKDIR_PATH ]] && stop_if_failed 1 "please mount the workdir filesystem, refer to README.md for further instructions" [[ ! -w $WORKDIR_PATH ]] && \ stop_if_failed 1 "please grant write permissions to the host folder mounted as volume, please refer to README.md for further instructions" - # check AWS credentials - [[ -z $AWS_ACCESS_KEY_ID ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_SECRET_ACCESS_KEY ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_DEFAULT_REGION ]] && stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" [[ ( $WORKING_MODE == "T" ) && ($TEARDOWN_RUN_ID == "latest") ]] && stop_if_failed 1 "TEARDOWN_RUN_ID must be set in container mode. Please set this value with the run id that you want to teardown, refer to README.md for further instructions" else parse_args $@ diff --git a/api/deployer/bash-aws/main.sh b/plugin/deployer/bash/main.sh similarity index 100% rename from api/deployer/bash-aws/main.sh rename to plugin/deployer/bash/main.sh diff --git a/api/deployer/example/main.sh b/plugin/deployer/example/main.sh similarity index 100% rename from api/deployer/example/main.sh rename to plugin/deployer/example/main.sh From 4083511a52b5ecdd19861420bc6ce0a0d4f02787 Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Wed, 11 Jan 2023 10:34:00 +0100 Subject: [PATCH 3/6] added plugin root folder variable and updated documentation + fixed a bug in main script related to plugin loading argument --- api/README.md | 3 ++- api/common.sh | 2 ++ crc-cloud.sh | 4 ++-- plugin/deployer/example/main.sh | 4 ++++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/api/README.md b/api/README.md index db4a4f4c..00e5c614 100644 --- a/api/README.md +++ b/api/README.md @@ -44,7 +44,8 @@ The **CRC-Cloud** engine will expose to the plugin some variables that must be u | --- | --- | --- | | $CONTAINER | Boolean | It's valorized if the script is running inside a container | | $RANDOM_SUFFIX | String | It's the random suffix applied to the resources created inside the cloud provider in order to avoid conflicts with other **CRC-Cloud** instances running in the same namespace (can be ignored if the deployment methods provides it's own logic) | -| $WORKDIR | String | That's the folder where all the deployment status infos must be stored (will be created by the engine) | +| $WORKDIR | String | That's the folder where all the deployment status infos must be stored (will be created by the engine) | +| $PLUGIN_ROOT_FOLDER | String | That's the folder containing the loaded plugin, this can be used as starting path for plugin resources | ## *Private* methods names conventions diff --git a/api/common.sh b/api/common.sh index 39eebe5f..b2772053 100644 --- a/api/common.sh +++ b/api/common.sh @@ -88,6 +88,8 @@ api_load_deployer() { ;; esac done + + PLUGIN_ROOT_FOLDER=$PLUGIN_DEPLOYER_FOLDER/$1 pr_info "successfully loaded $1 deployer plugin" } diff --git a/crc-cloud.sh b/crc-cloud.sh index 0287a5ff..b792480a 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -267,9 +267,7 @@ source ./api/common.sh [ -z $WORKING_MODE ] && WORKING_MODE="" [ -z $TEARDOWN_RUN_ID ] && TEARDOWN_RUN_ID="latest" [ -z $DEPLOYER_API ] && DEPLOYER_API=$DEFAULT_DEPLOYER -## LOAD DEPLOYER API -api_load_deployer $DEPLOYER_API set_workdir_dependent_variables ## PARSE & CHECK ARGS @@ -302,6 +300,8 @@ else fi +## LOAD DEPLOYER API +api_load_deployer $DEPLOYER_API ## ENTRYPOINT: if everything is ok, run the script. if [[ $WORKING_MODE == "C" ]] diff --git a/plugin/deployer/example/main.sh b/plugin/deployer/example/main.sh index d864bd7f..f34a76ed 100644 --- a/plugin/deployer/example/main.sh +++ b/plugin/deployer/example/main.sh @@ -24,6 +24,10 @@ _parse_args() { deployer_create() { #all the command line args will be passed to that function pr_info "creates the infrastructure" + pr_info "\$CONTAINER: $CONTAINER" + pr_info "\$RANDOM_SUFFIX: $RANDOM_SUFFIX" + pr_info "\$WORKDIR: $WORKDIR" + pr_info "\$PLUGIN_ROOT_FOLDER: $PLUGIN_ROOT_FOLDER" exit 0 } From 21a9843b99ceb4d0ae09c2fee6b88a6cf36558c3 Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Wed, 11 Jan 2023 12:15:41 +0100 Subject: [PATCH 4/6] enhanced method name logic and updated documentation accordingly --- README.md | 9 +- api/README.md | 12 +- api/common.sh | 44 ++++--- crc-cloud.sh | 3 + plugin/deployer/bash/aws.sh | 111 ++++++++++++++++++ plugin/deployer/bash/main.sh | 126 ++------------------- plugin/deployer/example/example_include.sh | 5 + plugin/deployer/example/main.sh | 15 ++- 8 files changed, 186 insertions(+), 139 deletions(-) create mode 100644 plugin/deployer/bash/aws.sh create mode 100644 plugin/deployer/example/example_include.sh diff --git a/README.md b/README.md index 215b3abd..a9574a47 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ We had a meeting and they gave me all the detailed instructions on how to run th ## Infrastructure Deployers -In order to abstract the Infrastructure and the OpenShift Instance provisioning has been developed an **Infrastructure Deployer API**. If you're interested on how to implement a new Infrastructure Deployer please refer to the [documentation](api/deployer/README.md). +In order to abstract the Infrastructure and the OpenShift Instance provisioning has been developed an **Infrastructure Deployer API**. If you're interested on how to implement a new Infrastructure Deployer please refer to the [documentation](api/README.md). ### Available Deployers | Name | Status| @@ -147,7 +147,7 @@ Environment variables will be passed to the container from the command line invo | PASS_KUBEADMIN | overrides the default password (kubeadmin) for kubeadmin account | | PASS_REDHAT | overrides the default password (redhat) for redhat account | | INSTANCE_TYPE | overrides the default AWS instance type (c6in.2xlarge, infos [here](#prereq)) | -| DEPLOYER_API | selects the infrastructure deployer ( please refer to [deployer API documentation](api/deployer/README.md) +| DEPLOYER_API | selects the infrastructure deployer ( please refer to [deployer API documentation](api/README.md) @@ -164,6 +164,7 @@ To run **CRC-Cloud** from your command line you must be on Linux, be sure to hav - ssh-keygen - GNU sed - GNU grep +- expr - cat - nc (netcat) - ssh client @@ -186,7 +187,7 @@ Below you'll find all the options available ``` ./crc-cloud.sh -C -p pull secret path [-D infrastructure_deployer] [-d developer user password] [-k kubeadmin user password] [-r redhat user password] [-a AMI ID] [-t Instance type] where: - -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in plugin/deployer (please refer to the deployer documentation in api/README.md) -C Cluster Creation mode -p pull secret file path (download from https://console.redhat.com/openshift/create/local) -d developer user password (optional, default: $PASS_DEVELOPER) @@ -203,7 +204,7 @@ this will refer to the *latest* run found in ```/workspace```, if ``` ./crc-cloud.sh -T [-D infrastructure_deployer] [-v run id] - -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/deployer (please refer to the deployer documentation in api/deployer/README.md) + -D Infrastructure Deployer (default: $DEFAULT_DEPLOYER) *NOTE* Must match with the folder name placed in api/ (please refer to the deployer documentation in api/README.md) -T Cluster Teardown mode -v The Id of the run that is gonna be destroyed, corresponds with the numeric name of the folders created in workdir (optional, default: latest) -h show this help text diff --git a/api/README.md b/api/README.md index 00e5c614..94b387b4 100644 --- a/api/README.md +++ b/api/README.md @@ -3,11 +3,17 @@ The Infrastructure Deployer API has been designed to abstract the Infrastructure provisioning from the OpenShift instance provisioning. The first version of CRC-Cloud was relying on AWS and AWS CLI, but as soon as the project was gaining interest, we started considering to support other cloud providers so we decided to implement this abstraction to easily implement other deployment technologies such as IaC tools like [Ansible](https://www.redhat.com/it/engage/delivery-with-ansible-20170906?sc_cid=7013a000002w14JAAQ&gclid=EAIaIQobChMIwLPlpZG9_AIVA5zVCh2EPw9VEAAYASAAEgJJXfD_BwE&gclsrc=aw.ds), [Terraform](https://terraform.io), [Pulumi](https://https://www.pulumi.com/) etc. ## Plugin loading and API implementation -In order to be loaded, an Infrastructure Deployer Plugin must have a folder in ```plugin/deployer```. This folder must have the name of the plugin that will be passed to ```crc-cloud.sh``` with the ```-D``` option, so for example, if you want to create a plugin named ```my-deployer``` the plugin code and resources will be stored in ```/plugin/deployer/my-deployer```. +In order to be loaded, an Infrastructure Deployer Plugin must have a folder in ```plugin/deployer```. This folder must have the name of the plugin and the plugin name must contain **only** lowercase letters and underscores, that will be passed to ```crc-cloud.sh``` with the ```-D``` option, so for example, if you want to create a plugin named ```my_deployer``` the plugin code and resources will be stored in ```/plugin/deployer/my-deployer```. You can find an example implementation from which start to develop a new plugin in ```plugin/deployer/example``` (and you can even run it!!). -The plugin folder must contain a ```main.sh``` script that is the entrypoint of the plugin. The ```main.sh``` must implement the following methods: +The plugin folder must contain a ```main.sh``` script that is the entrypoint of the plugin. The ```main.sh``` **must** implement the following methods: ``` + +deployer_load_dependencies() { + pr_info "loads (if needed otherwise keep it empty) the plugin dependencies" + source $PLUGIN_ROOT_FOLDER/example_include.sh +} + deployer_create() { #all the command line args will be passed to that function pr_info "creates the infrastructure" @@ -49,4 +55,4 @@ The **CRC-Cloud** engine will expose to the plugin some variables that must be u ## *Private* methods names conventions - In order to increase code readability, non interface method names (kinda private) must start with an underscore "_" \ No newline at end of file + In order to increase code readability and avoid conflicts non interface method names (kinda private) must start with __ for example if you name your plugin *example_plugin* all the non interface methods defined inside the plugin must start with _example_plugin_ \ No newline at end of file diff --git a/api/common.sh b/api/common.sh index b2772053..5d841df1 100644 --- a/api/common.sh +++ b/api/common.sh @@ -52,10 +52,35 @@ wait_instance_readiness(){ pr_info "waiting sshd to become ready on $1, hang on...." done } +# PARAMS $1: file path $2 plugin name +api_check_method_name() { + # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE + PLUGIN_NAME_LENGTH=`expr length $2` + for i in `$CAT $1 | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` + do + case "$i" in + deployer_create);; + deployer_teardown);; + deployer_get_eip);; + deployer_get_iip);; + deployer_usage);; + deployer_load_dependencies);; + *) + [[ ${i:0:$PLUGIN_NAME_LENGTH+2} != "_$2_" ]] && stop_if_failed 1 "$i() in $1 is not a valid private method name, non interface method must start with __ in that case '_$2_' " + ;; + esac + done +} api_load_deployer() { - [ ! -d "$PLUGIN_DEPLOYER_FOLDER/$1" ] && stop_if_failed 1 "Deployer API $1 folder not found in $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" + + case $1 in + (*[![:lower:]_]*) stop_if_failed 1 "plugin name must contain only lowercase letters and underscores";; + (*);; + esac + + [ ! -d "$PLUGIN_DEPLOYER_FOLDER/$1" ] && stop_if_failed 1 "deployer API $1 folder not found in $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" [ ! -f "$PLUGIN_DEPLOYER_FOLDER/$1/main.sh" ] && stop_if_failed 1 "main.sh not found for deployer $1 in folder $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" source $PLUGIN_DEPLOYER_FOLDER/$1/main.sh [[ ! `declare -F deployer_create` ]] &&\ @@ -75,20 +100,13 @@ api_load_deployer() { # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE - for i in `$CAT $PLUGIN_DEPLOYER_FOLDER/$1/main.sh | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` + api_check_method_name $PLUGIN_DEPLOYER_FOLDER/$1/main.sh $1 + # CHECKS main.sh INCLUDES FOR METHOD NAME COMPLIANCE + for i in `$CAT $PLUGIN_DEPLOYER_FOLDER/$1/main.sh | $GREP -P "^\s+source .+\.sh" | sed -r 's#source\s+.+/(.*)#\1#'` do - case "$i" in - deployer_create);; - deployer_teardown);; - deployer_get_eip);; - deployer_get_iip);; - deployer_usage);; - *) - [[ ${i::1} != '_' ]] && stop_if_failed 1 "$i() is not a valid private method name, non interface method must start with underscore '_'" - ;; - esac + api_check_method_name $PLUGIN_DEPLOYER_FOLDER/$1/$i $1 done - + PLUGIN_ROOT_FOLDER=$PLUGIN_DEPLOYER_FOLDER/$1 pr_info "successfully loaded $1 deployer plugin" } diff --git a/crc-cloud.sh b/crc-cloud.sh index b792480a..917c704b 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -120,6 +120,7 @@ set_cluster_infos() { create () { SECONDS=0 prepare_workdir + deployer_load_dependencies deployer_create $@ IIP=`deployer_get_iip` EIP=`deployer_get_eip` @@ -198,6 +199,8 @@ check_command_dependencies() { [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: cat, please install it and try again" GREP=`which grep 2>/dev/null` [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: grep, please install it and try again" + EXPR=`which expr 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: expr, please install it and try again" FIGLET=`which figlet 2>/dev/null` [[ $CONTAINER && ( $? != 0 ) ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: figlet (container mode only), please install it and try again" } diff --git a/plugin/deployer/bash/aws.sh b/plugin/deployer/bash/aws.sh new file mode 100644 index 00000000..cb68f05f --- /dev/null +++ b/plugin/deployer/bash/aws.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# TO AVOID AWS CLI PAGINATION +export AWS_PAGER="" + +# CHECK CONTAINER VARS +if [ $CONTAINER ] +then + [[ -z $AWS_ACCESS_KEY_ID ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_SECRET_ACCESS_KEY ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_DEFAULT_REGION ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" +fi + +# CONSTS +INSTANCE_DESCRIPTION="instance_description.json" + +# CAN BE OVERRIDE BY CONTAINER ENV VARS +[ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" +[ -z $AMI_ID ] && AMI_ID="ami-0569ce8a44f2351be" + +# DEPENDENCIES CHECKS +AWS=`which aws 2>/dev/null` +[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" + + +# PRIVATE AWS METHODS +_bash_get_ec2_instance_id() { + $JQ -r '.Instances[0].InstanceId' $1 +} + +_bash_get_ec2_instance_public_ip(){ + INSTANCE_IP="" + while [ -z $INSTANCE_IP ] + do + INSTANCE_IP=`$AWS ec2 describe-instances --instance-ids $1 --query 'Reservations[*].Instances[*].PublicIpAddress' --output text` + done + echo "$INSTANCE_IP" +} + +_bash_get_ec2_instance_private_ip(){ + $JQ -r '.Instances[0].PrivateIpAddress' $1 +} + +_bash_create_ec2_resources() { + pr_info "creating EC2 resources" + RESOURCES_NAME="openspot-ng-$RANDOM_SUFFIX" + GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "openspot-ng security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` + stop_if_failed $? "failed to create EC2 security group" + # KEYPAIR (Created just because mandatory, will be swapped manually fore core user later on) + $AWS ec2 create-key-pair --key-name $RESOURCES_NAME --no-paginate + stop_if_failed $? "failed to create EC2 keypair" + # SG SETUP + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 22 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create SSH rule for security group" + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 6443 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create API rule for security group" + $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 443 --cidr 0.0.0.0/0 --no-paginate + stop_if_failed $? "failed to create HTTPS rule for security group" + # CREATE INSTANCE + $AWS ec2 run-instances --no-paginate --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$RESOURCES_NAME}]" \ + --image-id $AMI_ID --instance-type $INSTANCE_TYPE --security-group-ids $GROUPID --key-name $RESOURCES_NAME > $WORKDIR/$INSTANCE_DESCRIPTION + + stop_if_failed $? "failed to launch EC2 instance" +} + +_bash_destroy_ec2_resources() { + ID=$WORKDIR/$INSTANCE_DESCRIPTION + [ ! -f $ID ] && stop_if_failed 1 "Missing EC2 resource descriptor $INSTANCE_DESCRIPTION in $WORKDIR" + INSTANCE_ID=`$JQ -r '.Instances[0].InstanceId' $ID` + stop_if_failed $? "Failed to parse InstanceId from $ID" + KEY_NAME=`$JQ -r '.Instances[0].KeyName' $ID` + stop_if_failed $? "Failed to parse KeyName from $ID" + SG_ID=`$JQ -r '.Instances[0].SecurityGroups[0].GroupId' $ID` + stop_if_failed $? "Failed to parse SecurityGroupName from $ID" + AZ=`$JQ -r '.Instances[0].Placement.AvailabilityZone' $ID` + stop_if_failed $? "Failed to parse AvailabilityZone from $ID" + + # CHECK IF INSTANCE IS FOUND + $AWS ec2 describe-instance-status --instance-id $INSTANCE_ID > /dev/null 2>&1 + stop_if_failed $? "instance $INSTANCE_ID not found, is your AWS_PROFILE exported and working?" + # KILL INSTANCE + pr_info "terminating instance $INSTANCE_ID" + $AWS ec2 terminate-instances --instance-ids $INSTANCE_ID + stop_if_failed $? "failed to terminate instance $INSTANCE_ID" + # WAIT FOR INSTANCE TO TERMINATE + while [[ `$AWS ec2 describe-instance-status --instance-id $INSTANCE_ID | $JQ -r ".InstanceStatuses[0].SystemStatus.Status"` != null ]] + do + pr_info "waiting instance $INSTANCE_ID to shutdown, hang on...." + sleep 1 + done + + pr_info "removing security group $SG_ID" + TRY=0 + until `$AWS ec2 delete-security-group --group-id $SG_ID` + do + pr_info "waiting the security group to be removable try $TRY" + ((TRY++)) + [[ $TRY == $TEARDOWN_MAX_RETRIES ]] && stop_if_failed 1 "failed to remove $SG_ID, are you sure that this sg exists?!" + sleep 6 + done + pr_info "removed security group $SG_ID" + pr_info "removing keypair $KEY_NAME" + $AWS --region ${AZ::-1} ec2 delete-key-pair --key-name $KEY_NAME + stop_if_failed $? "failed to remove keypair $KEY_NAME" + pr_end "everything has been cleaned up!" + exit 0 + +} \ No newline at end of file diff --git a/plugin/deployer/bash/main.sh b/plugin/deployer/bash/main.sh index 2de25030..fe80e18b 100644 --- a/plugin/deployer/bash/main.sh +++ b/plugin/deployer/bash/main.sh @@ -1,113 +1,6 @@ -# TO AVOID AWS CLI PAGINATION -export AWS_PAGER="" -# CHECK CONTAINER VARS -if [ $CONTAINER ] -then - [[ -z $AWS_ACCESS_KEY_ID ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_SECRET_ACCESS_KEY ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_DEFAULT_REGION ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" -fi -# CONSTS -INSTANCE_DESCRIPTION="instance_description.json" - -# CAN BE OVERRIDE BY CONTAINER ENV VARS -[ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" -[ -z $AMI_ID ] && AMI_ID="ami-0569ce8a44f2351be" - -# DEPENDENCIES CHECKS -AWS=`which aws 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" - -# PRIVATE METHODS -_get_ec2_instance_id() { - $JQ -r '.Instances[0].InstanceId' $1 -} - -_get_ec2_instance_public_ip(){ - INSTANCE_IP="" - while [ -z $INSTANCE_IP ] - do - INSTANCE_IP=`$AWS ec2 describe-instances --instance-ids $1 --query 'Reservations[*].Instances[*].PublicIpAddress' --output text` - done - echo "$INSTANCE_IP" -} - -_get_ec2_instance_private_ip(){ - $JQ -r '.Instances[0].PrivateIpAddress' $1 -} - -_create_ec2_resources() { - pr_info "creating EC2 resources" - RESOURCES_NAME="openspot-ng-$RANDOM_SUFFIX" - GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "openspot-ng security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` - stop_if_failed $? "failed to create EC2 security group" - # KEYPAIR (Created just because mandatory, will be swapped manually fore core user later on) - $AWS ec2 create-key-pair --key-name $RESOURCES_NAME --no-paginate - stop_if_failed $? "failed to create EC2 keypair" - # SG SETUP - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 22 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create SSH rule for security group" - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 6443 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create API rule for security group" - $AWS ec2 authorize-security-group-ingress --group-name $RESOURCES_NAME --protocol tcp --port 443 --cidr 0.0.0.0/0 --no-paginate - stop_if_failed $? "failed to create HTTPS rule for security group" - # CREATE INSTANCE - $AWS ec2 run-instances --no-paginate --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$RESOURCES_NAME}]" \ - --image-id $AMI_ID --instance-type $INSTANCE_TYPE --security-group-ids $GROUPID --key-name $RESOURCES_NAME > $WORKDIR/$INSTANCE_DESCRIPTION - - stop_if_failed $? "failed to launch EC2 instance" -} - -_destroy_ec2_resources() { - ID=$WORKDIR/$INSTANCE_DESCRIPTION - [ ! -f $ID ] && stop_if_failed 1 "Missing EC2 resource descriptor $INSTANCE_DESCRIPTION in $WORKDIR" - INSTANCE_ID=`$JQ -r '.Instances[0].InstanceId' $ID` - stop_if_failed $? "Failed to parse InstanceId from $ID" - KEY_NAME=`$JQ -r '.Instances[0].KeyName' $ID` - stop_if_failed $? "Failed to parse KeyName from $ID" - SG_ID=`$JQ -r '.Instances[0].SecurityGroups[0].GroupId' $ID` - stop_if_failed $? "Failed to parse SecurityGroupName from $ID" - AZ=`$JQ -r '.Instances[0].Placement.AvailabilityZone' $ID` - stop_if_failed $? "Failed to parse AvailabilityZone from $ID" - - # CHECK IF INSTANCE IS FOUND - $AWS ec2 describe-instance-status --instance-id $INSTANCE_ID > /dev/null 2>&1 - stop_if_failed $? "instance $INSTANCE_ID not found, is your AWS_PROFILE exported and working?" - # KILL INSTANCE - pr_info "terminating instance $INSTANCE_ID" - $AWS ec2 terminate-instances --instance-ids $INSTANCE_ID - stop_if_failed $? "failed to terminate instance $INSTANCE_ID" - # WAIT FOR INSTANCE TO TERMINATE - while [[ `$AWS ec2 describe-instance-status --instance-id $INSTANCE_ID | $JQ -r ".InstanceStatuses[0].SystemStatus.Status"` != null ]] - do - pr_info "waiting instance $INSTANCE_ID to shutdown, hang on...." - sleep 1 - done - - pr_info "removing security group $SG_ID" - TRY=0 - until `$AWS ec2 delete-security-group --group-id $SG_ID` - do - pr_info "waiting the security group to be removable try $TRY" - ((TRY++)) - [[ $TRY == $TEARDOWN_MAX_RETRIES ]] && stop_if_failed 1 "failed to remove $SG_ID, are you sure that this sg exists?!" - sleep 6 - done - pr_info "removed security group $SG_ID" - pr_info "removing keypair $KEY_NAME" - $AWS --region ${AZ::-1} ec2 delete-key-pair --key-name $KEY_NAME - stop_if_failed $? "failed to remove keypair $KEY_NAME" - pr_end "everything has been cleaned up!" - exit 0 - -} - -_parse_args() { +_bash_parse_args() { local opts='' # NOTE ON ARGUMENTS: # DUE TO A GETOPTS STRANGE BEHAVIOUR, IN ORDER TO ADD PLUGIN SPECIFIC ARGUMENTS @@ -122,26 +15,27 @@ _parse_args() { done } - - # INTERFACE METHODS +deployer_load_dependencies() { + source $PLUGIN_ROOT_FOLDER/aws.sh +} deployer_create() { - _parse_args $@ - _create_ec2_resources + _bash_parse_args $@ + _bash_create_ec2_resources } deployer_teardown() { - _destroy_ec2_resources + _bash_destroy_ec2_resources } deployer_get_eip() { - INSTANCE_ID=`_get_ec2_instance_id $WORKDIR/$INSTANCE_DESCRIPTION` - EIP=`_get_ec2_instance_public_ip $INSTANCE_ID` + INSTANCE_ID=`_bash_get_ec2_instance_id $WORKDIR/$INSTANCE_DESCRIPTION` + EIP=`_bash_get_ec2_instance_public_ip $INSTANCE_ID` echo $EIP } deployer_get_iip() { - IIP=`_get_ec2_instance_private_ip $WORKDIR/$INSTANCE_DESCRIPTION` + IIP=`_bash_get_ec2_instance_private_ip $WORKDIR/$INSTANCE_DESCRIPTION` echo $IIP } diff --git a/plugin/deployer/example/example_include.sh b/plugin/deployer/example/example_include.sh new file mode 100644 index 00000000..f7ee3a2d --- /dev/null +++ b/plugin/deployer/example/example_include.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +_example_test_include() { + echo "test include" +} diff --git a/plugin/deployer/example/main.sh b/plugin/deployer/example/main.sh index f34a76ed..c1493f93 100644 --- a/plugin/deployer/example/main.sh +++ b/plugin/deployer/example/main.sh @@ -1,7 +1,9 @@ # THIS METHOD IS NOT PART OF THE INTERFACE HAS BEEN PUT HERE AS AN EXAMPLE ON HOW # TO PARSE ADDITIONAL ARGUMENTS FOR A DEPLOYER PLUGIN -# NON INTERFACE METHODS *MUST* START WITH UNDERSCORE -_parse_args() { +# NON INTERFACE METHODS *MUST* START WITH __ SO IN THAT CASE, CONSIDERING THAT THE PLUGIN NAME IS example +# ALL THE METHODS MUST START WITH _example_ TO AVOID CONFLICTS AND KEEP THE CODE CLEAR + +_example_parse_args() { local opts='' #NOTE ON ARGUMENTS: #DUE TO A GETOPTS STRANGE BEHAVIOUR, IN ORDER TO ADD PLUGIN SPECIFIC ARGUMENTS @@ -16,9 +18,11 @@ _parse_args() { esac done echo $OPTIND - } + + + # INTERFACE METHODS deployer_create() { @@ -31,6 +35,11 @@ deployer_create() { exit 0 } +deployer_load_dependencies() { + pr_info "loads (if needed otherwise keep it empty) the plugin dependencies" + source $PLUGIN_ROOT_FOLDER/example_include.sh +} + deployer_teardown() { #all the command line args will be passed to that function pr_info "destroys infrastructure" From 0b8b8d6a46dbe951f1edb6d21a7077e68c2784fc Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Wed, 11 Jan 2023 16:45:12 +0100 Subject: [PATCH 5/6] laid out the structure for gcp logic in bash plugin --- README.md | 10 +++++--- api/common.sh | 18 +++++++------ crc-cloud.sh | 34 +++++++++++++++---------- plugin/deployer/bash/aws.sh | 49 +++++++++++++++++++----------------- plugin/deployer/bash/main.sh | 27 +++++++++++++++++--- 5 files changed, 87 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index a9574a47..553b9c37 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,18 @@ Increasing or decreasing the resources will affect the deployment time together | Argument | Description | Mandatory | --- | --- | --- | | -a | AMI ID (Amazon Machine Image) from which the VM will be Instantiated | false | - | -i | EC2 Instance Type | false + | -t | EC2 Instance Type | false | + | -i | Target infrastructure (aws,gcp) | true | #### Container variables | Variable | Description | Mandatory | | --- | --- | --- | -| AWS_ACCESS_KEY_ID | AWS access key (infos [here](#bash-aws-deployer-prereq)) | true | -| AWS_SECRET_ACCESS_KEY | AWS secret access key (infos [here](#bash-aws-deployer-prereq)) | true | -| AWS_DEFAULT_REGION | AWS region where the cluster will be deployed ( currently us-west-2 is the only supported) | true | +| AWS_ACCESS_KEY_ID | AWS access key (aws target infrastructure only) (infos [here](#bash-aws-deployer-prereq)) | true | +| AWS_SECRET_ACCESS_KEY | AWS secret (aws target infrastructure only) access key (infos [here](#bash-aws-deployer-prereq)) | true | +| AWS_DEFAULT_REGION | AWS region where the cluster will be deployed (aws target infrastructure only) ( currently us-west-2 is the only supported) | true | | INSTANCE_TYPE | AWS EC2 Instance Type | false | | AMI_ID | AMI ID (Amazon Machine Image) from which the VM will be Instantiated | false | +| TARGET_INFRASTRUCTURE | Target infrastructure (aws,gcp) | true |

diff --git a/api/common.sh b/api/common.sh index 5d841df1..5e16af97 100644 --- a/api/common.sh +++ b/api/common.sh @@ -55,6 +55,7 @@ wait_instance_readiness(){ # PARAMS $1: file path $2 plugin name api_check_method_name() { # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE + # IF THE INTERFACE WILL BE EXTENDED WITH OTHER METHODS ADD THEM HERE PLUGIN_NAME_LENGTH=`expr length $2` for i in `$CAT $1 | $GREP -P "^\s*.+\s*\(\)\s*\{"|$SED -r 's/(.+)\(\)\s*\{/\1/'` do @@ -79,27 +80,30 @@ api_load_deployer() { (*[![:lower:]_]*) stop_if_failed 1 "plugin name must contain only lowercase letters and underscores";; (*);; esac - + # IF THE INTERFACE WILL BE EXTENDED WITH OTHER METHODS ADD THEM HERE [ ! -d "$PLUGIN_DEPLOYER_FOLDER/$1" ] && stop_if_failed 1 "deployer API $1 folder not found in $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" [ ! -f "$PLUGIN_DEPLOYER_FOLDER/$1/main.sh" ] && stop_if_failed 1 "main.sh not found for deployer $1 in folder $PLUGIN_DEPLOYER_FOLDER/$1, please refer api/README.md for API specifications" source $PLUGIN_DEPLOYER_FOLDER/$1/main.sh [[ ! `declare -F deployer_create` ]] &&\ - stop_if_failed 1 "deployer_create method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_create method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" [[ ! `declare -F deployer_teardown` ]] &&\ - stop_if_failed 1 "deployer_teardown method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_teardown method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" [[ ! `declare -F deployer_get_eip` ]] &&\ - stop_if_failed 1 "deployer_get_eip method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_get_eip method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" [[ ! `declare -F deployer_get_iip` ]] &&\ - stop_if_failed 1 "deployer_get_iip method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_get_iip method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" [[ ! `declare -F deployer_usage` ]] &&\ - stop_if_failed 1 "deployer_usage method not found in main.sh implementation for $1 infrastructure deployer api, please refer api/README.md for API specifications" + stop_if_failed 1 "deployer_usage method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" + + [[ ! `declare -F deployer_load_dependencies` ]] &&\ + stop_if_failed 1 "deployer_load_dependencies method not found in main.sh implementation for $1 infrastructure deployer plugin, please refer api/README.md for API specifications" - # CHECKS FOR NOT INTERFACE METHDODS NAME NOT STARTING WITH UNDERSCORE, IF THE INTERFACE WILL BE EXTENDED ADD THEM TO THE SWITCH CASE + # CHECKS FOR INTERFACE METHDODS NAME NOT STARTING WITH __ , api_check_method_name $PLUGIN_DEPLOYER_FOLDER/$1/main.sh $1 # CHECKS main.sh INCLUDES FOR METHOD NAME COMPLIANCE for i in `$CAT $PLUGIN_DEPLOYER_FOLDER/$1/main.sh | $GREP -P "^\s+source .+\.sh" | sed -r 's#source\s+.+/(.*)#\1#'` diff --git a/crc-cloud.sh b/crc-cloud.sh index 917c704b..babb0582 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -140,7 +140,8 @@ create () { teardown() { WORKDIR="$WORKDIR_PATH/$TEARDOWN_RUN_ID" [ ! -d $WORKDIR ] && stop_if_failed 1 "$WORKDIR not found, please provide a correct path" - deployer_teardown + deployer_load_dependencies + deployer_teardown $@ } set_workdir_dependent_variables() { @@ -154,7 +155,7 @@ set_workdir_dependent_variables() { parse_args () { while getopts $BASE_OPTIONS option; do case "$option" in - h) usage;; + h) SHOW_HELP=1;; C) WORKING_MODE="C";pr_info "working mode: CREATE";; D) DEPLOYER_API=$OPTARG;pr_info "deployer api: $OPTARG";; T) WORKING_MODE="T";pr_info "working mode: TEARDOWN";; @@ -250,7 +251,7 @@ $(basename "$0") -T [-D infrastructure_deployer] [-v run id] echo -e "\n*********** Deployer: $DEPLOYER_API ***********" deployer_usage - exit 1 + exit 0 } ### END FUNCTIONS @@ -273,6 +274,8 @@ source ./api/common.sh set_workdir_dependent_variables + + ## PARSE & CHECK ARGS if [ $CONTAINER ] then @@ -291,28 +294,33 @@ else parse_args $@ ## VARIABLES SANITY CHECKS # WORKING MODE CHECK - [[ (-z $WORKING_MODE ) ]] && echo -e "\nERROR: Working mode must be set\n" && usage - [[ ( $WORKING_MODE != "C" ) && ( $WORKING_MODE != "T" ) ]] && echo \ - -e "\nERROR: Working mode Must be either -C (creation) or -T (teardown), not $WORKING_MODE\n" && usage - # CHECK MANDATORY ARGS FOR CREATION - [[ ($WORKING_MODE == "C" ) && ( ! "$PULL_SECRET_PATH" ) ]] && \ - echo -e "\nERROR: in creation mode argument -p must be provided\n" && usage - # CHECK PULL SECRET PATH - [[ ($WORKING_MODE == "C" ) && ( ! -f $PULL_SECRET_PATH ) ]] && \ - echo -e "\nERROR: $PULL_SECRET_PATH pull secret file not found" && usage + if [[ ! $SHOW_HELP ]] + then + [[ (-z $WORKING_MODE ) ]] && echo -e "\nERROR: Working mode must be set\n" && usage + [[ ( $WORKING_MODE != "C" ) && ( $WORKING_MODE != "T" ) ]] && echo \ + -e "\nERROR: Working mode Must be either -C (creation) or -T (teardown), not $WORKING_MODE\n" && usage + # CHECK MANDATORY ARGS FOR CREATION + [[ ($WORKING_MODE == "C" ) && ( ! "$PULL_SECRET_PATH" ) ]] && \ + echo -e "\nERROR: in creation mode argument -p must be provided\n" && usage + # CHECK PULL SECRET PATH + [[ ($WORKING_MODE == "C" ) && ( ! -f $PULL_SECRET_PATH ) ]] && \ + echo -e "\nERROR: $PULL_SECRET_PATH pull secret file not found" && usage + fi fi ## LOAD DEPLOYER API api_load_deployer $DEPLOYER_API +[[ $SHOW_USAGE ]] && usage + ## ENTRYPOINT: if everything is ok, run the script. if [[ $WORKING_MODE == "C" ]] then create $@ elif [[ $WORKING_MODE == "T" ]] then - teardown + teardown $@ else usage fi diff --git a/plugin/deployer/bash/aws.sh b/plugin/deployer/bash/aws.sh index cb68f05f..b408395f 100644 --- a/plugin/deployer/bash/aws.sh +++ b/plugin/deployer/bash/aws.sh @@ -1,29 +1,33 @@ #!/bin/bash -# TO AVOID AWS CLI PAGINATION -export AWS_PAGER="" +_bash_ec2_init() { + # TO AVOID AWS CLI PAGINATION + export AWS_PAGER="" -# CHECK CONTAINER VARS -if [ $CONTAINER ] -then - [[ -z $AWS_ACCESS_KEY_ID ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_SECRET_ACCESS_KEY ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" - [[ -z $AWS_DEFAULT_REGION ]] &&\ - stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" -fi + # CHECK CONTAINER VARS + if [ $CONTAINER ] + then + [[ -z $AWS_ACCESS_KEY_ID ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_SECRET_ACCESS_KEY ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $AWS_DEFAULT_REGION ]] &&\ + stop_if_failed 1 "AWS_ACCESS_KEY_ID must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + [[ -z $TARGET_INFRASTRUCTURE ]] &&\ + stop_if_failed 1 "TARGET_INFRASTRUCTURE must be set, please refer to AWS CLI documentation https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html" + fi -# CONSTS -INSTANCE_DESCRIPTION="instance_description.json" + # CONSTS + INSTANCE_DESCRIPTION="instance_description.json" -# CAN BE OVERRIDE BY CONTAINER ENV VARS -[ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" -[ -z $AMI_ID ] && AMI_ID="ami-0569ce8a44f2351be" + # CAN BE OVERRIDE BY CONTAINER ENV VARS + [ -z $INSTANCE_TYPE ] && INSTANCE_TYPE="c6in.2xlarge" + [ -z $AMI_ID ] && AMI_ID="ami-0569ce8a44f2351be" -# DEPENDENCIES CHECKS -AWS=`which aws 2>/dev/null` -[[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" + # DEPENDENCIES CHECKS + AWS=`which aws 2>/dev/null` + [[ $? != 0 ]] && stop_if_failed 1 "[DEPENDENCY MISSING]: aws cli, please install it and try again" +} # PRIVATE AWS METHODS @@ -46,8 +50,8 @@ _bash_get_ec2_instance_private_ip(){ _bash_create_ec2_resources() { pr_info "creating EC2 resources" - RESOURCES_NAME="openspot-ng-$RANDOM_SUFFIX" - GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "openspot-ng security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` + RESOURCES_NAME="crc-cloud-$RANDOM_SUFFIX" + GROUPID=`aws ec2 create-security-group --group-name $RESOURCES_NAME --description "crc-cloud security group run timestamp: $RUN_TIMESTAMP" --no-paginate | $JQ -r '.GroupId'` stop_if_failed $? "failed to create EC2 security group" # KEYPAIR (Created just because mandatory, will be swapped manually fore core user later on) $AWS ec2 create-key-pair --key-name $RESOURCES_NAME --no-paginate @@ -107,5 +111,4 @@ _bash_destroy_ec2_resources() { stop_if_failed $? "failed to remove keypair $KEY_NAME" pr_end "everything has been cleaned up!" exit 0 - } \ No newline at end of file diff --git a/plugin/deployer/bash/main.sh b/plugin/deployer/bash/main.sh index fe80e18b..c4161a32 100644 --- a/plugin/deployer/bash/main.sh +++ b/plugin/deployer/bash/main.sh @@ -5,10 +5,11 @@ _bash_parse_args() { # NOTE ON ARGUMENTS: # DUE TO A GETOPTS STRANGE BEHAVIOUR, IN ORDER TO ADD PLUGIN SPECIFIC ARGUMENTS # IT IS *MANDATORY* TO CONCATENATE WITH THE MAIN SCRIPT ARGUMENT STRING ($BASE_OPTIONS) - while getopts "${BASE_OPTIONS}t:a:" option; do + while getopts "${BASE_OPTIONS}t:a:i:" option; do case "$option" in a) AMI_ID=$OPTARG;pr_info "provided ami-id: $AMI_ID";; t) INSTANCE_TYPE=$OPTARG;pr_info "provided instance type: $INSTANCE_TYPE";; + i) TARGET_INFRASTRUCTURE=$OPTARG; pr_info "selected target infrastructure: $TARGET_INFRASTRUCTURE";; :) printf "missing argument for -%s\n" "$OPTARG" >&2; usage;; \?) esac @@ -19,13 +20,31 @@ _bash_parse_args() { deployer_load_dependencies() { source $PLUGIN_ROOT_FOLDER/aws.sh } + deployer_create() { - _bash_parse_args $@ - _bash_create_ec2_resources + [[ ! $CONTAINER ]] && _bash_parse_args $@ + case $TARGET_INFRASTRUCTURE in + aws) + _bash_ec2_init + _bash_create_ec2_resources + ;; + gcp)pr_info "coming soon";exit 0;; + "") stop_if_failed 1 "please select a target infrastructure [aws,gcp], if running in container mode please set environment variable TARGET_INFRASTRUCTURE";; + *) stop_if_failed 1 "$TARGET_INFRASTRUCTURE is not a valid target infrastructure";; + esac } deployer_teardown() { - _bash_destroy_ec2_resources + [[ ! $CONTAINER ]] && _bash_parse_args $@ + case $TARGET_INFRASTRUCTURE in + aws) + _bash_ec2_init + _bash_destroy_ec2_resources + ;; + gcp)pr_info "coming soon";exit 0;; + "") stop_if_failed 1 "please select a target infrastructure [aws,gcp], if running in container mode please set environment variable TARGET_INFRASTRUCTURE";; + *) stop_if_failed 1 "$TARGET_INFRASTRUCTURE is not a valid target infrastructure";; + esac } deployer_get_eip() { From e0e2ae05b32744c2f4fbb5bbebb09bb61e1ad9fa Mon Sep 17 00:00:00 2001 From: Tullio Sebastiani Date: Wed, 11 Jan 2023 17:19:03 +0100 Subject: [PATCH 6/6] fixed ssh quiet and UserKnownHostsFile as for PR #17 --- README.md | 2 -- crc-cloud.sh | 16 ++++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 553b9c37..1196e9eb 100644 --- a/README.md +++ b/README.md @@ -195,8 +195,6 @@ where: -d developer user password (optional, default: $PASS_DEVELOPER) -k kubeadmin user password (optional, default: $PASS_KUBEADMIN) -r redhat user password (optional, default: $PASS_REDHAT) - -a AMI ID (Amazon Machine Image) from which the VM will be Instantiated (optional, default: $AMI_ID) - -i EC2 Instance Type (optional, default; $INSTANCE_TYPE) -h show this help text ``` #### Single node cluster teardown diff --git a/crc-cloud.sh b/crc-cloud.sh index babb0582..001a06fc 100755 --- a/crc-cloud.sh +++ b/crc-cloud.sh @@ -56,9 +56,9 @@ swap_ssh_key() { pr_info "swapping default key with the one just created" $SSHKEYGEN -f $WORKDIR/id_rsa -q -N '' stop_if_failed $? "failed to generate the key pair" - $SCP -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY $WORKDIR/id_rsa.pub core@$EIP:. + $SCP -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY $WORKDIR/id_rsa.pub core@$EIP:. stop_if_failed $? "failed to upload the public key on the machine @ $EIP" - $SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "cat /home/core/id_rsa.pub > /home/core/.ssh/authorized_keys" + $SSH -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "cat /home/core/id_rsa.pub > /home/core/.ssh/authorized_keys" stop_if_failed $? "failed to swap the key on the authorized_keys @ $EIP" # after swapping on VM private key is replaced by the new one PRIVATE_KEY=$WORKDIR/id_rsa @@ -66,20 +66,20 @@ swap_ssh_key() { inject_and_run_cluster_setup() { pr_info "injecting the setup script on the machine & running it [next logs will be remote]" - $SCP -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY $WORKDIR/cluster_setup.sh core@$EIP:/var/home/core/ - $SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "chmod +x /var/home/core/cluster_setup.sh" - $SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "sudo /var/home/core/cluster_setup.sh" + $SCP -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY $WORKDIR/cluster_setup.sh core@$EIP:/var/home/core/ + $SSH -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "chmod +x /var/home/core/cluster_setup.sh" + $SSH -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "sudo /var/home/core/cluster_setup.sh" } tail_cluster_setup() { pr_info "reading from VM logs from remote instance" pr_info "waiting the log to be created, hang on...." - $SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "while [ ! -f /tmp/$RANDOM_SUFFIX.log ]; do sleep 1; done" + $SSH -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "while [ ! -f /tmp/$RANDOM_SUFFIX.log ]; do sleep 1; done" pr_info "log detected, printing remote output on $EIP:" PREVIOUS_LINE="" while : do - LINE=$($SSH -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "sudo tail -n 1 /tmp/$RANDOM_SUFFIX.log") + LINE=$($SSH -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -p $SSH_PORT -i $PRIVATE_KEY core@$EIP "sudo tail -n 1 /tmp/$RANDOM_SUFFIX.log") stop_if_failed $? "impossible to get the logs from $EIP" # xargs used to remove leading space if [[ $LINE =~ "[ERR]" ]] @@ -104,7 +104,7 @@ tail_cluster_setup() { get_remote_log() { pr_info "getting remote setup log" - $SCP -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY core@$EIP:/tmp/$RANDOM_SUFFIX.log $WORKDIR/remote.log + $SCP -q -o UserKnownHostsFile=/dev/null -o StrictHostKeychecking=no -P $SSH_PORT -i $PRIVATE_KEY core@$EIP:/tmp/$RANDOM_SUFFIX.log $WORKDIR/remote.log stop_if_failed $? "impossible to get the logs from $EIP" }