CloudWatch error in bash script - bash

I am creating a simple bash script that would routinely check mdadm and report back to CloudWatch with numeric values:
#!/bin/bash
## CHECKING RAID STATUS AND SUBMITTING RESULTS TO CLOUDWATCH ##
## 0 = Good, 1 = Bad, 2 = Still bad, needs investigating ##
HECK_RAID=`mdadm -D /dev/md1 | grep "State : [a-zA-Z]"`
SEND_RESPONSE=`aws cloudwatch put-metric-data --metric-name RAID-STATUS --namespace MONGODB --value "$STATUS" --dimensions InstanceID="$INSTANCEID" --region us-east-1`
INSTANCEID=$(wget -q -O - http://169.254.169.254/latest/meta-data/instance-id)
if [[ $CHECK_RAID =~ [Cc]lean ]]; then
STATUS=0
$SEND_RESPONSE
elif [[ $CHECK_RAID =~ [Ff]ailed ]]; then
STATUS=1
$SEND_RESPONSE
else
STATUS=2
$SEND_RESPONSE
fi
Problem is, when I run the script to test, I get this:
Invalid value ('') for param element of list:MetricData of type list
I know STATUS might be a culprit but is there a better way of creating the conditions I have made and actually submitting the results back to CloudWatch?

You are setting the variable "HECK_RAID" instead of "CHECK_RAID".

Related

Makefile: Interpolate variable uuid in command

I want to write a command so it runs using a randomly generated uuid each time, interpolating this variable into the command.
I'm in MacOS, so I can use the uuidgen command
❯ uuidgen
E4BC7525-1EC7-4338-8F4F-8DA268CB7051
I want to interpolate this value as part of my command like that:
send-test-sqs-message: ## Send test sqs message to generate an email
aws sqs send-message "{\"identifier\":$(uuidgen), ..." --output table | cat
But the output when running make send-test-sqs-message shows that "identifier" is empty.
Running the command with set -x provides this extra output which doesn't look relevant:
+_p9k_preexec1:1> _p9k_restore_special_params +_p9k_restore_special_params:1> (( ! 0 )) +_p9k_restore_special_params:7> (( ! 0 )) +_p9k_restore_special_params:11> (( ! 0 )) +_p9k_preexec1:2> unset __p9k_trapint +_p9k_preexec1:3> trap - INT +omz_termsupport_preexec:1> [[ '' != true ]]
How can this be fixed?

cant suppress or use an AWS-CLI error in bash

i have a simple script that i want to display specific information from AWS using
AWS CLI.
for example:
get_cluster_name() {
EKS_NAME=$(aws eks describe-cluster --name ${CUSTOMER_NAME}) && \
echo $EKS_NAME | jq -r .cluster.name}
the output when the cluster exist is ok, i get the name of the cluster.
when the cluster does not exist, i get:
An error occurred (ResourceNotFoundException) when calling the DescribeCluster operation: No cluster found for name: example_cluster.
my goal is to get an empty output when a cluster is not found.
for that i wanted to use the return code in a condition or a string lookup in the output.
the issue is that the output is not stdout or stderr, therefor
i cant even direct it to /dev/null just to silence the error.
how can i make this code work properly?:
[[ $(get_cluster_name) =~ "ResourceNotFoundException" ]] && echo "EKS Cluster:....$(get_cluster_name)"
or
[[ $(get_cluster_name) ]] && echo "EKS Cluster:....$(get_cluster_name)"
Thank you.
Here's a consideration, and expansion on my comments. Again you're getting a stderr response when no cluster is found, so this makes this pretty straightforward.
Using >2 /dev/null to suppress that return message in stderr. Then using ret=$? to capture the return code.
get_cluster_name() {
EKS_NAME=$(aws eks describe-cluster --name ${CUSTOMER_NAME} 2> /dev/null);
ret=$?
if [ $ret -eq 0 ]; then
echo $EKS_NAME | jq -r .cluster.name
return 0
else
return $ret
fi
}
You can do the same thing now when you call the function as your error will propagate from aws command up the stack:
cluster=$(get_cluster_name)
ret=$?
if [ $ret -eq 0 ]; then
echo $cluster
else
echo "failed to find cluster. Error code: $ret"
fi
As an example.

AWS EKS CLI command - if/else not working

I'm trying to do a basic if/else that checks to see if a cluster already exists before creating another of that name. This is the entire section;
cluster=$(aws eks list-clusters | jq -r ".clusters" | grep mycluster_test)
if [ $? -eq 0 ]; then
echo "this worked, the cluster is $cluster"
else
echo "no cluster by this name"
fi
There is no cluster with this name, and when I run the script it returns nothing. But I don't understand why it's not returning the else statement
I do have a cluster called 'mycluster_new' and when I grep for this, the first echo statement is returned, so can I please get help on why the else statement is failing. Thanks
try this
if your vairabe is string
if [[ $cluster == 1 ]]; then
if your variable is integer
if [[ $cluster -eq 1 ]]; then
Managed to resolve this by checking if the string was empty and ran a check that would continue regardless of if it was empty or not.
CLUSTER=my_eks_cluster
CHECK_NAME=$(aws eks list-clusters | jq -r ".clusters" | grep $CLUSTER || true)
then ran a check for this;
if [ "$CHECK_NAME" != "" ]; then
echo "There is already a cluster by this name; $CHECK_NAME. Cannot build another"
exit 1
else
echo "No cluster by this name $CLUSTER, will continue with terraform"
fi
if you really want to go ahead with your old way, you can always use '-z' to check if the string is null
if [ -z "$cluster" ]; then
echo "this worked, the cluster is $cluster"
else
echo "no cluster by this name"
fi

How do I use one series of arguments vs another series when calling script?

newish programmer here. This is my first bash script...
This is a script to backup EBS volumes on AWS. I can currently call the script with the backup argument. Or I can call it with the delete argument followed by a numerical argument (i.e. 3), which deletes any snapshots with the 'backup-script' description that is as old or older than the number of days passed as the numerical argument passed after delete.
Unfortunately, no matter whether I call backup or delete, either one is the first argument and goes to the ACTION variable, and whatever my second argument is it goes to the AGE variable.
Proof:
~ bash -x backup backup vol-e5bf623b
+ ACTION=backup
+ AGE=vol-e5bf623b
+ VOLUME=
+ '[' -z backup ']'
+ '[' backup = delete ']'
+ '[' backup = backup ']'
+ '[' -z ']'
+ echo 'Please provide a volume id that you would like to backup'
Please provide a volume id that you would like to backup
+ exit 3
What's not obvious to me, is how to do it differently so that when I call script_name backup vol-f00bar that vol-id is passed to a variable (like VOLUME) and then passed to my backup_ebs function.
I am going this route to run the script with different cron tasks to handle rotation policy of backups.
#!/bin/bash
ACTION=$1
AGE=$2
VOLUME=$3
# When calling script, if an action is not called as an argument, exit with message
#
if [ -z $ACTION ];
then
echo "Usage $0: Define ACTION of backup or delete"
exit 1
fi
# When calling script with delete argument, if no interval is provided, exit with message
#
if [ $ACTION = "delete" ] && [ -z $AGE ];
then
echo "Please enter the age of backups you would like to delete"
exit 2
fi
# When calling script with backup argument, if no volume id is provided, exit with message
#
if [ $ACTION = "backup" ] && [ -z $VOLUME ];
then
echo "Please provide a volume id that you would like to backup"
exit 3
fi
# Creates snapshot of volume and tag it with 'backup-script' description for tracking
#
function backup_ebs () {
echo "Creating backup"
aws ec2 create-snapshot --volume-id $VOLUME --description "backup-script"
}
function delete_snapshots () {
# Parses all snapshot ID's
for snapshot in $(aws ec2 describe-snapshots --filters Name=description,Values=backup-script | egrep "(SnapshotId)" | grep -o "snap\-\w\{8\}")
do
# Parses dates in "YYYY/MM/DD" format from snapshot ID's
snapshotdate=$(aws ec2 describe-snapshots --filters Name=description,Values=backup-script | egrep "(StartTime)" | grep -o "\d\{4\}\-\d\{2\}\-\d\{2\}")
startdate=$(date +"%Y-%m-%d")
enddate=$(date -d $snapshotdate +"%Y-%m-%d")
# Calculates the days since snapshot was created
interval=$[ (startdate - enddate) / (60*60*24) ]
if (( $interval >= $AGE ));
then
aws ec2 delete-snapshot --snapshot-id $snapshot
fi
done
}
# Decides which function to call based on what action argument is provided when script is called
#
case $ACTION in
"backup")
backup_ebs
;;
"delete")
delete_snapshots
;;
esac
Simple answer: do your assignment within an if or a case statement:
case "$1" in
backup) VOLUME="$2";;
delete) AGE="$2"; VOLUME="$3";;
esac
However, I would also avoid using global variables. So, I'd suggest passing those variables to your functions:
backup_ebs "$VOLUME"
and
delete_snapshots "$AGE"
This will ease maintaining that script
And if you prefer compact solution just exploit conditional assignment:
ACION="$1"
VOLUME="${3:-$2}"
AGE="$2"
Given that you can view backup and delete+3 as examples of a single variable-length argument, it might be better to make VOLUME first argument, since it is fixed for all commands.
bash -x backup vol-e5bf623b backup
bash -x backup vol-e5bf623b delete 3
Then in your script
VOLUME=$1
ACTION=$2
AGE=$3
Being able to make this kind of change easily is one of the benefits of naming the arguments, rather than using $1 et al. sprinkled throughout your code.
The positional arguments are set by position. The first argument is $1 the second is $2 and so on. If you want $2 to be for delete and $3 to be for backup then you need to use backup backup '' vol-e5bf623b when you run it (to pass an empty second argument).
That said, don't do that.
Just use the second argument as the argument for both. Either by giving it a generic name or by assigning it to an appropriately named variable after checking the mode.

