diff --git a/api/README.md b/api/README.md index 1eb8387e64..a0aa3cae46 100644 --- a/api/README.md +++ b/api/README.md @@ -99,3 +99,15 @@ through unit tests and integration tests that exercise the operations. In order to test the API specifically, there are integraiton tests which will deploy the API and test the functionality using the generated client. +### Invoking the API + +Install requirements for the example: +``` +pip install -r client/requirements.txt +``` + +Invoke a deployed ParallelCluster API: +``` +python client/example.py --region [REGION] --stack-name [PCAPI_STACK_NAME] +``` + diff --git a/api/client/example.py b/api/client/example.py index 8852a20ad4..8ac66be026 100755 --- a/api/client/example.py +++ b/api/client/example.py @@ -13,19 +13,18 @@ # language governing permissions and limitations under the License. import boto3 +import click from pcluster_client.api import cluster_operations_api from pcluster_client import Configuration, ApiClient, ApiException -apigateway = boto3.client("apigateway") - -def request(): +@click.command() +@click.option("--stack-name", help="ParallelCluster API stack name") +@click.option("--region", help="AWS region") +def request(stack_name: str, region: str): """Makes a simple request to the API Gateway""" - apis = apigateway.get_rest_apis()["items"] - api_id = next(api["id"] for api in apis if api["name"] == "ParallelCluster") - region = boto3.session.Session().region_name - host = f"{api_id}.execute-api.{region}.amazonaws.com" - configuration = Configuration(host=f"https://{host}/prod") + invoke_url = describe_stack_output(region, stack_name, "ParallelClusterApiInvokeUrl") + configuration = Configuration(host=invoke_url) with ApiClient(configuration) as api_client: client = cluster_operations_api.ClusterOperationsApi(api_client) @@ -33,10 +32,30 @@ def request(): try: response = client.list_clusters(region=region_filter) - print("clusters: ", [c["cluster_name"] for c in response["clusters"]]) + print("Response: ", response) except ApiException as ex: print("Exception when calling ClusterOperationsApi->list_clusters: %s\n" % ex) +def describe_stack_output(region: str, stack_name: str, output_name: str): + try: + # Describe stack + cloudformation = boto3.client("cloudformation", region_name=region) + response = cloudformation.describe_stacks(StackName=stack_name) + + # Get the stack details + stacks = response.get("Stacks", []) + if not stacks: + print(f"No stacks found with the name: {stack_name}") + return None + + # Extract output + outputs = stacks[0].get("Outputs", []) + return list(filter(lambda o: o['OutputKey'] == 'ParallelClusterApiInvokeUrl', outputs))[0]['OutputValue'] + + except Exception as e: + print(f"Cannot describe output '{output_name}' for stack '{stack_name}': {e}") + return None + if __name__ == "__main__": request() diff --git a/api/client/requirements.txt b/api/client/requirements.txt new file mode 100644 index 0000000000..fcbc3b2f8e --- /dev/null +++ b/api/client/requirements.txt @@ -0,0 +1,2 @@ +boto3>=1.16.14 +click~=8.1.7 \ No newline at end of file diff --git a/api/infrastructure/deploy-api.sh b/api/infrastructure/deploy-api.sh index d1307aac88..bfc4f0c9f0 100755 --- a/api/infrastructure/deploy-api.sh +++ b/api/infrastructure/deploy-api.sh @@ -7,7 +7,7 @@ # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and # limitations under the License. -usage="$(basename "$0") [-h] --s3-bucket bucket-name --region aws-region [--stack-name name] [--enable-iam-admin true|false] [--create-api-user true|false])" +usage="$(basename "$0") [-h] --s3-bucket bucket-name --region aws-region [--stack-name name] [--enable-iam-admin true|false] [--create-api-user true|false] [--lambda-layer abs_path]" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" @@ -15,6 +15,7 @@ S3_BUCKET= STACK_NAME="ParallelClusterApi" ENABLE_IAM_ADMIN="true" CREATE_API_USER="false" +LAMBDA_LAYER= while [[ $# -gt 0 ]] do key="$1" @@ -59,6 +60,11 @@ case $key in shift # past argument shift # past value ;; + --lambda-layer) + export LAMBDA_LAYER=$2 + shift # past argument + shift # past value + ;; *) # unknown option echo "$usage" >&2 exit 1 @@ -71,6 +77,8 @@ if [ -z "${S3_BUCKET}" ] || [ -z "${AWS_DEFAULT_REGION}" ] ; then exit 1 fi +PC_VERSION=$(yq ".Mappings.ParallelCluster.Constants.Version" "${SCRIPT_DIR}/parallelcluster-api.yaml") + S3_UPLOAD_URI="s3://${S3_BUCKET}/api/ParallelCluster.openapi.yaml" POLICIES_S3_URI="s3://${S3_BUCKET}/stacks/parallelcluster-policies.yaml" POLICIES_TEMPLATE_URI="http://${S3_BUCKET}.s3.${AWS_DEFAULT_REGION}.amazonaws.com/stacks/parallelcluster-policies.yaml" @@ -81,6 +89,12 @@ aws s3 cp "${SCRIPT_DIR}/../spec/openapi/ParallelCluster.openapi.yaml" "${S3_UPL echo "Publishing policies CloudFormation stack to S3" aws s3 cp "${SCRIPT_DIR}/../../cloudformation/policies/parallelcluster-policies.yaml" "${POLICIES_S3_URI}" +if [ -n "${LAMBDA_LAYER}" ]; then + LAMBDA_LAYER_S3_URI="s3://${S3_BUCKET}/parallelcluster/${PC_VERSION}/layers/aws-parallelcluster/lambda-layer.zip" + echo "Publishing Lambda Layer for version ${PC_VERSION} to S3" + aws s3 cp "${LAMBDA_LAYER}" "${LAMBDA_LAYER_S3_URI}" +fi + echo "Deploying API template" aws cloudformation deploy \ --stack-name "${STACK_NAME}" \ @@ -90,4 +104,5 @@ aws cloudformation deploy \ --parameter-overrides ApiDefinitionS3Uri="${S3_UPLOAD_URI}" \ PoliciesTemplateUri="${POLICIES_TEMPLATE_URI}" \ EnableIamAdminAccess="${ENABLE_IAM_ADMIN}" CreateApiUserRole="${CREATE_API_USER}" \ + "$([[ -n "${LAMBDA_LAYER}" ]] && echo "CustomBucket=${S3_BUCKET}" || echo " ")" \ --capabilities CAPABILITY_NAMED_IAM