diff --git a/do-common.sh b/do-common.sh new file mode 100755 index 0000000..347056f --- /dev/null +++ b/do-common.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Sets the correct environment variables +# And then calls the corresponding s3-{method} script + +readonly DO_SCRIPT_NAME="$(basename $0)" + +METHOD=$(echo $DO_SCRIPT_NAME | cut -c4-) + +case $METHOD in + get ) s3-get $@ + ;; + put ) s3-put @? + ;; +esac \ No newline at end of file diff --git a/do-get b/do-get new file mode 120000 index 0000000..372976c --- /dev/null +++ b/do-get @@ -0,0 +1 @@ +./do-common.sh \ No newline at end of file diff --git a/s3-common.sh b/s3-common.sh new file mode 100755 index 0000000..b4e530f --- /dev/null +++ b/s3-common.sh @@ -0,0 +1,301 @@ +#!/usr/bin/env bash +# +# Common functions for s3-bash4 commands +# (c) 2015 Chi Vinh Le + +# Constants +readonly VERSION="0.0.1" + +# Exit codes +readonly INVALID_USAGE_EXIT_CODE=1 +readonly INVALID_USER_DATA_EXIT_CODE=2 +readonly INVALID_ENVIRONMENT_EXIT_CODE=3 + +## +# Write error to stderr +# Arguments: +# $1 string to output +## +err() { + echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')] Error: $@" >&2 +} + + +## +# Display version and exit +## +showVersionAndExit() { + printf "$VERSION\n" + exit +} + +## +# Helper for parsing the command line. +## +assertArgument() { + if [[ $# -lt 2 ]]; then + err "Option $1 needs an argument." + exit $INVALID_USAGE_EXIT_CODE + fi +} + +## +# Asserts given resource path +# Arguments: +# $1 string resource path +## +assertResourcePath() { + if [[ $1 = !(/*) ]]; then + err "Resource should start with / e.g. /bucket/file.ext" + exit $INVALID_USAGE_EXIT_CODE + fi +} + +## +# Asserts given file exists. +# Arguments: +# $1 string file path +## +assertFileExists() { + if [[ ! -f $1 ]]; then + err "$1 file doesn't exists" + exit $INVALID_USER_DATA_EXIT_CODE + fi +} + +## +# Check for valid environment. Exit if invalid. +## +checkEnvironment() +{ + programs=(openssl curl printf echo sed awk od date shasum pwd dirname) + for program in "${programs[@]}"; do + if [ ! -x "$(which $program)" ]; then + err "$program is required to run" + exit $INVALID_ENVIRONMENT_EXIT_CODE + fi + done +} + +## +# Reads, validates and return aws secret stored in a file +# Arguments: +# $1 path to secret file +# Output: +# string AWS secret +## +processAWSSecretFile() { + local errStr="The Amazon AWS secret key must be 40 bytes long. Make sure that there is no carriage return at the end of line." + if ! [[ -f $1 ]]; then + err "The file $1 does not exist." + exit $INVALID_USER_DATA_EXIT_CODE + fi + + # limit file size to max 41 characters. 40 + potential null terminating character. + local fileSize="$(ls -l "$1" | awk '{ print $5 }')" + if [[ $fileSize -gt 41 ]]; then + err $errStr + exit $INVALID_USER_DATA_EXIT_CODE + fi + + secret=$(<$1) + # exact string size should be 40. + if [[ ${#secret} != 40 ]]; then + err $errStr + exit $INVALID_USER_DATA_EXIT_CODE + fi + echo $secret +} + +## +# Convert string to hex with max line size of 256 +# Arguments: +# $1 string to convert +# Returns: +# string hex +## +hex256() { + printf "$1" | od -A n -t x1 | sed ':a;N;$!ba;s/[\n ]//g' +} + +## +# Calculate sha256 hash +# Arguments: +# $1 string to hash +# Returns: +# string hash +## +sha256Hash() { + local output=$(printf "$1" | shasum -a 256) + echo "${output%% *}" +} + +## +# Calculate sha256 hash of file +# Arguments: +# $1 file path +# Returns: +# string hash +## +sha256HashFile() { + local output=$(shasum -a 256 $1) + echo "${output%% *}" +} + +## +# Generate HMAC signature using SHA256 +# Arguments: +# $1 signing key in hex +# $2 string data to sign +# Returns: +# string signature +## +hmac_sha256() { + printf "$2" | openssl dgst -binary -hex -sha256 -mac HMAC -macopt hexkey:$1 \ + | sed 's/^.* //' +} + +## +# Sign data using AWS Signature Version 4 +# Arguments: +# $1 AWS Secret Access Key +# $2 yyyymmdd +# $3 AWS Region +# $4 AWS Service +# $5 string data to sign +# Returns: +# signature +## +sign() { + local kSigning=$(hmac_sha256 $(hmac_sha256 $(hmac_sha256 \ + $(hmac_sha256 $(hex256 "AWS4$1") $2) $3) $4) "aws4_request") + hmac_sha256 "${kSigning}" "$5" +} + +## +# Get endpoint of specified region +# Arguments: +# $1 region +# Returns: +# amazon andpoint +## +convS3RegionToEndpoint() { + case "$1" in + us-east-1) echo "s3.amazonaws.com" + ;; + *) echo ${1}.digitaloceanspaces.com + ;; + esac +} + +## +# Perform request to S3 +# Uses the following Globals: +# METHOD string +# AWS_ACCESS_KEY_ID string +# AWS_SECRET_ACCESS_KEY string +# AWS_REGION string +# RESOURCE_PATH string +# FILE_TO_UPLOAD string +# CONTENT_TYPE string +# PUBLISH bool +# DEBUG bool +# VERBOSE bool +# INSECURE bool +## +performRequest() { + local timestamp=$(date -u "+%Y-%m-%d %H:%M:%S") + local isoTimestamp=$(date -ud "${timestamp}" "+%Y%m%dT%H%M%SZ") + local dateScope=$(date -ud "${timestamp}" "+%Y%m%d") + local host=$(convS3RegionToEndpoint "${AWS_REGION}") + + # Generate payload hash + if [[ $METHOD == "PUT" ]]; then + local payloadHash=$(sha256HashFile $FILE_TO_UPLOAD) + else + local payloadHash=$(sha256Hash "") + fi + + local cmd=("curl") + local headers= + local headerList= + + cmd+=("--silent") + if [[ ${DEBUG} != true ]]; then + cmd+=("--fail") + fi + + if [[ ${VERBOSE} == true ]]; then + cmd+=("--verbose") + fi + + if [[ ${METHOD} == "PUT" ]]; then + cmd+=("-T" "${FILE_TO_UPLOAD}") + fi + + if [[ ${METHOD} == "HEAD" ]]; then + cmd+=("--head") + else + cmd+=("-X" "${METHOD}") + fi + + + if [[ ${METHOD} == "PUT" && ! -z "${CONTENT_TYPE}" ]]; then + cmd+=("-H" "Content-Type: ${CONTENT_TYPE}") + headers+="content-type:${CONTENT_TYPE}\n" + headerList+="content-type;" + fi + + cmd+=("-H" "Host: ${host}") + headers+="host:${host}\n" + headerList+="host;" + + if [[ ${METHOD} == "PUT" && "${PUBLISH}" == true ]]; then + cmd+=("-H" "x-amz-acl: public-read") + headers+="x-amz-acl:public-read\n" + headerList+="x-amz-acl;" + fi + + cmd+=("-H" "x-amz-content-sha256: ${payloadHash}") + headers+="x-amz-content-sha256:${payloadHash}\n" + headerList+="x-amz-content-sha256;" + + cmd+=("-H" "x-amz-date: ${isoTimestamp}") + headers+="x-amz-date:${isoTimestamp}" + headerList+="x-amz-date" + + # Generate canonical request + local canonicalRequest="${METHOD} +${RESOURCE_PATH} + +${headers} + +${headerList} +${payloadHash}" + + # Generated request hash + local hashedRequest=$(sha256Hash "${canonicalRequest}") + + # Generate signing data + local stringToSign="AWS4-HMAC-SHA256 +${isoTimestamp} +${dateScope}/${AWS_REGION}/s3/aws4_request +${hashedRequest}" + + # Sign data + local signature=$(sign "${AWS_SECRET_ACCESS_KEY}" "${dateScope}" "${AWS_REGION}" \ + "s3" "${stringToSign}") + + local authorizationHeader="AWS4-HMAC-SHA256 Credential=${AWS_ACCESS_KEY_ID}/${dateScope}/${AWS_REGION}/s3/aws4_request, SignedHeaders=${headerList}, Signature=${signature}" + cmd+=("-H" "Authorization: ${authorizationHeader}") + + local protocol="https" + # if [[ $INSECURE == false ]]; then + # protocol="http" + # fi + cmd+=("${protocol}://${host}${RESOURCE_PATH}") + + # Curl + "${cmd[@]}" +} + diff --git a/s3-delete b/s3-delete new file mode 100755 index 0000000..d60bcff --- /dev/null +++ b/s3-delete @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# +# Delete a file from S3 +# (c) 2015 Chi Vinh Le +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +set -euo pipefail + +readonly PROJECT_PATH=$(dirname $0) +readonly SCRIPT_NAME="$(basename $0)" +readonly METHOD="DELETE" + +# Includes +source ${PROJECT_PATH}/s3-common.sh + +## +# Print help and exit +# Arguments: +# $1 int exit code +# Output: +# string help +## +printUsageAndExitWith() { + printf "Usage:\n" + printf " ${SCRIPT_NAME} [-vi] [-k key] [-s file] [-r region] resource_path\n" + printf " ${SCRIPT_NAME} -h\n" + printf "Example:\n" + printf " ${SCRIPT_NAME} -k key -s secret -r eu-central-1 /bucket/file.ext\n" + printf "Options:\n" + printf " --debug\tEnable debugging mode\n" + printf " -h,--help\tPrint this help\n" + printf " -i,--insecure\tUse http instead of https\n" + printf " -k,--key\tAWS Access Key ID. Default to environment variable AWS_ACCESS_KEY_ID\n" + printf " -r,--region\tAWS S3 Region. Default to environment variable AWS_DEFAULT_REGION\n" + printf " -s,--secret\tFile containing AWS Secret Access Key. If not set, secret will be environment variable AWS_SECRET_ACCESS_KEY\n" + printf " -v,--verbose\tVerbose output\n" + printf " --version\tShow version\n" + exit $1 +} + +## +# Parse command line and set global variables +# Arguments: +# $@ command line +# Globals: +# AWS_ACCESS_KEY_ID string +# AWS_SECRET_ACCESS_KEY string +# AWS_REGION string +# RESOURCE_PATH string +# VERBOSE bool +# INSECURE bool +# DEBUG bool +## +parseCommandLine() { + # Init globals + AWS_REGION=${AWS_DEFAULT_REGION:-""} + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""} + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""} + VERBOSE=false + INSECURE=false + DEBUG=false + + # Parse options + local remaining= + local secretKeyFile= + while [[ $# > 0 ]]; do + local key="$1" + case ${key} in + --version) showVersionAndExit;; + --debug) DEBUG=true;; + -h|--help) printUsageAndExitWith 0;; + -v|--verbose) VERBOSE=true;; + -i|--insecure) INSECURE=true;; + -r|--region) assertArgument $@; AWS_REGION=$2; shift;; + -k|--key) assertArgument $@; AWS_ACCESS_KEY_ID=$2; shift;; + -s|--secret) assertArgument $@; secretKeyFile=$2; shift;; + -*) err "Unknown option $1" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE};; + *) remaining="${remaining} \"${key}\"";; + esac + shift + done + + # Set the non-parameters back into the positional parameters ($1 $2 ..) + eval set -- ${remaining} + + # Read secret file if set + if ! [[ -z "${secretKeyFile}" ]]; then + AWS_SECRET_ACCESS_KEY=$(processAWSSecretFile "${secretKeyFile}") + fi + + # Parse arguments + if [[ $# != 1 ]]; then + err "You need to specify the resource path to download e.g. /bucket/file.ext" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + assertResourcePath "$1" + RESOURCE_PATH="$1" + + if [[ -z "${AWS_REGION}" ]]; then + err "AWS Region not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then + err "AWS Access Key ID not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_SECRET_ACCESS_KEY}" ]]; then + err "AWS Secret Access Key not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + # Freeze globals + readonly AWS_REGION + readonly AWS_ACCESS_KEY_ID + readonly AWS_SECRET_ACCESS_KEY + readonly RESOURCE_PATH + readonly DEBUG + readonly VERBOSE + readonly INSECURE +} + +## +# Main routine +## +main() { + checkEnvironment + parseCommandLine $@ + performRequest +} + +main $@ diff --git a/s3-get b/s3-get new file mode 100755 index 0000000..626df7c --- /dev/null +++ b/s3-get @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# +# Download a file from S3 +# (c) 2015 Chi Vinh Le +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +set -euo pipefail + +readonly PROJECT_PATH=$(dirname $0) +readonly SCRIPT_NAME="$(basename $0)" +readonly METHOD="GET" + +# Includes +source ${PROJECT_PATH}/s3-common.sh + +## +# Print help and exit +# Arguments: +# $1 int exit code +# Output: +# string help +## +printUsageAndExitWith() { + printf "Usage:\n" + printf " ${SCRIPT_NAME} [-vi] [-k key] [-s file] [-r region] resource_path\n" + printf " ${SCRIPT_NAME} -h\n" + printf "Example:\n" + printf " ${SCRIPT_NAME} -k key -s secret -r eu-central-1 /bucket/file.ext\n" + printf "Options:\n" + printf " --debug\tEnable debugging mode\n" + printf " -h,--help\tPrint this help\n" + printf " -i,--insecure\tUse http instead of https\n" + printf " -k,--key\tAWS Access Key ID. Default to environment variable AWS_ACCESS_KEY_ID\n" + printf " -r,--region\tAWS S3 Region. Default to environment variable AWS_DEFAULT_REGION\n" + printf " -s,--secret\tFile containing AWS Secret Access Key. If not set, secret will be environment variable AWS_SECRET_ACCESS_KEY\n" + printf " -v,--verbose\tVerbose output\n" + printf " --version\tShow version\n" + exit $1 +} + +## +# Parse command line and set global variables +# Arguments: +# $@ command line +# Globals: +# AWS_ACCESS_KEY_ID string +# AWS_SECRET_ACCESS_KEY string +# AWS_REGION string +# RESOURCE_PATH string +# VERBOSE bool +# INSECURE bool +# DEBUG bool +## +parseCommandLine() { + # Init globals + AWS_REGION=${AWS_DEFAULT_REGION:-""} + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""} + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""} + VERBOSE=false + INSECURE=false + DEBUG=false + + # Parse options + local remaining= + local secretKeyFile= + while [[ $# > 0 ]]; do + local key="$1" + case ${key} in + --version) showVersionAndExit;; + --debug) DEBUG=true;; + -h|--help) printUsageAndExitWith 0;; + -v|--verbose) VERBOSE=true;; + -i|--insecure) INSECURE=true;; + -r|--region) assertArgument $@; AWS_REGION=$2; shift;; + -k|--key) assertArgument $@; AWS_ACCESS_KEY_ID=$2; shift;; + -s|--secret) assertArgument $@; secretKeyFile=$2; shift;; + -*) err "Unknown option $1" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE};; + *) remaining="${remaining} \"${key}\"";; + esac + shift + done + + # Set the non-parameters back into the positional parameters ($1 $2 ..) + eval set -- ${remaining} + + # Read secret file if set + if ! [[ -z "${secretKeyFile}" ]]; then + AWS_SECRET_ACCESS_KEY=$(processAWSSecretFile "${secretKeyFile}") + fi + + # Parse arguments + if [[ $# != 1 ]]; then + err "You need to specify the resource path to download e.g. /bucket/file.ext" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + assertResourcePath "$1" + RESOURCE_PATH="$1" + + if [[ -z "${AWS_REGION}" ]]; then + err "AWS Region not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then + err "AWS Access Key ID not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_SECRET_ACCESS_KEY}" ]]; then + err "AWS Secret Access Key not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + # Freeze globals + readonly AWS_REGION + readonly AWS_ACCESS_KEY_ID + readonly AWS_SECRET_ACCESS_KEY + readonly RESOURCE_PATH + readonly DEBUG + readonly VERBOSE + readonly INSECURE +} + +## +# Main routine +## +main() { + checkEnvironment + parseCommandLine $@ + performRequest +} + +main $@ diff --git a/s3-head b/s3-head new file mode 100755 index 0000000..7733053 --- /dev/null +++ b/s3-head @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# +# Download a file from S3 +# (c) 2015 Chi Vinh Le +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +set -euo pipefail + +readonly PROJECT_PATH=$(dirname $0) +readonly SCRIPT_NAME="$(basename $0)" +readonly METHOD="HEAD" + +# Includes +source ${PROJECT_PATH}/s3-common.sh + +## +# Print help and exit +# Arguments: +# $1 int exit code +# Output: +# string help +## +printUsageAndExitWith() { + printf "Usage:\n" + printf " ${SCRIPT_NAME} [-vi] [-k key] [-s file] [-r region] resource_path\n" + printf " ${SCRIPT_NAME} -h\n" + printf "Example:\n" + printf " ${SCRIPT_NAME} -k key -s secret -r eu-central-1 /bucket/file.ext\n" + printf "Options:\n" + printf " --debug\tEnable debugging mode\n" + printf " -h,--help\tPrint this help\n" + printf " -i,--insecure\tUse http instead of https\n" + printf " -k,--key\tAWS Access Key ID. Default to environment variable AWS_ACCESS_KEY_ID\n" + printf " -r,--region\tAWS S3 Region. Default to environment variable AWS_DEFAULT_REGION\n" + printf " -s,--secret\tFile containing AWS Secret Access Key. If not set, secret will be environment variable AWS_SECRET_ACCESS_KEY\n" + printf " -v,--verbose\tVerbose output\n" + printf " --version\tShow version\n" + exit $1 +} + +## +# Parse command line and set global variables +# Arguments: +# $@ command line +# Globals: +# AWS_ACCESS_KEY_ID string +# AWS_SECRET_ACCESS_KEY string +# AWS_REGION string +# RESOURCE_PATH string +# VERBOSE bool +# INSECURE bool +# DEBUG bool +## +parseCommandLine() { + # Init globals + AWS_REGION=${AWS_DEFAULT_REGION:-""} + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""} + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""} + VERBOSE=false + INSECURE=false + DEBUG=false + + # Parse options + local remaining= + local secretKeyFile= + while [[ $# > 0 ]]; do + local key="$1" + case ${key} in + --version) showVersionAndExit;; + --debug) DEBUG=true;; + -h|--help) printUsageAndExitWith 0;; + -v|--verbose) VERBOSE=true;; + -i|--insecure) INSECURE=true;; + -r|--region) assertArgument $@; AWS_REGION=$2; shift;; + -k|--key) assertArgument $@; AWS_ACCESS_KEY_ID=$2; shift;; + -s|--secret) assertArgument $@; secretKeyFile=$2; shift;; + -*) err "Unknown option $1" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE};; + *) remaining="${remaining} \"${key}\"";; + esac + shift + done + + # Set the non-parameters back into the positional parameters ($1 $2 ..) + eval set -- ${remaining} + + # Read secret file if set + if ! [[ -z "${secretKeyFile}" ]]; then + AWS_SECRET_ACCESS_KEY=$(processAWSSecretFile "${secretKeyFile}") + fi + + # Parse arguments + if [[ $# != 1 ]]; then + err "You need to specify the resource path to download e.g. /bucket/file.ext" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + assertResourcePath "$1" + RESOURCE_PATH="$1" + + if [[ -z "${AWS_REGION}" ]]; then + err "AWS Region not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then + err "AWS Access Key ID not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_SECRET_ACCESS_KEY}" ]]; then + err "AWS Secret Access Key not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + # Freeze globals + readonly AWS_REGION + readonly AWS_ACCESS_KEY_ID + readonly AWS_SECRET_ACCESS_KEY + readonly RESOURCE_PATH + readonly DEBUG + readonly VERBOSE + readonly INSECURE +} + +## +# Main routine +## +main() { + checkEnvironment + parseCommandLine $@ + performRequest +} + +main $@ diff --git a/s3-put b/s3-put new file mode 100755 index 0000000..29c73ff --- /dev/null +++ b/s3-put @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +# +# Upload a file to S3 +# (c) 2015 Chi Vinh Le +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. + +set -euo pipefail + +readonly PROJECT_PATH=$(dirname $0) +readonly SCRIPT_NAME="$(basename $0)" +readonly METHOD="PUT" + +# Includes +source ${PROJECT_PATH}/s3-common.sh + +## +# Print help and exit +# Arguments: +# $1 int exit code +# Output: +# string help +## +printUsageAndExitWith() { + printf "Usage:\n" + printf " ${SCRIPT_NAME} [--debug] [-vip] [-k key] [-r region] [-s file] [-c content_type] -T file_to_upload resource_path\n" + printf " ${SCRIPT_NAME} -h\n" + printf "Example:\n" + printf " ${SCRIPT_NAME} -k key -s secret -r eu-central-1 -T file.ext -c text/plain /bucket/file.ext\n" + printf "Options:\n" + printf " -c,--content-type\tMIME content type\n" + printf " --debug\tEnable debugging mode\n" + printf " -h,--help\tPrint this help\n" + printf " -i,--insecure\tUse http instead of https\n" + printf " -k,--key\tAWS Access Key ID. Default to environment variable AWS_ACCESS_KEY_ID\n" + printf " -p,--public\tGrant public read on uploaded file\n" + printf " -r,--region\tAWS S3 Region. Default to environment variable AWS_DEFAULT_REGION\n" + printf " -s,--secret\tFile containing AWS Secret Access Key. If not set, secret will be environment variable AWS_SECRET_ACCESS_KEY\n" + printf " -T,--upload-file\tPath to file to upload\n" + printf " -v,--verbose\tVerbose output\n" + printf " --version\tShow version\n" + + exit $1 +} + +## +# Parse command line and set global variables +# Arguments: +# $@ command line +# Sets the following Globals: +# AWS_ACCESS_KEY_ID string +# AWS_SECRET_ACCESS_KEY string +# AWS_REGION string +# RESOURCE_PATH string +# FILE_TO_UPLOAD string +# CONTENT_TYPE string +# PUBLISH bool +# VERBOSE bool +# INSECURE bool +# DEBUG bool +# PUBLISH bool +## +parseCommandLine() { + # Init globals + AWS_REGION=${AWS_DEFAULT_REGION:-""} + AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""} + AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""} + FILE_TO_UPLOAD= + CONTENT_TYPE= + PUBLISH=false + VERBOSE=false + INSECURE=false + DEBUG=false + + # Parse options + local remaining= + local secretKeyFile= + while [[ $# > 0 ]]; do + local key="$1" + case ${key} in + --version) showVersionAndExit;; + --debug) DEBUG=true;; + -h|--help) printUsageAndExitWith 0;; + -v|--verbose) VERBOSE=true;; + -i|--insecure) INSECURE=true;; + -p|--publish) PUBLISH=true;; + -c|--content-type) assertArgument $@; CONTENT_TYPE=$2; shift;; + -T|--upload-file) assertArgument $@; FILE_TO_UPLOAD=$2; shift;; + -r|--region) assertArgument $@; AWS_REGION=$2; shift;; + -k|--key) assertArgument $@; AWS_ACCESS_KEY_ID=$2; shift;; + -s|--secret) assertArgument $@; secretKeyFile=$2; shift;; + -*) err "Unknown option $1" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE};; + *) remaining="${remaining} \"${key}\"";; + esac + shift + done + + # Set the non-parameters back into the positional parameters ($1 $2 ..) + eval set -- ${remaining} + + # Read secret file if set + if ! [[ -z "${secretKeyFile}" ]]; then + AWS_SECRET_ACCESS_KEY=$(processAWSSecretFile "${secretKeyFile}") + fi + + # Parse arguments + if [[ $# != 1 ]]; then + err "You need to specify the resource path to upload to e.g. /bucket/file.ext" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + assertResourcePath $1 + RESOURCE_PATH="$1" + + if [[ -z "${FILE_TO_UPLOAD}" ]]; then + err "You need to specify the file to upload using -T" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + assertFileExists "${FILE_TO_UPLOAD}" + + if [[ -z "${AWS_REGION}" ]]; then + err "AWS Region not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then + err "AWS Access Key ID not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + if [[ -z "${AWS_SECRET_ACCESS_KEY}" ]]; then + err "AWS Secret Access Key not specified" + printUsageAndExitWith ${INVALID_USAGE_EXIT_CODE} + fi + + # Freeze globals + readonly AWS_REGION + readonly AWS_ACCESS_KEY_ID + readonly AWS_SECRET_ACCESS_KEY + readonly RESOURCE_PATH + readonly CONTENT_TYPE + readonly PUBLISH + readonly DEBUG + readonly VERBOSE + readonly INSECURE +} + +## +# Main routine +## +main() { + checkEnvironment + parseCommandLine $@ + performRequest +} + +main $@