Possible spacing issue in a bash script. Command will not run in script but will when copied from output

I've gone around and around on the quoting stuff on http://tldp.org for bash and googled until I am blue in the face. I've also tried every obvious quoting scheme for this issue, and yet nothing works.
The problem seems to be that a space inside of a quoted argument in the command run at the end of the script is being interpreted as a separator instead of as a quoted space.
Behold, here's my script (I know full well I'm a noob so comments on my style and/or uneccessary syntax is cool with me, I'll learn):
#!/bin/bash
date=`date`
args="$#"
MSEND_HOME=/home/patrol/Impact #Path to the Impact Directory
integrationName=Introscope #Name of the integration
persistEnabled=1 #1 for Yes, 0 for No
persist=""
bufDir=$MSEND_HOME/tmp/$integrationName #DO NOT CHANGE
cellName=linuxtest #Cell name to forward events to
loggingEnabled=1 #1 for Yes, 0 for No
logFile=$MSEND_HOME/log/$integrationName.$cellName.log
die () {
if [ $loggingEnabled -eq 1 ]
then
echo >>$logFile "$#"
fi
exit 1
}
[ "$#" -ge 1 ] || die "$date - At least 1 argument required, $# provided" "$#"
# This is where you would parse out your arguments and form the following
# slots as a minimum for sending an event.
class=$2
msg=\"$3\"
# Parse the first argument and assign the correct syntax
if [[ $1 == "INFORMATIONAL" ]]
then
severity=INFO
elif [[ $1 == "WARN" ]]
then
severity=WARNING
elif [[ $1 == "CRIT" ]]
then
severity=CRITICAL
else
severity=INFO
fi
#Additional slots can be set, parse them all in this variable;
#e.g., additionalSlots="slot1=value1;slot2=value2;slot3=\"value 3\""
additionalSlots=""
cmd="$MSEND_HOME/bin/msend"
cmd="$cmd -q"
cmd="$cmd -l $MSEND_HOME"
if [ $persistEnabled -eq 1 ]
then
cmd="$cmd -j $bufDir"
fi
cmd="$cmd -n $cellName"
cmd="$cmd -a $class"
cmd="$cmd -m $msg"
cmd="$cmd -r $severity"
if [ $additionalSlots ]
then
cmd="$cmd -b $additionalSlots"
fi
$cmd || die "$date - msend exited with error $? | Original arguments: $args | Command: $cmd"
#echo "msend exited with error $? | Original arguments: $args | Command: $cmd"
The script is executed like this:
./sendEvent.sh "CRIT" "EVENT" "Test Event"
The error I get from the msend executable is that the arguments are wrong, but I'm logging the command line in it's entirety to a file and when I run that logged command in the shell interactively, it works.
Here's the log output:
Tue Oct 4 20:31:29 CDT 2011 - msend exited with error 27 | Original arguments: CRIT EVENT Test Event | Command: /home/patrol/Impact/bin/msend -q -l /home/patrol/Impact -j /home/patrol/Impact/tmp/Introscope -n linuxtest -a EVENT -m "Test Event" -r CRITICAL
So if I paste /home/patrol/Impact/bin/msend -q -l /home/patrol/Impact -j /home/patrol/Impact/tmp/Introscope -n linuxtest -a EVENT -m "Test Event" -r CRITICAL and run it, it works.
If I run the script like ./sendEvent.sh "CRIT" "EVENT" "TestEvent" it works. But I need that argument to allow spaces.
I'm on the track that it's an $IFS issue or something... maybe a difference between the interactive shell and the script environment.
I'd appreciate any insight from smarter people than me!
tl;dr - My command doesn't work when run from within a script, but does when the logged command syntax is used in an interactive shell.
Short answer: see BashFAQ #50.
Long answer: When bash parses a line, it parses quote marks before doing variable substitution; as a result, when you put quotes inside a variable, they don't do what you'd expect. You're actually passing an argument list including '-m' '"Test' 'Event"' '-r' -- those double-quotes aren't around the arguments, they're in the arguments.
In this case, the best solution is to build the command in an array rather than a string. Also, get in the habbit of putting double-quotes around variables (e.g. filenames) when you use them, to prevent confusion if they contain spaces. With those changes (and a few other tweaks), here's my version of your script:
#!/bin/bash
date="$(date)" # Backquotes are confusing, use $() instead
args=("$#") # Save the args in an array rather than mushing them together in a string
MSEND_HOME=/home/patrol/Impact #Path to the Impact Directory
MSEND_HOME="$HOME/tmp" #Path to the Impact Directory
integrationName=Introscope #Name of the integration
persistEnabled=1 #1 for Yes, 0 for No
persist=""
bufDir="$MSEND_HOME/tmp/$integrationName" #DO NOT CHANGE
cellName=linuxtest #Cell name to forward events to
loggingEnabled=1 #1 for Yes, 0 for No
logFile="$MSEND_HOME/log/$integrationName.$cellName.log"
die () {
if [ $loggingEnabled -eq 1 ]
then
echo >>"$logFile" "$#"
fi
exit 1
}
[ "$#" -ge 1 ] || die "$date - At least 1 argument required, $# provided" "$#"
# This is where you would parse out your arguments and form the following
# slots as a minimum for sending an event.
class="$2" # Quotes not strictly needed here, but a good habbit
msg="$3"
# Parse the first argument and assign the correct syntax
if [[ "$1" == "INFORMATIONAL" ]]
then
severity=INFO
elif [[ "$1" == "WARN" ]]
then
severity=WARNING
elif [[ "$1" == "CRIT" ]]
then
severity=CRITICAL
else
severity=INFO
fi
#Additional slots can be set, parse them all in this array;
#e.g., additionalSlots="slot1=value1;slot2=value2;slot3=value 3" # Don't embed quotes
additionalSlots=""
cmd=("$MSEND_HOME/bin/msend") # Build the command as an array, not a string
cmd+=(-q) # Could equivalently use cmd=("${cmd[#]}" -q), but this is simpler
cmd+=(-l "$MSEND_HOME")
if [ $persistEnabled -eq 1 ]
then
cmd+=(-j "$bufDir")
fi
cmd+=(-n "$cellName")
cmd+=(-a "$class") # Possible bug: $2 and #3 aren't required, but they're getting added unconditionally
cmd+=(-m "$msg") # These should probably be conditional, like additionalSlots
cmd+=(-r "$severity")
if [ -n "$additionalSlots" ]
then
cmd+=(-b "$additionalSlots")
fi
"${cmd[#]}" || die "$date - msend exited with error $? | Original arguments:$(printf " %q" "${args[#]}") | Command:$(printf " %q" "${cmd[#]}")"
#echo "msend exited with error $? | Original arguments:$(printf " %q" "${args[#]}") | Command:$(printf " %q" "${cmd[#]}")"
I think the arg goes wrong with this assignment: cmd="$cmd -m $msg".
Change it to cmd="$cmd -m \"$msg\"".
Okay, I don't see the exact problem immediately, but I can tell you what it is; this hint should help.
Remember that the shell quoting mechanism only interprets a string once. As a result, if you're not careful, what you thought was "foo" "a" "b" is in fact "foo a b" -- that is, all one token, not three.
Run the script with bash -x which will show you at each step what the shell is actually seeing.

Resources