Repository: aws-cloudformation/rain
Pull request #209 (2023-11-12 20:56:16)

added log filters to filter the log output
*Issue #192 *

*Description of changes:*
Added two flags to rain log command `--length` or `-l` for limiting the length of logs and `--days` or `-d` for limiting logs output by number of days

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.



Patch:
diff --git a/docs/bash_completion.sh b/docs/bash_completion.sh
index 3eca4413..a2b86519 100644
--- a/docs/bash_completion.sh
+++ b/docs/bash_completion.sh
@@ -49,9 +49,10 @@ __rain_handle_go_custom_completion()
     local out requestComp lastParam lastChar comp directive args
 
     # Prepare the command to request completions for the program.
-    # Calling ${words[0]} instead of directly rain allows to handle aliases
+    # Calling ${words[0]} instead of directly rain allows handling aliases
     args=("${words[@]:1}")
-    requestComp="${words[0]} __completeNoDesc ${args[*]}"
+    # Disable ActiveHelp which is not supported for bash completion v1
+    requestComp="RAIN_ACTIVE_HELP=0 ${words[0]} __completeNoDesc ${args[*]}"
 
     lastParam=${words[$((${#words[@]}-1))]}
     lastChar=${lastParam:$((${#lastParam}-1)):1}
@@ -77,7 +78,7 @@ __rain_handle_go_custom_completion()
         directive=0
     fi
     __rain_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
-    __rain_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
+    __rain_debug "${FUNCNAME[0]}: the completions are: ${out}"
 
     if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
         # Error code.  No completion.
@@ -103,7 +104,7 @@ __rain_handle_go_custom_completion()
         local fullFilter filter filteringCmd
         # Do not use quotes around the $out variable or else newline
         # characters will be kept.
-        for filter in ${out[*]}; do
+        for filter in ${out}; do
             fullFilter+="$filter|"
         done
 
@@ -114,7 +115,7 @@ __rain_handle_go_custom_completion()
         # File completion for directories only
         local subdir
         # Use printf to strip any trailing newline
-        subdir=$(printf "%s" "${out[0]}")
+        subdir=$(printf "%s" "${out}")
         if [ -n "$subdir" ]; then
             __rain_debug "Listing directories in $subdir"
             __rain_handle_subdirs_in_dir_flag "$subdir"
@@ -125,7 +126,7 @@ __rain_handle_go_custom_completion()
     else
         while IFS='' read -r comp; do
             COMPREPLY+=("$comp")
-        done < <(compgen -W "${out[*]}" -- "$cur")
+        done < <(compgen -W "${out}" -- "$cur")
     fi
 }
 
@@ -665,6 +666,8 @@ _rain_fmt()
     flags_with_completion=()
     flags_completion=()
 
+    flags+=("--debug")
+    local_nonpersistent_flags+=("--debug")
     flags+=("--help")
     flags+=("-h")
     local_nonpersistent_flags+=("--help")
@@ -685,7 +688,6 @@ _rain_fmt()
     flags+=("-w")
     local_nonpersistent_flags+=("--write")
     local_nonpersistent_flags+=("-w")
-    flags+=("--debug")
     flags+=("--no-colour")
 
     must_have_one_flag=()
@@ -713,6 +715,8 @@ _rain_forecast()
     local_nonpersistent_flags+=("--config")
     local_nonpersistent_flags+=("--config=")
     local_nonpersistent_flags+=("-c")
+    flags+=("--debug")
+    local_nonpersistent_flags+=("--debug")
     flags+=("--experimental")
     flags+=("-x")
     local_nonpersistent_flags+=("--experimental")
@@ -751,7 +755,6 @@ _rain_forecast()
     two_word_flags+=("--type")
     local_nonpersistent_flags+=("--type")
     local_nonpersistent_flags+=("--type=")
-    flags+=("--debug")
     flags+=("--no-colour")
 
     must_have_one_flag=()
@@ -846,10 +849,24 @@ _rain_logs()
     flags+=("-c")
     local_nonpersistent_flags+=("--chart")
     local_nonpersistent_flags+=("-c")
+    flags+=("--days=")
+    two_word_flags+=("--days")
+    two_word_flags+=("-d")
+    local_nonpersistent_flags+=("--days")
+    local_nonpersistent_flags+=("--days=")
+    local_nonpersistent_flags+=("-d")
+    flags+=("--debug")
+    local_nonpersistent_flags+=("--debug")
     flags+=("--help")
     flags+=("-h")
     local_nonpersistent_flags+=("--help")
     local_nonpersistent_flags+=("-h")
+    flags+=("--length=")
+    two_word_flags+=("--length")
+    two_word_flags+=("-l")
+    local_nonpersistent_flags+=("--length")
+    local_nonpersistent_flags+=("--length=")
+    local_nonpersistent_flags+=("-l")
     flags+=("--profile=")
     two_word_flags+=("--profile")
     two_word_flags+=("-p")
@@ -862,7 +879,6 @@ _rain_logs()
     local_nonpersistent_flags+=("--region")
     local_nonpersistent_flags+=("--region=")
     local_nonpersistent_flags+=("-r")
-    flags+=("--debug")
     flags+=("--no-colour")
 
     must_have_one_flag=()
@@ -962,6 +978,8 @@ _rain_pkg()
     flags_with_completion=()
     flags_completion=()
 
+    flags+=("--debug")
+    local_nonpersistent_flags+=("--debug")
     flags+=("--experimental")
     flags+=("-x")
     local_nonpersistent_flags+=("--experimental")
@@ -996,7 +1014,6 @@ _rain_pkg()
     two_word_flags+=("--s3-prefix")
     local_nonpersistent_flags+=("--s3-prefix")
     local_nonpersistent_flags+=("--s3-prefix=")
-    flags+=("--debug")
     flags+=("--no-colour")
 
     must_have_one_flag=()
@@ -1265,6 +1282,7 @@ _rain_stackset()
     flags+=("-h")
     local_nonpersistent_flags+=("--help")
     local_nonpersistent_flags+=("-h")
+    flags+=("--no-colour")
     flags+=("--profile=")
     two_word_flags+=("--profile")
     two_word_flags+=("-p")
@@ -1278,7 +1296,6 @@ _rain_stackset()
     local_nonpersistent_flags+=("--region=")
     local_nonpersistent_flags+=("-r")
     flags+=("--debug")
-    flags+=("--no-colour")
 
     must_have_one_flag=()
     must_have_one_noun=()
diff --git a/docs/index.md b/docs/index.md
index 9fff7fff..604c3779 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -34,4 +34,4 @@ Rain is a command line tool for working with AWS CloudFormation templates and st
 * [rain tree](rain_tree.md)	 - Find dependencies of Resources and Outputs in a local template
 * [rain watch](rain_watch.md)	 - Display an updating view of a CloudFormation stack
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_bootstrap.md b/docs/rain_bootstrap.md
index 022cd7bf..c56b353a 100644
--- a/docs/rain_bootstrap.md
+++ b/docs/rain_bootstrap.md
@@ -30,4 +30,4 @@ rain bootstrap
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_build.md b/docs/rain_build.md
index e688e7c1..63e5abb3 100644
--- a/docs/rain_build.md
+++ b/docs/rain_build.md
@@ -30,4 +30,4 @@ rain build [<resource type>...]
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_cat.md b/docs/rain_cat.md
index 489aa763..c0f813fd 100644
--- a/docs/rain_cat.md
+++ b/docs/rain_cat.md
@@ -31,4 +31,4 @@ rain cat <stack>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_console.md b/docs/rain_console.md
index f1de15b6..78deb920 100644
--- a/docs/rain_console.md
+++ b/docs/rain_console.md
@@ -38,4 +38,4 @@ rain console [stack]
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_deploy.md b/docs/rain_deploy.md
index f576c25e..c6d56802 100644
--- a/docs/rain_deploy.md
+++ b/docs/rain_deploy.md
@@ -70,4 +70,4 @@ rain deploy <template> [stack]
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_diff.md b/docs/rain_diff.md
index 54afe2ec..f918363e 100644
--- a/docs/rain_diff.md
+++ b/docs/rain_diff.md
@@ -28,4 +28,4 @@ rain diff <from> <to>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_fmt.md b/docs/rain_fmt.md
index 6d6f0c28..67a618fd 100644
--- a/docs/rain_fmt.md
+++ b/docs/rain_fmt.md
@@ -13,6 +13,7 @@ rain fmt <filename>...
 ### Options
 
 ```
+      --debug      Output debugging information
   -h, --help       help for fmt
   -j, --json       Output the template as JSON (default format: YAML).
   -u, --unsorted   Do not sort the template's properties.
@@ -24,7 +25,6 @@ rain fmt <filename>...
 ### Options inherited from parent commands
 
 ```
-      --debug       Output debugging information
       --no-colour   Disable colour output
 ```
 
@@ -32,4 +32,4 @@ rain fmt <filename>...
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_forecast.md b/docs/rain_forecast.md
index 0131c79c..9543f574 100644
--- a/docs/rain_forecast.md
+++ b/docs/rain_forecast.md
@@ -37,6 +37,7 @@ rain forecast --experimental <template> [stackName]
 
 ```
   -c, --config string     YAML or JSON file to set tags and parameters
+      --debug             Output debugging information
   -x, --experimental      Acknowledge that this is an experimental feature
   -h, --help              help for forecast
       --params strings    set parameter values; use the format key1=value1,key2=value2
@@ -51,7 +52,6 @@ rain forecast --experimental <template> [stackName]
 ### Options inherited from parent commands
 
 ```
-      --debug       Output debugging information
       --no-colour   Disable colour output
 ```
 
@@ -59,4 +59,4 @@ rain forecast --experimental <template> [stackName]
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_info.md b/docs/rain_info.md
index 13688f45..acb8539a 100644
--- a/docs/rain_info.md
+++ b/docs/rain_info.md
@@ -30,4 +30,4 @@ rain info
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_logs.md b/docs/rain_logs.md
index 7f12dd4b..a362ca08 100644
--- a/docs/rain_logs.md
+++ b/docs/rain_logs.md
@@ -18,7 +18,10 @@ rain logs <stack> (<resource>)
 ```
   -a, --all              include uninteresting logs
   -c, --chart            Output a gantt chart of the most recent action as an html file
+  -d, --days uint        Age of the logs to display in days
+      --debug            Output debugging information
   -h, --help             help for logs
+  -l, --length uint      Number of logs to display
   -p, --profile string   AWS profile name; read from the AWS CLI configuration file
   -r, --region string    AWS region to use
 ```
@@ -26,7 +29,6 @@ rain logs <stack> (<resource>)
 ### Options inherited from parent commands
 
 ```
-      --debug       Output debugging information
       --no-colour   Disable colour output
 ```
 
@@ -34,4 +36,4 @@ rain logs <stack> (<resource>)
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_ls.md b/docs/rain_ls.md
index 85d3db99..72032ca0 100644
--- a/docs/rain_ls.md
+++ b/docs/rain_ls.md
@@ -30,4 +30,4 @@ rain ls <stack>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_merge.md b/docs/rain_merge.md
index 5625a1ca..c2ba87f7 100644
--- a/docs/rain_merge.md
+++ b/docs/rain_merge.md
@@ -29,4 +29,4 @@ rain merge <template> <template> ...
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_pkg.md b/docs/rain_pkg.md
index 45f897db..dc1a3cb9 100644
--- a/docs/rain_pkg.md
+++ b/docs/rain_pkg.md
@@ -46,6 +46,7 @@ rain pkg <template>
 ### Options
 
 ```
+      --debug              Output debugging information
   -x, --experimental       Enable experimental features
   -h, --help               help for pkg
   -o, --output string      Output packaged template to a file
@@ -58,7 +59,6 @@ rain pkg <template>
 ### Options inherited from parent commands
 
 ```
-      --debug       Output debugging information
       --no-colour   Disable colour output
 ```
 
@@ -66,4 +66,4 @@ rain pkg <template>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_rm.md b/docs/rain_rm.md
index 360ed257..548fd10f 100644
--- a/docs/rain_rm.md
+++ b/docs/rain_rm.md
@@ -32,4 +32,4 @@ rain rm <stack>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_stackset.md b/docs/rain_stackset.md
index 96c8da6d..bd28082d 100644
--- a/docs/rain_stackset.md
+++ b/docs/rain_stackset.md
@@ -10,6 +10,7 @@ This command manipulates stack sets. It has no action if specific stack set comm
 
 ```
   -h, --help             help for stackset
+      --no-colour        Disable colour output
   -p, --profile string   AWS profile name; read from the AWS CLI configuration file
   -r, --region string    AWS region to use
 ```
@@ -17,8 +18,7 @@ This command manipulates stack sets. It has no action if specific stack set comm
 ### Options inherited from parent commands
 
 ```
-      --debug       Output debugging information
-      --no-colour   Disable colour output
+      --debug   Output debugging information
 ```
 
 ### SEE ALSO
@@ -28,4 +28,4 @@ This command manipulates stack sets. It has no action if specific stack set comm
 * [rain stackset ls](rain_stackset_ls.md)	 - List a CloudFormation stack sets in a given region
 * [rain stackset rm](rain_stackset_rm.md)	 - Delete a CloudFormation stack set and/or its instances.
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_stackset_deploy.md b/docs/rain_stackset_deploy.md
index 55556054..366648d7 100644
--- a/docs/rain_stackset_deploy.md
+++ b/docs/rain_stackset_deploy.md
@@ -64,4 +64,4 @@ rain stackset deploy <template> [stackset] [flags]
 
 * [rain stackset](rain_stackset.md)	 - This command manipulates stack sets.
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_stackset_ls.md b/docs/rain_stackset_ls.md
index 28ec9e75..684cd89d 100644
--- a/docs/rain_stackset_ls.md
+++ b/docs/rain_stackset_ls.md
@@ -30,4 +30,4 @@ rain stackset ls <stack set>
 
 * [rain stackset](rain_stackset.md)	 - This command manipulates stack sets.
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_stackset_rm.md b/docs/rain_stackset_rm.md
index ed6afb4f..ba665e8c 100644
--- a/docs/rain_stackset_rm.md
+++ b/docs/rain_stackset_rm.md
@@ -30,4 +30,4 @@ rain stackset rm <stackset>
 
 * [rain stackset](rain_stackset.md)	 - This command manipulates stack sets.
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_tree.md b/docs/rain_tree.md
index 158e7394..2a7cbbba 100644
--- a/docs/rain_tree.md
+++ b/docs/rain_tree.md
@@ -30,4 +30,4 @@ rain tree [template]
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/rain_watch.md b/docs/rain_watch.md
index 7a1a7447..6954c996 100644
--- a/docs/rain_watch.md
+++ b/docs/rain_watch.md
@@ -30,4 +30,4 @@ rain watch <stack>
 
 * [rain](index.md)	 - 
 
-###### Auto generated by spf13/cobra on 6-Sep-2023
+###### Auto generated by spf13/cobra on 15-Nov-2023
diff --git a/docs/zsh_completion.sh b/docs/zsh_completion.sh
index 514cdd20..51845d05 100644
--- a/docs/zsh_completion.sh
+++ b/docs/zsh_completion.sh
@@ -1,4 +1,5 @@
-#compdef _rain rain
+#compdef rain
+compdef _rain rain
 
 # zsh completion for rain                                 -*- shell-script -*-
 
@@ -17,8 +18,9 @@ _rain()
     local shellCompDirectiveNoFileComp=4
     local shellCompDirectiveFilterFileExt=8
     local shellCompDirectiveFilterDirs=16
+    local shellCompDirectiveKeepOrder=32
 
-    local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
+    local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
     local -a completions
 
     __rain_debug "\n========= starting completion logic =========="
@@ -86,7 +88,24 @@ _rain()
         return
     fi
 
+    local activeHelpMarker="_activeHelp_ "
+    local endIndex=${#activeHelpMarker}
+    local startIndex=$((${#activeHelpMarker}+1))
+    local hasActiveHelp=0
     while IFS='\n' read -r comp; do
+        # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
+        if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
+            __rain_debug "ActiveHelp found: $comp"
+            comp="${comp[$startIndex,-1]}"
+            if [ -n "$comp" ]; then
+                compadd -x "${comp}"
+                __rain_debug "ActiveHelp will need delimiter"
+                hasActiveHelp=1
+            fi
+
+            continue
+        fi
+
         if [ -n "$comp" ]; then
             # If requested, completions are returned with a description.
             # The description is preceded by a TAB character.
@@ -94,7 +113,7 @@ _rain()
             # We first need to escape any : as part of the completion itself.
             comp=${comp//:/\\:}
 
-            local tab=$(printf '\t')
+            local tab="$(printf '\t')"
             comp=${comp//$tab/:}
 
             __rain_debug "Adding completion: ${comp}"
@@ -103,11 +122,27 @@ _rain()
         fi
     done < <(printf "%s\n" "${out[@]}")
 
+    # Add a delimiter after the activeHelp statements, but only if:
+    # - there are completions following the activeHelp statements, or
+    # - file completion will be performed (so there will be choices after the activeHelp)
+    if [ $hasActiveHelp -eq 1 ]; then
+        if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
+            __rain_debug "Adding activeHelp delimiter"
+            compadd -x "--"
+            hasActiveHelp=0
+        fi
+    fi
+
     if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
         __rain_debug "Activating nospace."
         noSpace="-S ''"
     fi
 
+    if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
+        __rain_debug "Activating keep order."
+        keepOrder="-V"
+    fi
+
     if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
         # File extension filtering
         local filteringCmd
@@ -143,7 +178,7 @@ _rain()
         return $result
     else
         __rain_debug "Calling _describe"
-        if eval _describe "completions" completions $flagPrefix $noSpace; then
+        if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
             __rain_debug "_describe found some completions"
 
             # Return the success of having called _describe
diff --git a/go.mod b/go.mod
index e05a1b0d..8b295671 100644
--- a/go.mod
+++ b/go.mod
@@ -4,46 +4,49 @@ go 1.18
 
 require (
 	github.com/ake-persson/mapslice-json v0.0.0-20210720081907-22c8edf57807
-	github.com/aws/aws-sdk-go-v2 v1.17.3
-	github.com/aws/aws-sdk-go-v2/config v1.15.3
-	github.com/aws/aws-sdk-go-v2/credentials v1.11.2
-	github.com/aws/aws-sdk-go-v2/service/cloudformation v1.20.3
-	github.com/aws/aws-sdk-go-v2/service/ec2 v1.34.0
-	github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3
-	github.com/aws/aws-sdk-go-v2/service/sts v1.16.3
-	github.com/aws/smithy-go v1.13.5
-	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
-	github.com/google/go-cmp v0.5.9
-	github.com/google/uuid v1.3.0
-	github.com/gookit/color v1.5.0
+	github.com/aws/aws-sdk-go-v2 v1.22.2
+	github.com/aws/aws-sdk-go-v2/config v1.23.0
+	github.com/aws/aws-sdk-go-v2/credentials v1.15.2
+	github.com/aws/aws-sdk-go-v2/service/cloudformation v1.39.0
+	github.com/aws/aws-sdk-go-v2/service/ec2 v1.132.0
+	github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1
+	github.com/aws/aws-sdk-go-v2/service/sts v1.25.1
+	github.com/aws/smithy-go v1.16.0
+	github.com/chzyer/readline v1.5.1
+	github.com/google/go-cmp v0.6.0
+	github.com/google/uuid v1.4.0
+	github.com/gookit/color v1.5.4
 	github.com/nathan-fiscaletti/consolesize-go v0.0.0-20220204101620-317176b6684d
-	github.com/spf13/cobra v1.4.0
+	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
-	golang.org/x/sys v0.5.0
-	golang.org/x/term v0.5.0
-	gopkg.in/yaml.v3 v3.0.0
+	golang.org/x/sys v0.14.0
+	golang.org/x/term v0.14.0
+	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
-	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.11.0
-	github.com/aws/aws-sdk-go-v2/service/iam v1.19.0
-	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.11.3 // indirect
-	github.com/chzyer/logex v1.2.0 // indirect
-	github.com/chzyer/test v0.0.0-20210722231415-061457976a23 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
-	github.com/inconshreveable/mousetrap v1.0.0 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 // indirect
+)
+
+require (
+	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.6.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.14.2
+	github.com/aws/aws-sdk-go-v2/service/iam v1.27.1
+	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 // indirect
+	github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
-	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
-	golang.org/x/exp v0.0.0-20221230185412-738e83a70c30
+	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
+	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
 	gopkg.in/yaml.v2 v2.4.0
 )
diff --git a/go.sum b/go.sum
index 706b0d50..256f5fa5 100644
--- a/go.sum
+++ b/go.sum
@@ -1,72 +1,70 @@
 github.com/ake-persson/mapslice-json v0.0.0-20210720081907-22c8edf57807 h1:w3nrGk00TWs/4iZ3Q0k9c0vL0e/wRziArKU4e++d/nA=
 github.com/ake-persson/mapslice-json v0.0.0-20210720081907-22c8edf57807/go.mod h1:fGnnfniJiO/ajHAVHqMSUSL8sE9LmU9rzclCtoeB+y8=
-github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
-github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
-github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM=
-github.com/aws/aws-sdk-go-v2/config v1.15.3 h1:5AlQD0jhVXlGzwo+VORKiUuogkG7pQcLJNzIzK7eodw=
-github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg=
-github.com/aws/aws-sdk-go-v2/credentials v1.11.2 h1:RQQ5fzclAKJyY5TvF+fkjJEwzK4hnxQCLOu5JXzDmQo=
-github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 h1:LWPg5zjHV9oz/myQr4wMs0gi4CjnDN/ILmyZUFYXZsU=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 h1:by9P+oy3P/CwggN4ClnW2D4oL91QV7pBzBICi1chZvQ=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE=
-github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.11.0 h1:DBdVqh1110pD8FYJu7nASSS9O/4pvks2eNmqeflq+QA=
-github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.11.0/go.mod h1:KzvQs0zcugEyGER+yyZdANRZ+pMjDFSN9j8bNFhofGw=
-github.com/aws/aws-sdk-go-v2/service/cloudformation v1.20.3 h1:3tyryiV3iI1bfDAS63cVShKa7g4V/O9NnqVqEnDH59w=
-github.com/aws/aws-sdk-go-v2/service/cloudformation v1.20.3/go.mod h1:BJangPV5HOHGFMgaMssixK5C9+IUZ3VOfVFGNsdN/WQ=
-github.com/aws/aws-sdk-go-v2/service/ec2 v1.34.0 h1:dfWleW7/a3+TR6qJynYZsaovCEStQOep5x+BxkiBDhc=
-github.com/aws/aws-sdk-go-v2/service/ec2 v1.34.0/go.mod h1:37MWOQMGyj8lcranOwo716OHvJgeFJUOaWu6vk1pWNE=
-github.com/aws/aws-sdk-go-v2/service/iam v1.19.0 h1:9vCynoqC+dgxZKrsjvAniyIopsv3RZFsZ6wkQ+yxtj8=
-github.com/aws/aws-sdk-go-v2/service/iam v1.19.0/go.mod h1:OyAuvpFeSVNppcSsp1hFOVQcaTRc1LE24YIR7pMbbAA=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 h1:T4pFel53bkHjL2mMo+4DKE6r6AuoZnM0fg7k1/ratr4=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3 h1:I0dcwWitE752hVSMrsLCxqNQ+UdEp3nACx2bYNMQq+k=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3 h1:Gh1Gpyh01Yvn7ilO/b/hr01WgNpaszfbKMUgqM186xQ=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3 h1:BKjwCJPnANbkwQ8vzSbaZDKawwagDubrH/z/c0X+kbQ=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3 h1:rMPtwA7zzkSQZhhz9U3/SoIDz/NZ7Q+iRn4EIO8rSyU=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak=
-github.com/aws/aws-sdk-go-v2/service/sso v1.11.3 h1:frW4ikGcxfAEDfmQqWgMLp+F1n4nRo9sF39OcIb5BkQ=
-github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU=
-github.com/aws/aws-sdk-go-v2/service/sts v1.16.3 h1:cJGRyzCSVwZC7zZZ1xbx9m32UnrKydRYhOvcD1NYP9Q=
-github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8=
-github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
-github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
-github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
-github.com/chzyer/logex v1.2.0 h1:+eqR0HfOetur4tgnC8ftU5imRnhi4te+BadWS95c5AM=
-github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20210722231415-061457976a23 h1:dZ0/VyGgQdVGAss6Ju0dt5P0QltE0SFY5Woh6hbIfiQ=
-github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/aws/aws-sdk-go-v2 v1.22.2 h1:lV0U8fnhAnPz8YcdmZVV60+tr6CakHzqA6P8T46ExJI=
+github.com/aws/aws-sdk-go-v2 v1.22.2/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 h1:hHgLiIrTRtddC0AKcJr5s7i/hLgcpTt+q/FKxf1Zayk=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0/go.mod h1:w4I/v3NOWgD+qvs1NPEwhd++1h3XPHFaVxasfY6HlYQ=
+github.com/aws/aws-sdk-go-v2/config v1.23.0 h1:kqzEfGGDIrRJpfJckgwuZfFTbU9NB1jZnRcaO9MpOqE=
+github.com/aws/aws-sdk-go-v2/config v1.23.0/go.mod h1:p7wbxKXXjS1GGQOss7VXOazVMFF9bjUGq85/4wR/fSw=
+github.com/aws/aws-sdk-go-v2/credentials v1.15.2 h1:rKH7khRMxPdD0u3dHecd0Q7NOVw3EUe7AqdkUOkiOGI=
+github.com/aws/aws-sdk-go-v2/credentials v1.15.2/go.mod h1:tXM8wmaeAhfC7nZoCxb0FzM/aRaB1m1WQ7x0qlBLq80=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 h1:G5KawTAkyHH6WyKQCdHiW4h3PmAXNJpOgwKg3H7sDRE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3/go.mod h1:hugKmSFnZB+HgNI1sYGT14BUPZkO6alC/e0AWu+0IAQ=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 h1:AaQsr5vvGR7rmeSWBtTCcw16tT9r51mWijuCQhzLnq8=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2/go.mod h1:o1IiRn7CWocIFTXJjGKJDOwxv1ibL53NpcvcqGWyRBA=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 h1:UZx8SXZ0YtzRiALzYAWcjb9Y9hZUR7MBKaBQ5ouOjPs=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2/go.mod h1:ipuRpcSaklmxR6C39G187TpBAO132gUfleTGccUPs8c=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.6.0 h1:hwZB07/beLiCopuRKF0t+dEHmP39iN4YtDh3X5d3hrg=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.6.0/go.mod h1:rdAuXeHWhI/zkpYcO5n8WCpaIgY9MUxFyBsuqq3kjyA=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 h1:pyVrNAf7Hwz0u39dLKN5t+n0+K/3rMYKuiOoIum3AsU=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2/go.mod h1:mydrfOb9uiOYCxuCPR8YHQNQyGQwUQ7gPMZGBKbH8NY=
+github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.14.2 h1:wm8zm7z6Gl+5O4K5DbZAm1Q37W2SGX4OrP9VOVPuHvc=
+github.com/aws/aws-sdk-go-v2/service/cloudcontrol v1.14.2/go.mod h1:PsNbJnRfd+PDuV0zZ55KDBSdDSOb/SRcD3FFPFxXnUI=
+github.com/aws/aws-sdk-go-v2/service/cloudformation v1.39.0 h1:Inl237UhHY2vGmRUBZvi3NJ+pbFLcgrE0qKrDFoxS1k=
+github.com/aws/aws-sdk-go-v2/service/cloudformation v1.39.0/go.mod h1:31JYEhx6zQd8DUT6Bq0h292pEs+qJIl4fFxoLpVr7jY=
+github.com/aws/aws-sdk-go-v2/service/ec2 v1.132.0 h1:JCujezP7Ia2pN6a9WDaT6pS7w1c/uJvuztxdbzc3Fks=
+github.com/aws/aws-sdk-go-v2/service/ec2 v1.132.0/go.mod h1:NOPsghjhZRkrVvKIxrDrEL7zhVIFYJsHqdeol50Eodk=
+github.com/aws/aws-sdk-go-v2/service/iam v1.27.1 h1:rPkEOnwPOVop34lpAlA4Dv6x67Ys3moXkPDvBfjgSSo=
+github.com/aws/aws-sdk-go-v2/service/iam v1.27.1/go.mod h1:qdQ8NUrhmXE80S54w+LrtHUY+1Fp7cQSRZbJUZKrAcU=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0 h1:CJxo7ZBbaIzmXfV3hjcx36n9V87gJsIUPJflwqEHl3Q=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0/go.mod h1:yjVfjuY4nD1EW9i387Kau+I6V5cBA5YnC/mWNopjZrI=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2 h1:f2LhPofnjcdOQKRtumKjMvIHkfSQ8aH/rwKUDEQ/SB4=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2/go.mod h1:q+xX0H4OfuWDuBy7y/LDi4v8IBOWuF+vtp8Z6ex+lw4=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2 h1:h7j73yuAVVjic8pqswh+L/7r2IHP43QwRyOu6zcCDDE=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2/go.mod h1:H07AHdK5LSy8F7EJUQhoxyiCNkePoHj2D8P2yGTWafo=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 h1:gbIaOzpXixUpoPK+js/bCBK1QBDXM22SigsnzGZio0U=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2/go.mod h1:p+S7RNbdGN8qgHDSg2SCQJ9FeMAmvcETQiVpeGhYnNM=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1 h1:o6MCcX1rJW8Y3g+hvg2xpjF6JR6DftuYhfl3Nc1WV9Q=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1/go.mod h1:UDtxEWbREX6y4KREapT+jjtjoH0TiVSS6f5nfaY1UaM=
+github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 h1:km+ZNjtLtpXYf42RdaDZnNHm9s7SYAuDGTafy6nd89A=
+github.com/aws/aws-sdk-go-v2/service/sso v1.17.1/go.mod h1:aHBr3pvBSD5MbzOvQtYutyPLLRPbl/y9x86XyJJnUXQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 h1:iRFNqZH4a67IqPvK8xxtyQYnyrlsvwmpHOe9r55ggBA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1/go.mod h1:pTy5WM+6sNv2tB24JNKFtn6EvciQ5k40ZJ0pq/Iaxj0=
+github.com/aws/aws-sdk-go-v2/service/sts v1.25.1 h1:txgVXIXWPXyqdiVn92BV6a/rgtpX31HYdsOYj0sVQQQ=
+github.com/aws/aws-sdk-go-v2/service/sts v1.25.1/go.mod h1:VAiJiNaoP1L89STFlEMgmHX1bKixY+FaP+TpRFrmyZ4=
+github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik=
+github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
+github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
+github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
+github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
+github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
+github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
+github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
-github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
+github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
+github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
 github.com/gordonklaus/ineffassign v0.0.0-20210522101830-0589229737b2/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -78,23 +76,22 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
-github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
-github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
+github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 h1:m9O6OTJ627iFnN2JIWfdqlZCzneRO6EEBsHXI25P8ws=
-golang.org/x/exp v0.0.0-20221230185412-738e83a70c30/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -117,11 +114,12 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -139,6 +137,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/go.work.sum b/go.work.sum
new file mode 100644
index 00000000..0397fec9
--- /dev/null
+++ b/go.work.sum
@@ -0,0 +1,17 @@
+github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
+github.com/gordonklaus/ineffassign v0.0.0-20210522101830-0589229737b2 h1:hC4RAQwLzbDbHsa+CwwGBm1uG2oX9o3Frx9G73duPi8=
+github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
+github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
+golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
diff --git a/internal/aws/cfn/cfn.go b/internal/aws/cfn/cfn.go
index 1133df31..c9214bfe 100644
--- a/internal/aws/cfn/cfn.go
+++ b/internal/aws/cfn/cfn.go
@@ -282,7 +282,7 @@ func DeleteStackSetInstances(stackSetName string, accounts []string, regions []s
 	var input = &cloudformation.DeleteStackInstancesInput{
 		Accounts:     UniqueStrings(accounts),
 		Regions:      UniqueStrings(regions),
-		RetainStacks: retainStacks,
+		RetainStacks: &retainStacks,
 		StackSetName: &stackSetName,
 	}
 
diff --git a/internal/aws/cfn/mock.go b/internal/aws/cfn/mock.go
index 0a4cb70d..8f76daf2 100644
--- a/internal/aws/cfn/mock.go
+++ b/internal/aws/cfn/mock.go
@@ -151,21 +151,50 @@ func GetStackResource(stackName string, logicalId string) (*types.StackResourceD
 
 // GetStackEvents returns all events associated with the named stack
 func GetStackEvents(stackName string) ([]types.StackEvent, error) {
-	return []types.StackEvent{
-		{
-			EventId:              ptr.String("mock event id"),
-			StackId:              ptr.String(stackName),
-			StackName:            ptr.String(stackName),
-			Timestamp:            &now,
-			ClientRequestToken:   ptr.String("mock event token"),
-			LogicalResourceId:    ptr.String("MockResourceId"),
-			PhysicalResourceId:   ptr.String("MockPhysicalId"),
-			ResourceProperties:   ptr.String("mock resource properties"),
-			ResourceStatus:       types.ResourceStatusCreateInProgress,
-			ResourceStatusReason: ptr.String("mock status reason"),
-			ResourceType:         ptr.String("Mock::Resource::Type"),
-		},
-	}, nil
+
+	if stackName == "logsrange-test-mock-stack" {
+		templogTimeStamp := time.Now()
+		var logs []types.StackEvent
+		var mockLogLength int = 30
+		var logsPerDay int = 6
+		for i := 0; i < mockLogLength; i++ {
+			for i := 0; i < logsPerDay; i++ {
+				logEntry := types.StackEvent{
+					EventId:              ptr.String("mock event id"),
+					StackId:              ptr.String(stackName),
+					StackName:            ptr.String(stackName),
+					ClientRequestToken:   ptr.String("mock event token"),
+					LogicalResourceId:    ptr.String("MockResourceId"),
+					PhysicalResourceId:   ptr.String("MockPhysicalId"),
+					ResourceProperties:   ptr.String("mock resource properties"),
+					ResourceStatus:       types.ResourceStatusCreateInProgress,
+					ResourceStatusReason: ptr.String("mock status reason"),
+					ResourceType:         ptr.String("Mock::Resource::Type"),
+				}
+				tempTimeStamp := templogTimeStamp
+				logEntry.Timestamp = &tempTimeStamp
+				logs = append(logs, logEntry)
+			}
+			templogTimeStamp = templogTimeStamp.Add(time.Hour * -24)
+		}
+		return logs, nil
+	} else {
+		return []types.StackEvent{
+			{
+				EventId:              ptr.String("mock event id"),
+				StackId:              ptr.String(stackName),
+				StackName:            ptr.String(stackName),
+				Timestamp:            &now,
+				ClientRequestToken:   ptr.String("mock event token"),
+				LogicalResourceId:    ptr.String("MockResourceId"),
+				PhysicalResourceId:   ptr.String("MockPhysicalId"),
+				ResourceProperties:   ptr.String("mock resource properties"),
+				ResourceStatus:       types.ResourceStatusCreateInProgress,
+				ResourceStatusReason: ptr.String("mock status reason"),
+				ResourceType:         ptr.String("Mock::Resource::Type"),
+			},
+		}, nil
+	}
 }
 
 // CreateChangeSet creates a changeset
diff --git a/internal/cmd/logs/logs.go b/internal/cmd/logs/logs.go
index a6f5c83c..79b13fff 100644
--- a/internal/cmd/logs/logs.go
+++ b/internal/cmd/logs/logs.go
@@ -11,6 +11,8 @@ import (
 
 var allLogs = false
 var chart = false
+var logsLength uint
+var logsDays uint
 
 // Cmd is the logs command's entrypoint
 var Cmd = &cobra.Command{
@@ -44,7 +46,7 @@ You can use the --all flag to change this behaviour.`,
 					fmt.Println("No interesting log messages to display. To see everything, use the --all flag")
 				}
 			} else {
-				printLogs(logs)
+				printLogs(logsLength, logsDays, logs)
 			}
 		} else {
 			err := createChart(stackName)
@@ -59,4 +61,6 @@ func init() {
 	Cmd.Flags().BoolVarP(&allLogs, "all", "a", false, "include uninteresting logs")
 	Cmd.Flags().BoolVarP(&chart, "chart", "c", false, "Output a gantt chart of the most recent action as an html file")
 	Cmd.Flags().BoolVar(&config.Debug, "debug", false, "Output debugging information")
+	Cmd.Flags().UintVarP(&logsLength, "length", "l", 0, "Number of logs to display")
+	Cmd.Flags().UintVarP(&logsDays, "days", "d", 0, "Age of the logs to display in days")
 }
diff --git a/internal/cmd/logs/util.go b/internal/cmd/logs/util.go
index e148b63c..0602c40e 100644
--- a/internal/cmd/logs/util.go
+++ b/internal/cmd/logs/util.go
@@ -34,7 +34,43 @@ func (e events) Less(i, j int) bool {
 	return ptr.ToTime(e[i].Timestamp).Unix() < ptr.ToTime(e[j].Timestamp).Unix()
 }
 
-func printLogs(logs events) {
+func reduceLogsToLength(logsRange uint, logs *events) {
+	if int(logsRange) >= len(*logs) {
+		return
+	}
+	var reducedLogs events
+	for i := 0; i < int(logsRange); i++ {
+		reducedLogs = append(reducedLogs, (*logs)[i])
+	}
+	*logs = reducedLogs
+}
+
+func reduceLogsByDuration(logsRange time.Duration, logs *events) {
+	timeNow := time.Now()
+	logLimitTime := timeNow.Add(logsRange)
+	var reducedLogs events
+	for _, log := range *logs {
+		if log.Timestamp.After(logLimitTime) {
+			reducedLogs = append(reducedLogs, log)
+		}
+
+	}
+	*logs = reducedLogs
+}
+
+func reduceLogs(logsRange uint, logsDays uint, logs *events) {
+	if logsDays > 0 {
+		duration := time.Duration(time.Hour * time.Duration(int(logsDays)*-24))
+		reduceLogsByDuration(duration, logs)
+	}
+	if logsRange > 0 {
+		reduceLogsToLength(logsRange, logs)
+	}
+
+}
+
+func printLogs(logsRange uint, logsDays uint, logs events) {
+	reduceLogs(logsRange, logsDays, &logs)
 	for _, log := range logs {
 		fmt.Printf("%s %s/%s (%s) %s",
 			console.White(ptr.ToTime(log.Timestamp).Format(time.Stamp)),


Test patch:
diff --git a/internal/cmd/logs/logs_test.go b/internal/cmd/logs/logs_test.go
index b4fb4ce4..8e450a60 100644
--- a/internal/cmd/logs/logs_test.go
+++ b/internal/cmd/logs/logs_test.go
@@ -26,8 +26,10 @@ func Example_logs_help() {
 	//   logs, log
 	//
 	// Flags:
-	//   -a, --all     include uninteresting logs
-	//   -c, --chart   Output a gantt chart of the most recent action as an html file
-	//       --debug   Output debugging information
-	//   -h, --help    help for logs
+	//   -a, --all           include uninteresting logs
+	//   -c, --chart         Output a gantt chart of the most recent action as an html file
+	//   -d, --days uint     Age of the logs to display in days
+	//       --debug         Output debugging information
+	//   -h, --help          help for logs
+	//   -l, --length uint   Number of logs to display
 }
diff --git a/internal/cmd/logs/util_test.go b/internal/cmd/logs/util_test.go
new file mode 100644
index 00000000..dcffc48c
--- /dev/null
+++ b/internal/cmd/logs/util_test.go
@@ -0,0 +1,86 @@
+//go:build func_test
+
+package logs
+
+import (
+	"log"
+	"testing"
+	"time"
+)
+
+func logsTestSetup() {
+	allLogs = true
+}
+
+func logsTestTeardown() {
+	allLogs = false
+}
+func TestReduceLogsByLength(t *testing.T) {
+	logsTestSetup()
+	logs, err := getLogs("logsrange-test-mock-stack", "MockResourceId")
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	if len(logs) < 30 {
+		t.Fatalf("expected log length 30, but got: %d", len(logs))
+	}
+	reduceLogsToLength(10, &logs)
+	if len(logs) != 10 {
+		t.Fatalf("expeced 10 logs got: %d", len(logs))
+	}
+	logsTestTeardown()
+}
+
+func TestReducedLogsByDuration(t *testing.T) {
+	logsTestSetup()
+	defer logsTestTeardown()
+
+	logs, err := getLogs("logsrange-test-mock-stack", "MockResourceId")
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	if len(logs) < 30 {
+		t.Fatalf("expected log length 30, but got: %d", len(logs))
+	}
+	logDays := 10
+	duration := time.Duration(time.Hour * time.Duration(logDays*-24))
+	expectedFirstLogTimeStamp := time.Now()
+	expectedLastLogTimeStamp := expectedFirstLogTimeStamp.Add(duration).Add(time.Hour * 24) // adding one day because we only want logs created after the the day before 10 days
+	reduceLogsByDuration(duration, &logs)
+
+	// anon function to print out all the relavant logs when tests fail
+	printLogs := func() {
+		t.Log(duration)
+		t.Log(time.Now().Add(duration))
+		t.Log("input logs:")
+		for _, log := range logs {
+			t.Log(log.Timestamp)
+		}
+	}
+
+	if (logs)[0].Timestamp.Sub(expectedFirstLogTimeStamp) > time.Duration(time.Hour) {
+		printLogs()
+		t.Fatalf("expected %s timestamp on the latest entry but got %s. The difference is: %s", expectedFirstLogTimeStamp, (logs)[0].Timestamp, (logs)[0].Timestamp.Sub(expectedFirstLogTimeStamp))
+	}
+
+	if (logs)[len(logs)-1].Timestamp.Sub(expectedLastLogTimeStamp) > time.Duration(time.Hour) {
+		printLogs()
+		t.Fatalf("expected %s timestamp on the oldest entry but got %s. The difference is: %s", expectedLastLogTimeStamp, (logs)[len(logs)-1].Timestamp, (logs)[len(logs)-1].Timestamp.Sub(expectedLastLogTimeStamp))
+	}
+}
+
+func TestReducedLogsWithMultipleFlags(t *testing.T) {
+	logsTestSetup()
+	defer logsTestTeardown()
+
+	logs, err := getLogs("logsrange-test-mock-stack", "MockResourceId")
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+
+	reduceLogs(5, 1, &logs)
+	expectedNumberOfLogs := 5
+	if len(logs) != expectedNumberOfLogs {
+		log.Fatalf("expected number of logs is %d, but got %d", expectedNumberOfLogs, len(logs))
+	}
+}