diff --git a/cft-templates/sagemaker-template-with-url.yml b/cft-templates/sagemaker-template-with-url.yml index f1bb802..c3ef6ae 100644 --- a/cft-templates/sagemaker-template-with-url.yml +++ b/cft-templates/sagemaker-template-with-url.yml @@ -5,6 +5,20 @@ Description: 'AWS CloudFormation Sample Template SageMaker NotebookInstance: Thi the creation of a SageMaker NotebookInstance with encryption. You will be billed for the AWS resources used if you create a stack from this template. (fdp-1qj64b3fd)' Parameters: + Namespace: + Type: String + Description: An environment name that will be prefixed to resource names + S3Mounts: + Type: String + Description: A JSON array of objects with name, bucket and prefix properties used to mount data + IamPolicyDocument: + Type: String + Description: The IAM policy to be associated with the launched workstation + EnvironmentInstanceFiles: + Type: String + Description: >- + An S3 URI (starting with "s3://") that specifies the location of files to be copied to + the environment instance, including any bootstrap scripts NotebookInstanceType: AllowedValues: - ml.t2.medium @@ -21,10 +35,15 @@ Parameters: Default: ml.t3.medium Description: Select Instance type for the SageMaker Notebook. e.g.ml.t3.medium Type: String +Conditions: + IamPolicyEmpty: !Equals [!Ref IamPolicyDocument, '{}'] + Resources: - SageMakerRole: + IAMRole: Type: AWS::IAM::Role Properties: + RoleName: !Join ['-', [Ref: Namespace, 'sagemaker-notebook-role']] + Path: '/' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: @@ -34,6 +53,12 @@ Resources: - "sagemaker.amazonaws.com" Action: - "sts:AssumeRole" + Policies: + - !If + - IamPolicyEmpty + - !Ref 'AWS::NoValue' + - PolicyName: !Join ['-', [Ref: Namespace, 's3-studydata-policy']] + PolicyDocument: !Ref IamPolicyDocument ManagedPolicyArns: - "arn:aws:iam::aws:policy/AmazonSageMakerFullAccess" - "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" @@ -42,8 +67,8 @@ Resources: Type: "AWS::SageMaker::NotebookInstance" Properties: InstanceType: !Ref NotebookInstanceType - RoleArn: !GetAtt SageMakerRole.Arn - PlatformIdentifier: notebook-al2-v2 + RoleArn: !GetAtt IAMRole.Arn + PlatformIdentifier: notebook-al2-v1 LifecycleConfigName: !GetAtt NotebookLifeCycleConfig.NotebookInstanceLifecycleConfigName Tags: - Key: cost_resource @@ -57,10 +82,11 @@ Resources: OnStart: - Content: Fn::Base64: !Sub | - #!/bin/bash - - set -e - + #!/usr/bin/env bash + aws s3 cp "${EnvironmentInstanceFiles}/get_bootstrap.sh" "/tmp" + chmod 500 "/tmp/get_bootstrap.sh" + /tmp/get_bootstrap.sh "${EnvironmentInstanceFiles}" '${S3Mounts}' + function get_tag() { @@ -80,7 +106,6 @@ Resources: echo "export 'DATA_BUCKET'=$DATA_BUCKET" >> /etc/profile.d/jupyter-env.sh fi - #Copy Sample notebook from S3 TEMPLATE_NOTEBOOK_BUCKET=$(get_tag "TEMPLATE_NOTEBOOK_BUCKET") @@ -98,12 +123,13 @@ Resources: systemctl restart jupyter-server - - Outputs: SageMakerNotebookInstanceARN: Description: "ARN for the newly created SageMaker Notebook Instance" Value: !Ref SageMakerNotebookInstance NotebookInstanceName: Description: "Name for the newly created SageMaker Notebook Instance" - Value: !GetAtt [SageMakerNotebookInstance, NotebookInstanceName] \ No newline at end of file + Value: !GetAtt [SageMakerNotebookInstance, NotebookInstanceName] + WorkspaceInstanceRoleArn: + Description: IAM role assumed by the SageMaker workspace instance + Value: !GetAtt IAMRole.Arn \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d633afd..b1da501 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: - sp2net cc-3102: - image: 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.16.0_b1600 + image: 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.17.0_b1675 secrets: - source: sp2prod-config.json target: /rlc/cc/server/app/config/config.json @@ -42,7 +42,7 @@ services: scheduler-3102: - image: 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.16.0_b1600 + image: 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.17.0_b1675 secrets: - source: sp2prod-config.json target: /rlc/cc/server/app/config/config.json diff --git a/dump/configs.json b/dump/configs.json index 4dec756..5a597fc 100644 --- a/dump/configs.json +++ b/dump/configs.json @@ -224,7 +224,7 @@ }, "key": "projectStorage", "value": { - "productList": ["ec2", "rstudio", "nextflow", "cromwell", "ec2-dcv"], + "productList": ["ec2", "rstudio", "nextflow", "cromwell", "ec2-dcv", "sagemaker"], "s3Mounts": { "ProjectStorage": { "id": "ProjectStorage", @@ -404,10 +404,11 @@ "ec2", "rstudio", "nextflow", - "cromwell" + "cromwell", + "sagemaker" ] }, - "studySelectionCount" : 4 + "studySelectionCount" : 5.0 }, { "_id" : { diff --git a/dump/standardcatalogitems.json b/dump/standardcatalogitems.json index 0355eb8..bd4d474 100644 --- a/dump/standardcatalogitems.json +++ b/dump/standardcatalogitems.json @@ -254,7 +254,29 @@ "availableRegions": [], "assignedOU": [], "metaData": { - "pre_provisioning": [], + "pre_provisioning": [ + { + "code" : "CFT_PARAMS", + "params" : [ + { + "name" : "EnvironmentInstanceFiles", + "type" : "RL::SC::PARAM::HD" + }, + { + "name" : "IamPolicyDocument", + "type" : "RL::SC::PARAM::HD" + }, + { + "name" : "S3Mounts", + "type" : "RL::SC::PARAM::HD" + }, + { + "name" : "Namespace", + "type" : "RL::SC::PARAM::HD" + } + ] + } + ], "post_provisioning": [], "checks_before_assigning_product": [], "checks_after_assigning_product": [], diff --git a/packer-rg.json b/packer-rg.json index 7aac6fd..a1c198f 100644 --- a/packer-rg.json +++ b/packer-rg.json @@ -6,7 +6,7 @@ "awsRegion": "", "RG_HOME": "/opt/deploy/sp2", "RG_SRC": "/home/ubuntu", - "amiName": "RG-AMI-1.16.0" + "amiName": "RG-AMI-1.17.0" }, "builders": [ { diff --git a/provisioners/provision-ecr.sh b/provisioners/provision-ecr.sh index aaf1a8e..78865e2 100644 --- a/provisioners/provision-ecr.sh +++ b/provisioners/provision-ecr.sh @@ -1,5 +1,5 @@ #!/bin/bash -xe sudo docker login -u AWS -p $(aws ecr get-login-password --region us-east-2) 045938549113.dkr.ecr.us-east-2.amazonaws.com -sudo docker pull 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.16.0_b1600 +sudo docker pull 045938549113.dkr.ecr.us-east-2.amazonaws.com/researchportal:_fd_1.17.0_b1675 sudo docker pull 045938549113.dkr.ecr.us-east-2.amazonaws.com/nginx:latest sudo docker pull 045938549113.dkr.ecr.us-east-2.amazonaws.com/notificationsink:1.15.0_b1 diff --git a/rg_document_db.yml b/rg_document_db.yml index 2f7d0bb..44daa08 100644 --- a/rg_document_db.yml +++ b/rg_document_db.yml @@ -144,6 +144,7 @@ Resources: DBSubnetGroupName : !Ref DocumentDBSubnetGroup VpcSecurityGroupIds: - !GetAtt DocumentDBSecurityGroup.GroupId + StorageEncrypted : true DBInstance: Type: "AWS::DocDB::DBInstance" Properties: diff --git a/scripts/bootstrap-scripts/bin/mount_s3.sh b/scripts/bootstrap-scripts/bin/mount_s3.sh index 500cc1a..03815bb 100644 --- a/scripts/bootstrap-scripts/bin/mount_s3.sh +++ b/scripts/bootstrap-scripts/bin/mount_s3.sh @@ -17,6 +17,14 @@ AWS_CONFIG_DIR="${HOME}/.aws" # Exit if CONFIG doesn't exist or is 0 bytes [ ! -s "$CONFIG" ] && exit 0 +# Define a function to determine what type of environment this is (EMR, SageMaker, RStudio, or EC2 Linux) +env_type() { + if [ -d "/home/ec2-user/SageMaker" ] + then + printf "sagemaker" + fi +} + # Add roleArn for a study to credentials file if not present already append_role_to_credentials() { study_id=$1 @@ -32,6 +40,7 @@ append_role_to_credentials() { fi } +export AWS_SDK_LOAD_CONFIG=1 # Mount S3 buckets mounts="$(cat "$CONFIG")" num_mounts=$(printf "%s" "$mounts" | jq ". | length" -) @@ -73,3 +82,21 @@ do fi fi done + +# Define where the Jupyter notebook (if any) should be running +notebook_dir="" +case "$(env_type)" in + "sagemaker") + notebook_dir="/home/ec2-user/SageMaker" + ;; +esac + +# Add a link to the mount in the notebook directory. +# (The user gets easy access, but it won't check the bucket into a git repo.) +# Only create a link if Jupyter is running, there are studies mounted, and the link +# doesn't already exist. +if [ -n "$notebook_dir" -a $num_mounts -ne 0 ] +then + symlink_name="$notebook_dir/studies" + [ ! -L "$symlink_name" ] && sudo ln -s "$MOUNT_DIR" "$symlink_name" +fi \ No newline at end of file diff --git a/scripts/bootstrap-scripts/bootstrap.sh b/scripts/bootstrap-scripts/bootstrap.sh index e08a113..eac71fe 100644 --- a/scripts/bootstrap-scripts/bootstrap.sh +++ b/scripts/bootstrap-scripts/bootstrap.sh @@ -14,7 +14,7 @@ RSTUDIO_USER="$2" # Get directory in which this script is stored and define URL from which to download goofys FILES_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -GOOFYS_URL="https://github.com/kahing/goofys/releases/download/v0.21.0/goofys" +GOOFYS_URL="https://github.com/kahing/goofys/releases/download/v0.24.0/goofys" # Define a function to determine what type of environment this is (RStudio, or EC2 Linux) env_type() { @@ -24,23 +24,74 @@ env_type() { elif [ -f "/usr/bin/nextflow" ] then printf "nextflow" + elif [ -d "/home/ec2-user/SageMaker" ] + then + printf "sagemaker" else printf "ec2-linux" fi } +# Define a function to update Jupyter configuration files +update_jupyter_config() { + + config_file="$1" + + # HACK: Update the default SessionManager class used by Jupyter notebooks + # so that it runs the S3 mount script the first time sessions are listed + cat << EOF | cut -b5- >> "$config_file" + + import subprocess + from notebook.services.sessions.sessionmanager import SessionManager as BaseSessionManager + + class SessionManager(BaseSessionManager): + def list_sessions(self, *args, **kwargs): + """Override default list_sessions() method""" + self.mount_studies() + result = super(SessionManager, self).list_sessions(*args, **kwargs) + return result + + def mount_studies(self): + """Execute mount_s3.sh if it hasn't already been run""" + if not hasattr(self, 'studies_mounted'): + mounting_result = subprocess.run( + "mount_s3.sh", + stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + + # Log results + if mounting_result.stdout: + for line in mounting_result.stdout.decode("utf-8").split("\n"): + if line: # Skip empty lines + self.log.info(line) + + self.studies_mounted = True + + c.NotebookApp.session_manager_class = SessionManager +EOF +} + # Install dependencies -yum install -y jq-1.5 -curl -LSs -o "/usr/local/bin/goofys" "$GOOFYS_URL" +echo "Installing JQ" +sudo mv "${FILES_DIR}/offline-packages/jq-1.5-linux64" "/usr/local/bin/jq" +chmod +x "/usr/local/bin/jq" +echo "Finish installing jq" + +echo "Copying Goofys from bootstrap.sh" +cp "${FILES_DIR}/offline-packages/goofys" /usr/local/bin/goofys chmod +x "/usr/local/bin/goofys" # Install ec2 instance connect agent sudo yum install ec2-instance-connect-1.1 # Create S3 mount script and config file +echo "Mounting S3" chmod +x "${FILES_DIR}/bin/mount_s3.sh" ln -s "${FILES_DIR}/bin/mount_s3.sh" "/usr/local/bin/mount_s3.sh" printf "%s" "$S3_MOUNTS" > "/usr/local/etc/s3-mounts.json" +echo "Finish mounting S3" + +OS_VERSION=`cat /etc/os-release | grep VERSION= | sed 's/VERSION="//' | sed 's/"//'` # Apply updates to environments based on environment type case "$(env_type)" in @@ -56,6 +107,31 @@ case "$(env_type)" in yum install -y fuse-2.9.2 printf "\n# Mount S3 study data\nmount_s3.sh\n\n" >> "/home/ec2-user/.bash_profile" ;; + "sagemaker") # Update config and restart Jupyter + if [ $OS_VERSION = '2' ] + then + echo "Installing fuse for AL2" + cd "${FILES_DIR}/offline-packages/sagemaker/fuse-2.9.4_AL2" + sudo yum --disablerepo=* localinstall -y *.rpm + echo "Finish installing fuse" + echo "Installing boto3 for AL2" + cd "${FILES_DIR}/offline-packages/sagemaker/boto3" + sudo yum --disablerepo=* localinstall -y python2-boto3-1.4.4-1.amzn2.noarch.rpm + echo "Finish installing boto3" + else + echo "Installing fuse for AL1" + cd "${FILES_DIR}/offline-packages/sagemaker/fuse-2.9.4" + sudo yum --disablerepo=* localinstall -y *.rpm + echo "Finish installing fuse" + fi + update_jupyter_config "/home/ec2-user/.jupyter/jupyter_notebook_config.py" + if [ $OS_VERSION = '2' ] + then + systemctl restart jupyter-server + else + initctl restart jupyter-server --no-wait + fi + ;; esac exit 0 diff --git a/scripts/bootstrap-scripts/bootstrap_chenlab.sh b/scripts/bootstrap-scripts/bootstrap_chenlab.sh new file mode 100644 index 0000000..4721973 --- /dev/null +++ b/scripts/bootstrap-scripts/bootstrap_chenlab.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This script bootstraps a workspace instance by preparing S3 study data to be +# mounted via the mount_s3.sh environment script. +# Note that mounting cannot be performed during initial bootstrapping +# because the instance's role will not yet have access to S3 study +# data since the associated resource policies aren't updated until after the +# CFN stack has been completed created. +S3_MOUNTS="$1" + +# Exit if no S3 mounts were specified +[ -z "$S3_MOUNTS" ] || [ "$S3_MOUNTS" = "[]" ] && exit 0 + +# Get directory in which this script is stored and define URL from which to download goofys +FILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +GOOFYS_URL="https://github.com/kahing/goofys/releases/download/v0.21.0/goofys" + +# Install dependencies +apt-get install jq=1.6-1ubuntu0.20.04.1 +curl -LSs -o "/usr/local/bin/goofys" "$GOOFYS_URL" +chmod +x "/usr/local/bin/goofys" + +# Install ec2 instance connect agent +apt-get install ec2-instance-connect=1.1.12+dfsg1-0ubuntu3.20.04.1 + +# Create S3 mount script and config file +chmod +x "${FILES_DIR}/bin/mount_s3.sh" +ln -s "${FILES_DIR}/bin/mount_s3.sh" "/usr/local/bin/mount_s3.sh" +printf "%s" "$S3_MOUNTS" >"/usr/local/etc/s3-mounts.json" +apt-get install fuse=2.9.9-3 +printf "\n# Mount S3 study data\nmount_s3.sh\n\n" >>"/home/ubuntu/.bash_profile" +printf "\nif [ -f /home/ubuntu/.bashrc ]; then\n\t. /home/ubuntu/.bashrc\nfi " >>"/home/ubuntu/.bash_profile" +printf 'export PATH=/usr/local/src/bowtie2-2.2.9:$PATH' >>"/home/ubuntu/.bashrc" +chown ubuntu:ubuntu /home/ubuntu/.bashrc +chown ubuntu:ubuntu /home/ubuntu/.bash_profile +exit 0 diff --git a/scripts/bootstrap-scripts/bootstrap_ubuntu.sh b/scripts/bootstrap-scripts/bootstrap_ubuntu.sh new file mode 100644 index 0000000..b4174d3 --- /dev/null +++ b/scripts/bootstrap-scripts/bootstrap_ubuntu.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# This script bootstraps a workspace instance by preparing S3 study data to be +# mounted via the mount_s3.sh environment script. +# Note that mounting cannot be performed during initial bootstrapping +# because the instance's role will not yet have access to S3 study +# data since the associated resource policies aren't updated until after the +# CFN stack has been completed created. +S3_MOUNTS="$1" + +# Exit if no S3 mounts were specified +[ -z "$S3_MOUNTS" ] || [ "$S3_MOUNTS" = "[]" ] && exit 0 + +# Get directory in which this script is stored and define URL from which to download goofys +FILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +GOOFYS_URL="https://github.com/kahing/goofys/releases/download/v0.21.0/goofys" + +# Install dependencies +apt-get install jq=1.6-1ubuntu0.20.04.1 +curl -LSs -o "/usr/local/bin/goofys" "$GOOFYS_URL" +chmod +x "/usr/local/bin/goofys" + +# Install ec2 instance connect agent +apt-get install ec2-instance-connect=1.1.12+dfsg1-0ubuntu3.20.04.1 + +# Create S3 mount script and config file +chmod +x "${FILES_DIR}/bin/mount_s3.sh" +ln -s "${FILES_DIR}/bin/mount_s3.sh" "/usr/local/bin/mount_s3.sh" +printf "%s" "$S3_MOUNTS" >"/usr/local/etc/s3-mounts.json" +apt-get install fuse=2.9.9-3 +printf "\n# Mount S3 study data\nmount_s3.sh\n\n" >>"/home/ubuntu/.bash_profile" +printf "\nif [ -f /home/ubuntu/.bashrc ]; then\n\t. /home/ubuntu/.bashrc\nfi " >>"/home/ubuntu/.bash_profile" +chown ubuntu:ubuntu /home/ubuntu/.bashrc +chown ubuntu:ubuntu /home/ubuntu/.bash_profile +exit 0 diff --git a/scripts/bootstrap-scripts/get_bootstrap.sh b/scripts/bootstrap-scripts/get_bootstrap.sh index 34183fe..6679bca 100644 --- a/scripts/bootstrap-scripts/get_bootstrap.sh +++ b/scripts/bootstrap-scripts/get_bootstrap.sh @@ -7,7 +7,7 @@ INSTALL_DIR="/usr/local/share/workspace-environment" # Download instance files and execute bootstrap script sudo mkdir "$INSTALL_DIR" -sudo aws s3 sync "$bootstrap_s3_location" "$INSTALL_DIR" +sudo aws s3 sync "$bootstrap_s3_location" "$INSTALL_DIR" --region us-east-2 bootstrap_script="$INSTALL_DIR/bootstrap.sh" if [ -s "$bootstrap_script" ] diff --git a/scripts/bootstrap-scripts/get_bootstrap_chenlab.sh b/scripts/bootstrap-scripts/get_bootstrap_chenlab.sh new file mode 100644 index 0000000..7a6abef --- /dev/null +++ b/scripts/bootstrap-scripts/get_bootstrap_chenlab.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +bootstrap_s3_location="$1" +s3_mounts="$2" +rstudio_user="$3" + +INSTALL_DIR="/usr/local/share/workspace-environment" + +# Download instance files and execute bootstrap script +sudo mkdir "$INSTALL_DIR" +sudo aws s3 sync "$bootstrap_s3_location" "$INSTALL_DIR" + +bootstrap_script="$INSTALL_DIR/bootstrap_chenlab.sh" +if [ -s "$bootstrap_script" ]; then + sudo chmod 500 "$bootstrap_script" + sudo "$bootstrap_script" "$s3_mounts" "$rstudio_user" +fi + +exit 0 + diff --git a/scripts/bootstrap-scripts/get_bootstrap_mysql.sh b/scripts/bootstrap-scripts/get_bootstrap_mysql.sh index 7be0e74..b136354 100644 --- a/scripts/bootstrap-scripts/get_bootstrap_mysql.sh +++ b/scripts/bootstrap-scripts/get_bootstrap_mysql.sh @@ -17,3 +17,4 @@ then fi exit 0 + diff --git a/scripts/bootstrap-scripts/get_bootstrap_ubuntu.sh b/scripts/bootstrap-scripts/get_bootstrap_ubuntu.sh new file mode 100644 index 0000000..413ec2e --- /dev/null +++ b/scripts/bootstrap-scripts/get_bootstrap_ubuntu.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +bootstrap_s3_location="$1" +s3_mounts="$2" +rstudio_user="$3" + +INSTALL_DIR="/usr/local/share/workspace-environment" + +# Download instance files and execute bootstrap script +sudo mkdir "$INSTALL_DIR" +sudo aws s3 sync "$bootstrap_s3_location" "$INSTALL_DIR" + +bootstrap_script="$INSTALL_DIR/bootstrap_ubuntu.sh" +if [ -s "$bootstrap_script" ]; then + sudo chmod 500 "$bootstrap_script" + sudo "$bootstrap_script" "$s3_mounts" "$rstudio_user" +fi + +exit 0 diff --git a/scripts/bootstrap-scripts/offline-packages/goofys b/scripts/bootstrap-scripts/offline-packages/goofys new file mode 100644 index 0000000..5aca737 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/goofys differ diff --git a/scripts/bootstrap-scripts/offline-packages/goofys-0.24.0 b/scripts/bootstrap-scripts/offline-packages/goofys-0.24.0 new file mode 100644 index 0000000..d6d6aea Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/goofys-0.24.0 differ diff --git a/scripts/bootstrap-scripts/offline-packages/jq-1.5-linux64 b/scripts/bootstrap-scripts/offline-packages/jq-1.5-linux64 new file mode 100644 index 0000000..939227e Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/jq-1.5-linux64 differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/autostop.py b/scripts/bootstrap-scripts/offline-packages/sagemaker/autostop.py new file mode 100644 index 0000000..643375d --- /dev/null +++ b/scripts/bootstrap-scripts/offline-packages/sagemaker/autostop.py @@ -0,0 +1,125 @@ +# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# https://aws.amazon.com/apache-2-0/ +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +import requests +from datetime import datetime +import getopt, sys +import urllib3 +import boto3 +import json + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +# Usage +usageInfo = """Usage: +This scripts checks if a notebook is idle for X seconds if it does, it'll stop the notebook: +python autostop.py --time [--port ] [--ignore-connections] +Type "python autostop.py -h" for available options. +""" +# Help info +helpInfo = """-t, --time + Auto stop time in seconds +-p, --port + jupyter port +-c --ignore-connections + Stop notebook once idle, ignore connected users +-h, --help + Help information +""" + +# Read in command-line parameters +idle = True +port = '8443' +ignore_connections = False +try: + opts, args = getopt.getopt(sys.argv[1:], "ht:p:c", ["help","time=","port=","ignore-connections"]) + if len(opts) == 0: + raise getopt.GetoptError("No input parameters!") + for opt, arg in opts: + if opt in ("-h", "--help"): + print(helpInfo) + exit(0) + if opt in ("-t", "--time"): + time = int(arg) + if opt in ("-p", "--port"): + port = str(arg) + if opt in ("-c", "--ignore-connections"): + ignore_connections = True +except getopt.GetoptError: + print(usageInfo) + exit(1) + +# Missing configuration notification +missingConfiguration = False +if not time: + print("Missing '-t' or '--time'") + missingConfiguration = True +if missingConfiguration: + exit(2) + + +def is_idle(last_activity): + last_activity = datetime.strptime(last_activity,"%Y-%m-%dT%H:%M:%S.%fz") + if (datetime.now() - last_activity).total_seconds() > time: + print('Notebook is idle. Last activity time = ', last_activity) + return True + else: + print('Notebook is not idle. Last activity time = ', last_activity) + return False + + +def get_notebook_name(): + log_path = '/opt/ml/metadata/resource-metadata.json' + with open(log_path, 'r') as logs: + _logs = json.load(logs) + return _logs['ResourceName'] + +# This is hitting Jupyter's sessions API: https://github.com/jupyter/jupyter/wiki/Jupyter-Notebook-Server-API#Sessions-API +response = requests.get('https://localhost:'+port+'/api/sessions', verify=False) +data = response.json() +if len(data) > 0: + for notebook in data: + # Idleness is defined by Jupyter + # https://github.com/jupyter/notebook/issues/4634 + if notebook['kernel']['execution_state'] == 'idle': + if not ignore_connections: + if notebook['kernel']['connections'] == 0: + if not is_idle(notebook['kernel']['last_activity']): + idle = False + else: + idle = False + print('Notebook idle state set as %s because no kernel has been detected.' % idle) + else: + if not is_idle(notebook['kernel']['last_activity']): + idle = False + print('Notebook idle state set as %s since kernel connections are ignored.' % idle) + else: + print('Notebook is not idle:', notebook['kernel']['execution_state']) + idle = False +else: + client = boto3.client('sagemaker') + uptime = client.describe_notebook_instance( + NotebookInstanceName=get_notebook_name() + )['LastModifiedTime'] + if not is_idle(uptime.strftime("%Y-%m-%dT%H:%M:%S.%fz")): + idle = False + print('Notebook idle state set as %s since no sessions detected.' % idle) + +if idle: + print('Closing idle notebook') + client = boto3.client('sagemaker') + client.stop_notebook_instance( + NotebookInstanceName=get_notebook_name() + ) +else: + print('Notebook not idle. Pass.') \ No newline at end of file diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/boto3/python2-boto3-1.4.4-1.amzn2.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/boto3/python2-boto3-1.4.4-1.amzn2.noarch.rpm new file mode 100644 index 0000000..c926cb2 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/boto3/python2-boto3-1.4.4-1.amzn2.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/basesystem-10.0-4.9.amzn1.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/basesystem-10.0-4.9.amzn1.noarch.rpm new file mode 100644 index 0000000..5513ff5 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/basesystem-10.0-4.9.amzn1.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/bash-4.2.46-34.43.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/bash-4.2.46-34.43.amzn1.x86_64.rpm new file mode 100644 index 0000000..83a66a4 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/bash-4.2.46-34.43.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/filesystem-2.4.30-3.8.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/filesystem-2.4.30-3.8.amzn1.x86_64.rpm new file mode 100644 index 0000000..68e065b Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/filesystem-2.4.30-3.8.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/fuse-2.9.4-1.18.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/fuse-2.9.4-1.18.amzn1.x86_64.rpm new file mode 100644 index 0000000..6a2570e Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/fuse-2.9.4-1.18.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-2.17-292.180.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-2.17-292.180.amzn1.x86_64.rpm new file mode 100644 index 0000000..809f73b Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-2.17-292.180.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-common-2.17-292.180.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-common-2.17-292.180.amzn1.x86_64.rpm new file mode 100644 index 0000000..240aa2e Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/glibc-common-2.17-292.180.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/info-5.1-4.10.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/info-5.1-4.10.amzn1.x86_64.rpm new file mode 100644 index 0000000..f68fff4 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/info-5.1-4.10.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libgcc72-7.2.1-2.59.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libgcc72-7.2.1-2.59.amzn1.x86_64.rpm new file mode 100644 index 0000000..b6f6605 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libgcc72-7.2.1-2.59.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libselinux-2.1.10-3.22.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libselinux-2.1.10-3.22.amzn1.x86_64.rpm new file mode 100644 index 0000000..cc00579 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libselinux-2.1.10-3.22.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libsepol-2.1.7-3.12.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libsepol-2.1.7-3.12.amzn1.x86_64.rpm new file mode 100644 index 0000000..149ee7a Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/libsepol-2.1.7-3.12.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-base-5.7-4.20090207.14.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-base-5.7-4.20090207.14.amzn1.x86_64.rpm new file mode 100644 index 0000000..5d6c820 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-base-5.7-4.20090207.14.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-libs-5.7-4.20090207.14.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-libs-5.7-4.20090207.14.amzn1.x86_64.rpm new file mode 100644 index 0000000..98a37cb Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/ncurses-libs-5.7-4.20090207.14.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nspr-4.21.0-1.43.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nspr-4.21.0-1.43.amzn1.x86_64.rpm new file mode 100644 index 0000000..0908d8f Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nspr-4.21.0-1.43.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-softokn-freebl-3.44.0-8.44.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-softokn-freebl-3.44.0-8.44.amzn1.x86_64.rpm new file mode 100644 index 0000000..775de48 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-softokn-freebl-3.44.0-8.44.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-util-3.44.0-4.56.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-util-3.44.0-4.56.amzn1.x86_64.rpm new file mode 100644 index 0000000..0319d40 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/nss-util-3.44.0-4.56.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/setup-2.8.14-20.12.amzn1.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/setup-2.8.14-20.12.amzn1.noarch.rpm new file mode 100644 index 0000000..674d89c Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/setup-2.8.14-20.12.amzn1.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/tzdata-2020d-2.76.amzn1.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/tzdata-2020d-2.76.amzn1.noarch.rpm new file mode 100644 index 0000000..07b80ee Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/tzdata-2020d-2.76.amzn1.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/which-2.19-6.10.amzn1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/which-2.19-6.10.amzn1.x86_64.rpm new file mode 100644 index 0000000..e485239 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4/which-2.19-6.10.amzn1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/basesystem-10.0-7.amzn2.0.1.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/basesystem-10.0-7.amzn2.0.1.noarch.rpm new file mode 100644 index 0000000..ccd782b Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/basesystem-10.0-7.amzn2.0.1.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/bash-4.2.46-34.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/bash-4.2.46-34.amzn2.x86_64.rpm new file mode 100644 index 0000000..ea240d3 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/bash-4.2.46-34.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/filesystem-3.2-25.amzn2.0.4.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/filesystem-3.2-25.amzn2.0.4.x86_64.rpm new file mode 100644 index 0000000..7c679ad Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/filesystem-3.2-25.amzn2.0.4.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/fuse-2.9.2-11.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/fuse-2.9.2-11.amzn2.x86_64.rpm new file mode 100644 index 0000000..e206033 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/fuse-2.9.2-11.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-2.26-58.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-2.26-58.amzn2.x86_64.rpm new file mode 100644 index 0000000..d3e7e1b Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-2.26-58.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-common-2.26-58.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-common-2.26-58.amzn2.x86_64.rpm new file mode 100644 index 0000000..84b1252 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/glibc-common-2.26-58.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/info-5.1-5.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/info-5.1-5.amzn2.x86_64.rpm new file mode 100644 index 0000000..dd6c637 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/info-5.1-5.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libgcc-7.3.1-14.amzn2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libgcc-7.3.1-14.amzn2.x86_64.rpm new file mode 100644 index 0000000..2b38a61 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libgcc-7.3.1-14.amzn2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libselinux-2.5-12.amzn2.0.2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libselinux-2.5-12.amzn2.0.2.x86_64.rpm new file mode 100644 index 0000000..7a5d057 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libselinux-2.5-12.amzn2.0.2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libsepol-2.5-8.1.amzn2.0.2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libsepol-2.5-8.1.amzn2.0.2.x86_64.rpm new file mode 100644 index 0000000..dd1f70b Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/libsepol-2.5-8.1.amzn2.0.2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nspr-4.32.0-1.amzn2.0.1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nspr-4.32.0-1.amzn2.0.1.x86_64.rpm new file mode 100644 index 0000000..4bd55a7 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nspr-4.32.0-1.amzn2.0.1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-softokn-freebl-3.67.0-3.amzn2.0.1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-softokn-freebl-3.67.0-3.amzn2.0.1.x86_64.rpm new file mode 100644 index 0000000..30526ff Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-softokn-freebl-3.67.0-3.amzn2.0.1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-util-3.67.0-1.amzn2.0.1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-util-3.67.0-1.amzn2.0.1.x86_64.rpm new file mode 100644 index 0000000..52939a2 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/nss-util-3.67.0-1.amzn2.0.1.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/setup-2.8.71-10.amzn2.0.1.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/setup-2.8.71-10.amzn2.0.1.noarch.rpm new file mode 100644 index 0000000..9e8d6d8 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/setup-2.8.71-10.amzn2.0.1.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/tzdata-2022a-1.amzn2.noarch.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/tzdata-2022a-1.amzn2.noarch.rpm new file mode 100644 index 0000000..a9267a9 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/tzdata-2022a-1.amzn2.noarch.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/which-2.20-7.amzn2.0.2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/which-2.20-7.amzn2.0.2.x86_64.rpm new file mode 100644 index 0000000..a8c03dd Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/which-2.20-7.amzn2.0.2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-17.amzn2.0.2.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-17.amzn2.0.2.x86_64.rpm new file mode 100644 index 0000000..40aaa39 Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-17.amzn2.0.2.x86_64.rpm differ diff --git a/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-19.amzn2.0.1.x86_64.rpm b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-19.amzn2.0.1.x86_64.rpm new file mode 100644 index 0000000..dd5a4dc Binary files /dev/null and b/scripts/bootstrap-scripts/offline-packages/sagemaker/fuse-2.9.4_AL2/zlib-1.2.7-19.amzn2.0.1.x86_64.rpm differ