diff --git a/cmd/logs.go b/cmd/logs.go new file mode 100644 index 0000000..75bdd00 --- /dev/null +++ b/cmd/logs.go @@ -0,0 +1,460 @@ +package cmd + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/antchfx/jsonquery" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/markusmobius/go-dateparser" + "github.com/spf13/cobra" + awspkg "github.com/surajincloud/kubectl-eks/pkg/aws" + "github.com/surajincloud/kubectl-eks/pkg/kube" +) + +var Follow bool +var SinceTime string + +func init() { + logsCmd.Flags().BoolVarP(&Follow, "follow", "f", false, "Follow logs (not available for node file queries)") + logsCmd.Flags().StringVar(&SinceTime, "since", "1 hour ago", "What time logs should start from") +} + +// logsCmd represents the logs command +var logsCmd = &cobra.Command{ + Use: "logs [flags] LOG_SOURCE", + Example: ` kubectl eks logs kube-apiserver + kubectl eks logs NODE [kubelet] + + Query multiple log sources: + kubectl eks logs api audit scheduler + kubectl eks logs NODE kubelet containerd`, + Args: cobra.MinimumNArgs(1), + Short: "Get logs from EKS control plane or nodes", + Long: "Get logs from EKS control plane or nodes", + RunE: logs, +} + +// the main logs function is responsible for routing the request +// to the correct log endpoint (cloudwatch or kubelet) and +// printing the logs to stdout from the logs channel +func logs(cmd *cobra.Command, args []string) error { + + // pass empty string to let fuction get name + clusterName, err := kube.GetClusterName(*KubernetesConfigFlags.ClusterName) + if err != nil { + return err + } + + // we only look at the first argument to determine if it is a control plane log source or node + logTarget := args[0] + logsChan := make(chan string) + logsDoneChan := make(chan bool, len(args)) + + cloudwatchLogStreams := []string{ + "scheduler", + "kube-scheduler", + "audit", + "kube-audit", + "kube-apiserver-audit", + "cm", + "controller-manager", + "kube-controller-manager", + "api", + "apiserver", + "kube-apiserver", + "auth", + "authenticator", + "ccm", + "cloud-controller", + "cloud-controller-manager", + } + + // handle ctl+c interrupt without printing output + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + os.Exit(0) + }() + + // check first argument if it is a control plane log source or node + if contains(cloudwatchLogStreams, logTarget) || (logTarget == "all") { + + if logTarget == "all" { + args = args[1:] + args = append(args, "scheduler", "kube-apiserver-audit", "kube-controller-manager", "kube-apiserver", "authenticator", "cloud-controller-manager") + } + + var cwlStreamInput cloudwatchlogs.DescribeLogStreamsInput + var streams *cloudwatchlogs.DescribeLogStreamsOutput + var limit int32 = 100 + var cwlGroupPrefix string + + cwlGroupName := "/aws/eks/" + clusterName + "/cluster" + cwlStreamInput.LogGroupName = &cwlGroupName + + // aws config + ctx := context.Background() + // read flag values + region, _ := cmd.Flags().GetString("region") + + cfg, err := awspkg.GetAWSConfig(ctx, region) + if err != nil { + log.Fatal(err) + } + + // TODO check if logging is enabled + // TODO allow user to enable logging + svc := eks.NewFromConfig(cfg) + eksClusterInput := &eks.DescribeClusterInput{ + Name: aws.String(clusterName), + } + result, _ := svc.DescribeCluster(ctx, eksClusterInput) + // check if logging is enabled + if !*result.Cluster.Logging.ClusterLogging[0].Enabled == true { + fmt.Println("Logging is not enabled for this cluster. Please enable logging and try again.") + os.Exit(1) + } + + cwl := cloudwatchlogs.NewFromConfig(cfg) + // verify the group exists first + err = ensureLogGroupExists(cwlGroupName, ctx, cwl) + //TODO prompt if user wants to enable logs + if err != nil { + panic(err) + } + + var fetchStream string + matchedStream := 0 + for _, logSource := range args { + + cwlGroupPrefix = getLogStreamPrefix(logSource) + cwlStreamInput.LogStreamNamePrefix = &cwlGroupPrefix + + streams, err = cwl.DescribeLogStreams(ctx, &cwlStreamInput) + if err != nil { + log.Fatal(err) + } + + if len(streams.LogStreams) == 0 { + fmt.Fprintln(os.Stderr, "No log streams found for", cwlGroupPrefix) + } else if cwlGroupPrefix == "kube-apiserver" { + // we have to make sure kube-apiserver doesn't add kube-apiserver-audit logs + for _, stream := range streams.LogStreams { + if !strings.Contains(*stream.LogStreamName, "kube-apiserver-audit") { + fetchStream = *stream.LogStreamName + // only match the first stream + break + } + } + if fetchStream == "" { + fmt.Fprintln(os.Stderr, "No log streams found for", cwlGroupPrefix) + } else { + go getLogEvents(&cwlGroupName, &fetchStream, &limit, logsChan, logsDoneChan, ctx, cwl, len(args)) + matchedStream++ + + } + } else { + fetchStream = *streams.LogStreams[0].LogStreamName + go getLogEvents(&cwlGroupName, &fetchStream, &limit, logsChan, logsDoneChan, ctx, cwl, len(args)) + matchedStream++ + } + } + + // use a count to make sure we have a goroutine fetching logs + if matchedStream > 0 { + // Print each line from logsChan + for log := range logsChan { + // print the log + fmt.Println(log) + + // once all event streams are done, close the channel + if len(logsDoneChan) == len(args) { + close(logsChan) + } + } + } + } else { + // we assume the target is a node instead of control plane + nodeList, err := kube.GetNodes(KubernetesConfigFlags) + if err != nil { + return err + } + + var nodeMatched bool = false + var currentNodeSlice []string + logTargetSlice := strings.Split(logTarget, ".") + + for _, i := range nodeList { + // match node based on substring eg. ip-192-168-1-1 + currentNodeSlice = strings.Split(i.Name, ".") + if currentNodeSlice[0] == logTargetSlice[0] { + nodeMatched = true + var query []string + + // use all additional arguments as services to query + if len(args) > 1 { + query = args[1:] + } else { + query = append(query, "kubelet") + } + + // validate kubelet settings for remote logs + if validateKubeletConfig(i.Name) { + // get logs and assume query is journald and can accept sinceTime + go getNodeLogs(i.Name, query, false, logsChan, logsDoneChan) + + // print each line from logsChan + for log := range logsChan { + + fmt.Println(log) + + if len(logsDoneChan) == 1 { + close(logsChan) + } + } + } + } + } + if nodeMatched { + return nil + } else { + fmt.Printf("Node %s not found\nTo query control plane logs please see options in --help output.\n", logTarget) + } + } + return nil +} + +// fetches logs from a node using the kubelet api +// sends logs to the logsChan and sends a bool to logsDoneChan when done +// there are additional queries that can be used on the kubelet API but +// I wanted to keep the UX simple so people should rely on CLI tools +// to filter logs further +func getNodeLogs(node string, query []string, fileQuery bool, logsChan chan<- string, logsDoneChan chan<- bool) { + + dt, err := dateparser.Parse(nil, SinceTime) + if err != nil { + panic(err) + } + // create URL for log fetching + rawURL := "/api/v1/nodes/" + node + "/proxy/logs/" + + for { + + clientSet, _ := kube.ClientSet(KubernetesConfigFlags) + req := clientSet.RESTClient().Get(). + AbsPath(rawURL) + + for _, q := range query { + req.Param("query", q) + } + + if !fileQuery { + // sinceTime is ignored for file queries + req.Param("sinceTime", dt.Time.Format(time.RFC3339)) + } + + resp, err := req.DoRaw(context.Background()) + if err != nil { + log.Panicln(err, req.URL().String()) + } + + // api returns a byte string + // convert to string and split by newline to send each line to channel + for _, logLine := range strings.Split(string(resp), "\n") { + // fmt.Printf("%v %s", lineNumber, logLine) + if strings.Contains(logLine, "options present and query resolved to log files") { + // file queries cannot use sinceTime + // we catch this error output and run again as file query + go getNodeLogs(node, query, true, logsChan, logsDoneChan) + break + } else if logLine == "" || + strings.Contains(logLine, "-- No entries --") || + strings.Contains(logLine, "-- Logs begin at ") { + // don't send log decorations + } else { + logsChan <- logLine + } + } + + if Follow { + if fileQuery { + fmt.Fprintln(os.Stderr, "Cannot follow file queries") + close(logsChan) + break + } + dt, err = dateparser.Parse(nil, "now") + if err != nil { + panic(err) + } + time.Sleep(1 * time.Second) + } else { + if fileQuery { + close(logsChan) + } else { + logsDoneChan <- true + } + break + } + } +} + +// ensureLogGroupExists first checks if the log group exists +// Cluster logging can be enabled but the log group may not exist +// this is because not all control plane components need logging enabled +func ensureLogGroupExists(name string, ctx context.Context, cwl *cloudwatchlogs.Client) error { + resp, err := cwl.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{}) + if err != nil { + return err + } + + for _, logGroup := range resp.LogGroups { + if *logGroup.LogGroupName == name { + return nil + } + } + + return err +} + +// fetchLogs fetches logs from a cloudwatch stream +// sends logs to the logsChan and sends true to logsDoneChan when done +// logs are not guaranteed to be in order because they are fetched from each source +// individually and no ordering is performed on the channel before printing +func getLogEvents(logGroupName *string, logStreamName *string, limit *int32, logsChan chan<- string, logsDoneChan chan<- bool, ctx context.Context, cwl *cloudwatchlogs.Client, totalStreams int) { + + dt, err := dateparser.Parse(nil, SinceTime) + if err != nil { + panic(err) + } + + // loop forever if Follow == true + for { + resp, err := cwl.GetLogEvents(ctx, &cloudwatchlogs.GetLogEventsInput{ + Limit: limit, + LogGroupName: logGroupName, + LogStreamName: logStreamName, + StartTime: aws.Int64(dt.Time.UnixMilli()), + }) + if err != nil { + panic(err) + } + + for i, event := range resp.Events { + // TODO allow for following tokens for more logs from different streams + // currently we only fetch the newest stream which may not have all logs + + if i == len(resp.Events)-1 { + if Follow { + dt, err = dateparser.Parse(nil, "now") + if err != nil { + panic(err) + } + // wait 1 sec before querying again + time.Sleep(1 * time.Second) + } else { + logsDoneChan <- true + } + logsChan <- *event.Message + } else { + logsChan <- *event.Message + } + + } + + } +} + +// initialize command and get region +func init() { + rootCmd.AddCommand(logsCmd) + logsCmd.PersistentFlags().String("region", "", "region") +} + +// check if a string is present in a slice +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +// Convert possible log source aliases to full log stream prefix +// needed because log streams have defined prefixes but we allow +// multiple aliases for each log source +func getLogStreamPrefix(logTarget string) string { + schedulerSlice := []string{"scheduler", "kube-scheduler"} + auditSlice := []string{"audit", "kube-audit", "kube-apiserver-audit"} + cmSlice := []string{"cm", "controller-manager", "kube-controller-manager"} + apiSlice := []string{"api", "apiserver", "kube-apiserver"} + authSlice := []string{"auth", "authenticator"} + ccmSlice := []string{"ccm", "cloud-controller", "cloud-controller-manager"} + + if contains(schedulerSlice, logTarget) { + return "kube-scheduler" + } else if contains(auditSlice, logTarget) { + return "kube-apiserver-audit" + } else if contains(cmSlice, logTarget) { + return "kube-controller-manager" + } else if contains(apiSlice, logTarget) { + return "kube-apiserver" + } else if contains(authSlice, logTarget) { + return "authenticator" + } else if contains(ccmSlice, logTarget) { + return "cloud-controller-manager" + } else { + return "" + } +} + +// validates kubelet config for remote logging +// fetches the full config from the configz endpoint +// verifies if the settings are set correctly +// there's currently no way I know to change this without restarting the kubelet +func validateKubeletConfig(node string) bool { + rawURL := "/api/v1/nodes/" + node + "/proxy/configz" + clientSet, _ := kube.ClientSet(KubernetesConfigFlags) + req := clientSet.RESTClient().Get(). + AbsPath(rawURL).Timeout(20 * time.Second) + + resp, err := req.DoRaw(context.Background()) + if err != nil { + log.Panicln(err, req.URL().String()) + } + + // read the kueblet config json + kubeletConfigJson, err := jsonquery.Parse(strings.NewReader(string(resp))) + if err != nil { + panic(err) + } + + // check if the node has the appropriate config for remote logging + nodeLogQuery := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/featureGates/NodeLogQuery") + + systemLogHandler := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/enableSystemLogHandler") + systemLogQuery := jsonquery.FindOne(kubeletConfigJson, "kubeletconfig/enableSystemLogQuery") + if (nodeLogQuery != nil) && (systemLogHandler != nil) && (systemLogQuery != nil) { + if nodeLogQuery.Value().(bool) { + return true + } + } + // if the node doesn't have the appropriate config, print a message + fmt.Printf(` Node %s is not configured for remote logs. + Please enable remote logging on the kubelet from the documentation here + Requires Kubernetes 1.27 https://kubernetes.io/blog/2023/04/21/node-log-query-alpha/`, node) + + return false + +} diff --git a/cmd/ssm.go b/cmd/ssm.go index 9f3918d..9f082f9 100644 --- a/cmd/ssm.go +++ b/cmd/ssm.go @@ -15,7 +15,8 @@ import ( // ssmCmd represents the ssm command var ssmCmd = &cobra.Command{ - Use: "ssm", + Use: "ssm [flags] node", + Args: cobra.ExactArgs(1), Short: "Access given EKS node via SSM", Long: ` SSM Access to given EKS Node diff --git a/go.mod b/go.mod index 03581cf..fb5a0a9 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,14 @@ module github.com/surajincloud/kubectl-eks go 1.20 require ( - github.com/aws/aws-sdk-go-v2 v1.17.4 + github.com/antchfx/jsonquery v1.3.2 + github.com/aws/aws-sdk-go-v2 v1.19.0 github.com/aws/aws-sdk-go-v2/config v1.18.13 + github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.22.1 github.com/aws/aws-sdk-go-v2/service/ec2 v1.64.0 github.com/aws/aws-sdk-go-v2/service/eks v1.27.3 github.com/aws/aws-sdk-go-v2/service/ssm v1.31.3 + github.com/markusmobius/go-dateparser v1.2.1 github.com/mmmorris1975/ssm-session-client v0.400.1 github.com/siderolabs/go-kubeconfig v0.1.0 github.com/spf13/cobra v1.6.1 @@ -18,11 +21,12 @@ require ( ) require ( + github.com/antchfx/xpath v1.2.3 // indirect github.com/aws/aws-sdk-go v1.44.76 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.13 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.12.2 // indirect @@ -33,6 +37,7 @@ require ( github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect + github.com/elliotchance/pie/v2 v2.7.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect @@ -42,6 +47,7 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/swag v0.19.14 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect @@ -51,12 +57,16 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hablullah/go-hijri v1.0.2 // indirect + github.com/hablullah/go-juliandays v1.0.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/magefile/mage v1.15.0 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -68,17 +78,20 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/testify v1.8.1 // indirect + github.com/tetratelabs/wazero v1.3.1 // indirect github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19 // indirect + github.com/wasilibs/go-re2 v1.3.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect github.com/xtaci/smux v1.5.16 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/exp v0.0.0-20230724220655-d98519c11495 // indirect golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.3.0 // indirect + golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index 0d746f9..c027528 100644 --- a/go.sum +++ b/go.sum @@ -33,11 +33,16 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antchfx/jsonquery v1.3.2 h1:/BgHv1le9CCkqDe7t1x5BRlCg6DQmXTsztnMQFG5Hoc= +github.com/antchfx/jsonquery v1.3.2/go.mod h1:VsW9O/sNgHoUVvhoMEjR+opjIOjKOViNFTpAlxcI4Ws= +github.com/antchfx/xpath v1.2.3 h1:CCZWOzv5bAqjVv0offZ2LVgVYFbeldKQVuLNbViZdes= +github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/aws/aws-sdk-go v1.44.76 h1:5e8yGO/XeNYKckOjpBKUd5wStf0So3CrQIiOMCVLpOI= github.com/aws/aws-sdk-go v1.44.76/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw= -github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k= +github.com/aws/aws-sdk-go-v2 v1.19.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.17.10/go.mod h1:/4np+UiJJKpWHN7Q+LZvqXYgyjgeXm5+lLfDI6TPZao= github.com/aws/aws-sdk-go-v2/config v1.18.13 h1:v0xlYqbO6/EVlM8tUn2QEOA7btQxcgidEq2JRDBPTho= github.com/aws/aws-sdk-go-v2/config v1.18.13/go.mod h1:r39wGSZB7wPDW1i54JyQXUpc5KsWjh5z/3S5D9eCqDg= @@ -48,14 +53,18 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.19/go.mod h1:VihW95zQpeKQWVP github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29/go.mod h1:M/eUABlDbw2uVrdAn+UsI6M727qp2fxkp8K0ejcBDUY= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26/go.mod h1:Y2OJ+P+MC1u1VKnavT+PshiEuGPyh/7DqxoDNij4/bg= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.22.1 h1:qm8LnOQM9yHwfGI7kY2W3gpd3hKttGuKkWplI7fHGH4= +github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.22.1/go.mod h1:4tbPbziIVYtGAoIqr939uQmg6G/RAbZtU9j4384r1LI= github.com/aws/aws-sdk-go-v2/service/ec2 v1.64.0 h1:zI904mHbXiJgIc5bwpo5jOk1+wDvcX04PyYd2dInh/4= github.com/aws/aws-sdk-go-v2/service/ec2 v1.64.0/go.mod h1:zul71QqzR4D1a90/5FloZiAnZ1CtuIjVH7R9MP997+A= github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.14.11/go.mod h1:E29Z9YWBhILsNzaxWab92P6Wni6pdd4NVN8D4FCyNUU= @@ -96,6 +105,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= +github.com/elliotchance/pie/v2 v2.7.0 h1:FqoIKg4uj0G/CrLGuMS9ejnFKa92lxE1dEgBD3pShXg= +github.com/elliotchance/pie/v2 v2.7.0/go.mod h1:18t0dgGFH006g4eVdDtWfgFZPQEgl10IoEO8YWEq3Og= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -128,6 +139,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -191,6 +204,10 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hablullah/go-hijri v1.0.2 h1:drT/MZpSZJQXo7jftf5fthArShcaMtsal0Zf/dnmp6k= +github.com/hablullah/go-hijri v1.0.2/go.mod h1:OS5qyYLDjORXzK4O1adFw9Q5WfhOcMdAKglDkcTxgWQ= +github.com/hablullah/go-juliandays v1.0.0 h1:A8YM7wIj16SzlKT0SRJc9CD29iiaUzpBLzh5hr0/5p0= +github.com/hablullah/go-juliandays v1.0.0/go.mod h1:0JOYq4oFOuDja+oospuc61YoX+uNEn7Z6uHYTbBzdGc= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -198,6 +215,8 @@ github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958 h1:qxLoi6CAcXVzjfvu+KXIXJOAsQB62LXjsfbOaErsVzE= +github.com/jalaali/go-jalaali v0.0.0-20210801064154-80525e88d958/go.mod h1:Wqfu7mjUHj9WDzSSPI5KfBclTTEnLveRUFr/ujWnTgE= 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= @@ -218,10 +237,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markusmobius/go-dateparser v1.2.1 h1:mYRRdu3TzpAeE6fSl2Gn3arfxEtoTRvFOKlumlVsUtg= +github.com/markusmobius/go-dateparser v1.2.1/go.mod h1:5xYsZ1h7iB3sE1BSu8bkjYpbFST7EU1/AFxcyO3mgYg= github.com/mmmorris1975/ssm-session-client v0.400.1 h1:WrKlegOjn4geYte3i7wYn3flONGW0JmshMThjMYAKno= github.com/mmmorris1975/ssm-session-client v0.400.1/go.mod h1:Gv045ehxc1lwPCiaaYNrjdRSBctFaIfTOSb3y8MpQ8E= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -267,8 +290,13 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM= +github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19 h1:HlxV0XiEKMMyjS3gGtJmmFZsxQ22GsLvA7F980il+1w= github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY= +github.com/wasilibs/go-re2 v1.3.0 h1:LFhBNzoStM3wMie6rN2slD1cuYH2CGiHpvNL3UtcsMw= +github.com/wasilibs/go-re2 v1.3.0/go.mod h1:AafrCXVvGRJJOImMajgJ2M7rVmWyisVK7sFshbxnVrg= +github.com/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk= @@ -301,6 +329,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230724220655-d98519c11495 h1:zKGKw2WlGb8oPoRGqQ2PT8g2YoCN1w/YbbQjHXCdUWE= +golang.org/x/exp v0.0.0-20230724220655-d98519c11495/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -406,8 +436,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= @@ -419,8 +449,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=