From 5b16fd79c8fe35654648dfa810e5814f6eafdd95 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Tue, 29 Oct 2024 10:08:03 -0400 Subject: [PATCH 01/15] docs: cluster scanner first commit --- scripts/cluster-scanner/README.md | 57 +++++++++++++++ scripts/cluster-scanner/go.mod | 38 ++++++++++ scripts/cluster-scanner/go.sum | 70 +++++++++++++++++++ .../cluster-scanner/internal/format_age.go | 32 +++++++++ .../internal/search_clusters.go | 16 +++++ .../internal/search_old_clusters.go | 30 ++++++++ scripts/cluster-scanner/main.go | 58 +++++++++++++++ 7 files changed, 301 insertions(+) create mode 100644 scripts/cluster-scanner/README.md create mode 100644 scripts/cluster-scanner/go.mod create mode 100644 scripts/cluster-scanner/go.sum create mode 100644 scripts/cluster-scanner/internal/format_age.go create mode 100644 scripts/cluster-scanner/internal/search_clusters.go create mode 100644 scripts/cluster-scanner/internal/search_old_clusters.go create mode 100644 scripts/cluster-scanner/main.go diff --git a/scripts/cluster-scanner/README.md b/scripts/cluster-scanner/README.md new file mode 100644 index 0000000..dbebdc5 --- /dev/null +++ b/scripts/cluster-scanner/README.md @@ -0,0 +1,57 @@ +# Cluster Scanner + +The **Cluster Scanner** tool uses the Palette Go SDK to scan your Palette environment and identify clusters that have been active for more than 24 hours. + +## Prerequisites + +- Go version 1.22.5 or later +- Git +- The `palette-samples` repository available locally +- A Palette acount +- A Palette API key + +## Usage + +1. Open a terminal window and export your Palette URL. Replace `` with your Palette URL, for example, `console.spectrocloud.com`. + + ```shell + export PALETTE_HOST= + ``` + +2. Export your Palette API key. Replace `` with your Palette API key. + + ```shell + export PALETTE_API_KEY= + ``` + +3. To scan a specific project, export the project's UID. Replace `` with the Palette project UID. If no project is provided, the tool assumes a tenant scope and scans clusters across all projects. + + ```shell + export PALETTE_PROJECT_UID= + ``` + +4. Navigate to the `cluster-scanner` folder. + + ```shell + cd cluster-scanner + ``` + +5. Issue the command below to download the required Palette SDK modules. + + ```shell + go get + ``` + +6. Execute the `cluster-scanner` application. + + ```shell + go run . + ``` + + The application will print the clusters that have been active in your Palette environment for more than 24 hours. + + ```text hideClipboard + time=2024-10-28T21:21:47.516-04:00 level=INFO msg="Setting scope to tenant." + time=2024-10-28T21:21:47.516-04:00 level=INFO msg="Searching for clusters..." + time=2024-10-28T21:21:48.297-04:00 level=INFO msg="The aws cluster named aws-test has been running for 2 weeks 6 days 2 hours. Are you sure you need this cluster?" + ``` diff --git a/scripts/cluster-scanner/go.mod b/scripts/cluster-scanner/go.mod new file mode 100644 index 0000000..2f12676 --- /dev/null +++ b/scripts/cluster-scanner/go.mod @@ -0,0 +1,38 @@ +module github.com/spectrocloud/palette-samples/cluster-scanner + +go 1.22.5 + +toolchain go1.22.8 + +require ( + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/go-errors/errors v1.5.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/analysis v0.23.0 // indirect + github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/loads v0.22.0 // indirect + github.com/go-openapi/runtime v0.28.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/strfmt v0.23.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/validate v0.24.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spectrocloud/palette-sdk-go v0.0.0-20240930211255-e224a905d31a // indirect + go.mongodb.org/mongo-driver v1.16.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/scripts/cluster-scanner/go.sum b/scripts/cluster-scanner/go.sum new file mode 100644 index 0000000..9911030 --- /dev/null +++ b/scripts/cluster-scanner/go.sum @@ -0,0 +1,70 @@ +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= +github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= +github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= +github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= +github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= +github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= +github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= +github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= +github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spectrocloud/palette-sdk-go v0.0.0-20240930211255-e224a905d31a h1:u1itx2mJzS9VCjVRr3cXqSnJpBxELSay5pdgFHIhz8w= +github.com/spectrocloud/palette-sdk-go v0.0.0-20240930211255-e224a905d31a/go.mod h1:dSlNvDS0qwUWTbrYI6P8x981mcbbRHFrBg67v5zl81U= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= +go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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/scripts/cluster-scanner/internal/format_age.go b/scripts/cluster-scanner/internal/format_age.go new file mode 100644 index 0000000..6866c1c --- /dev/null +++ b/scripts/cluster-scanner/internal/format_age.go @@ -0,0 +1,32 @@ +package internal + +import ( + "fmt" + "time" +) + +// Format the cluster age using weeks, days, and hours +func FormatAge (clusterAge time.Duration) (int, int, int) { + const hoursPerWeek = 168 + weeks := int(clusterAge.Hours() / hoursPerWeek) + remainingHours := int(clusterAge.Hours() - float64((weeks * hoursPerWeek))) + days := int(remainingHours / 24) + hours := int(remainingHours % 24) + return weeks, days, hours +} + +// Return the cluster age as a formatted string +func PrintFormattedAge (clusterAge time.Duration) (string) { + weeks, days, hours := FormatAge(clusterAge) + formattedString := "" + if weeks > 0 { + formattedString += fmt.Sprint(weeks) + " weeks " + } + if days > 0 { + formattedString += fmt.Sprint(days) + " days " + } + if hours > 0 { + formattedString += fmt.Sprint(hours) + " hours" + } + return formattedString +} \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_clusters.go b/scripts/cluster-scanner/internal/search_clusters.go new file mode 100644 index 0000000..b917c69 --- /dev/null +++ b/scripts/cluster-scanner/internal/search_clusters.go @@ -0,0 +1,16 @@ +package internal + +import ( + "log/slog" + + "github.com/spectrocloud/palette-sdk-go/api/models" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func SearchClusters (paletteClient *client.V1Client, logger *slog.Logger) ([]*models.V1SpectroClusterSummary, error) { + // Search for clusters + logger.Info("Searching for clusters...") + clusters, err := paletteClient.SearchClusterSummaries(&models.V1SearchFilterSpec{}, []*models.V1SearchFilterSortSpec{}) + + return clusters, err +} \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go new file mode 100644 index 0000000..0e5e3b0 --- /dev/null +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -0,0 +1,30 @@ +package internal + +import ( + "fmt" + "log/slog" + "time" + + "github.com/spectrocloud/palette-sdk-go/api/models" +) + +func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog.Logger) bool { + // Variable to keep track of any found clusters older than 24h + foundOldCluster := false + + // List the clusters that are running for more than 24h + for _, cluster := range clusters { + creationTime := cluster.Metadata.CreationTimestamp + timeValue := time.Time(creationTime) + timeNow := time.Now() + clusterAge := timeNow.Sub(timeValue) + + if clusterAge.Hours() >= 24 { + foundOldCluster = true + message := fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, PrintFormattedAge(clusterAge)) + logger.Info(message) + } + } + + return foundOldCluster +} \ No newline at end of file diff --git a/scripts/cluster-scanner/main.go b/scripts/cluster-scanner/main.go new file mode 100644 index 0000000..c0716a8 --- /dev/null +++ b/scripts/cluster-scanner/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "log/slog" + "os" + + "github.com/spectrocloud/palette-samples/cluster-scanner/internal" + "github.com/spectrocloud/palette-sdk-go/client" +) + +func main() { + + // Read environment variables + host := os.Getenv("PALETTE_HOST") + apiKey := os.Getenv("PALETTE_API_KEY") + projectUid := os.Getenv("PALETTE_PROJECT_UID") + + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + + + if host == "" || apiKey == "" { + logger.Error("You must specify the PALETTE_HOST and PALETTE_API_KEY environment variables.") + os.Exit(1) + } + + // Initialize a Palette client + paletteClient := client.New( + client.WithPaletteURI(host), + client.WithAPIKey(apiKey), + ) + + if projectUid != "" { + client.WithScopeProject(projectUid)(paletteClient) + logger.Info("Setting scope to project.") + } else { + client.WithScopeTenant()(paletteClient) + logger.Info("Setting scope to tenant.") + } + + // Search for clusters + clusters, err := internal.SearchClusters(paletteClient, logger) + if err != nil { + logger.Error("Failed to search cluster summaries", "error", err) + os.Exit(2) + } + + // Check active clusters + if len(clusters) == 0 { + logger.Warn("There are no clusters running.") + return + } + + foundOldCluster := internal.SearchOldClusters(clusters, logger) + + if !foundOldCluster { + logger.Info("There are no clusters running for more than 24 hours.") + } +} \ No newline at end of file From 167608b717b902fa508b8a849b6dc8135c4207eb Mon Sep 17 00:00:00 2001 From: caroldelwing Date: Tue, 29 Oct 2024 21:37:17 -0400 Subject: [PATCH 02/15] Apply suggestions from code review Co-authored-by: Adelina Simion <43963729+addetz@users.noreply.github.com> --- scripts/cluster-scanner/README.md | 4 ++-- scripts/cluster-scanner/internal/format_age.go | 18 +++++++++--------- .../internal/search_clusters.go | 3 +-- .../internal/search_old_clusters.go | 10 ++++------ 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/scripts/cluster-scanner/README.md b/scripts/cluster-scanner/README.md index dbebdc5..5082f86 100644 --- a/scripts/cluster-scanner/README.md +++ b/scripts/cluster-scanner/README.md @@ -4,7 +4,7 @@ The **Cluster Scanner** tool uses the Palette Go SDK to scan your Palette enviro ## Prerequisites -- Go version 1.22.5 or later +- Go version 1.22 or later - Git - The `palette-samples` repository available locally - A Palette acount @@ -39,7 +39,7 @@ The **Cluster Scanner** tool uses the Palette Go SDK to scan your Palette enviro 5. Issue the command below to download the required Palette SDK modules. ```shell - go get + go get ./... ``` 6. Execute the `cluster-scanner` application. diff --git a/scripts/cluster-scanner/internal/format_age.go b/scripts/cluster-scanner/internal/format_age.go index 6866c1c..40c3394 100644 --- a/scripts/cluster-scanner/internal/format_age.go +++ b/scripts/cluster-scanner/internal/format_age.go @@ -7,26 +7,26 @@ import ( // Format the cluster age using weeks, days, and hours func FormatAge (clusterAge time.Duration) (int, int, int) { - const hoursPerWeek = 168 - weeks := int(clusterAge.Hours() / hoursPerWeek) - remainingHours := int(clusterAge.Hours() - float64((weeks * hoursPerWeek))) - days := int(remainingHours / 24) - hours := int(remainingHours % 24) + const hoursPerWeek int = 168 + weeks := int(clusterAge.Hours()) / hoursPerWeek + remainingHours := int(clusterAge.Hours()) - weeks * hoursPerWeek + days := remainingHours / 24 + hours := remainingHours % 24 return weeks, days, hours } // Return the cluster age as a formatted string func PrintFormattedAge (clusterAge time.Duration) (string) { weeks, days, hours := FormatAge(clusterAge) - formattedString := "" + var formattedString string if weeks > 0 { - formattedString += fmt.Sprint(weeks) + " weeks " + formattedString = fmt.Sprintf("%d weeks ", weeks) } if days > 0 { - formattedString += fmt.Sprint(days) + " days " + formattedString = fmt.Sprintf("%s %d days ", formattedString, days) } if hours > 0 { - formattedString += fmt.Sprint(hours) + " hours" + formattedString = fmt.Sprintf("%s %d hours", formattedString, hours) } return formattedString } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_clusters.go b/scripts/cluster-scanner/internal/search_clusters.go index b917c69..e3b988f 100644 --- a/scripts/cluster-scanner/internal/search_clusters.go +++ b/scripts/cluster-scanner/internal/search_clusters.go @@ -10,7 +10,6 @@ import ( func SearchClusters (paletteClient *client.V1Client, logger *slog.Logger) ([]*models.V1SpectroClusterSummary, error) { // Search for clusters logger.Info("Searching for clusters...") - clusters, err := paletteClient.SearchClusterSummaries(&models.V1SearchFilterSpec{}, []*models.V1SearchFilterSortSpec{}) + return paletteClient.SearchClusterSummaries(&models.V1SearchFilterSpec{}, []*models.V1SearchFilterSortSpec{}) - return clusters, err } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go index 0e5e3b0..8a23bab 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters.go +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -8,16 +8,14 @@ import ( "github.com/spectrocloud/palette-sdk-go/api/models" ) -func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog.Logger) bool { +func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog.Logger) (bool, string) { // Variable to keep track of any found clusters older than 24h foundOldCluster := false // List the clusters that are running for more than 24h for _, cluster := range clusters { - creationTime := cluster.Metadata.CreationTimestamp - timeValue := time.Time(creationTime) - timeNow := time.Now() - clusterAge := timeNow.Sub(timeValue) + timeValue := time.Time(cluster.Metadata.CreationTimestamp) + clusterAge := time.Now().Sub(timeValue) if clusterAge.Hours() >= 24 { foundOldCluster = true @@ -26,5 +24,5 @@ func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog } } - return foundOldCluster + return foundOldCluster, message } \ No newline at end of file From 46bac635a740b6727b1878772c8fe59607682fb9 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Wed, 30 Oct 2024 15:03:50 -0400 Subject: [PATCH 03/15] docs: address more suggestions, improve comments --- scripts/cluster-scanner/README.md | 12 +++++------ .../cluster-scanner/internal/format_age.go | 6 ++++-- .../internal/search_clusters.go | 9 +++----- .../internal/search_old_clusters.go | 11 ++++++---- scripts/cluster-scanner/main.go | 21 ++++++++++++------- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/scripts/cluster-scanner/README.md b/scripts/cluster-scanner/README.md index 5082f86..55c2ae4 100644 --- a/scripts/cluster-scanner/README.md +++ b/scripts/cluster-scanner/README.md @@ -12,22 +12,22 @@ The **Cluster Scanner** tool uses the Palette Go SDK to scan your Palette enviro ## Usage -1. Open a terminal window and export your Palette URL. Replace `` with your Palette URL, for example, `console.spectrocloud.com`. +1. Open a terminal window and export your Palette URL. Substitute `` with your Palette URL, for example, `console.spectrocloud.com`. ```shell - export PALETTE_HOST= + export PALETTE_HOST= ``` -2. Export your Palette API key. Replace `` with your Palette API key. +2. Export your Palette API key. Replace `` with your Palette API key. ```shell - export PALETTE_API_KEY= + export PALETTE_API_KEY= ``` -3. To scan a specific project, export the project's UID. Replace `` with the Palette project UID. If no project is provided, the tool assumes a tenant scope and scans clusters across all projects. +3. To scan a specific project, export the project's UID. Substitute `` with the Palette project UID. If no project is provided, the tool assumes a tenant scope and scans clusters across all projects. ```shell - export PALETTE_PROJECT_UID= + export PALETTE_PROJECT_UID= ``` 4. Navigate to the `cluster-scanner` folder. diff --git a/scripts/cluster-scanner/internal/format_age.go b/scripts/cluster-scanner/internal/format_age.go index 40c3394..cfce59c 100644 --- a/scripts/cluster-scanner/internal/format_age.go +++ b/scripts/cluster-scanner/internal/format_age.go @@ -5,7 +5,7 @@ import ( "time" ) -// Format the cluster age using weeks, days, and hours +// FormatAge returns the age of a cluster as the number of weeks, days, and hours. func FormatAge (clusterAge time.Duration) (int, int, int) { const hoursPerWeek int = 168 weeks := int(clusterAge.Hours()) / hoursPerWeek @@ -15,7 +15,9 @@ func FormatAge (clusterAge time.Duration) (int, int, int) { return weeks, days, hours } -// Return the cluster age as a formatted string +// PrintFormattedAge returns a formatted string representation of the cluster's age. +// It uses FormatAge to calculate the age in weeks, days, and hours and formats +// the output accordingly. func PrintFormattedAge (clusterAge time.Duration) (string) { weeks, days, hours := FormatAge(clusterAge) var formattedString string diff --git a/scripts/cluster-scanner/internal/search_clusters.go b/scripts/cluster-scanner/internal/search_clusters.go index e3b988f..c351336 100644 --- a/scripts/cluster-scanner/internal/search_clusters.go +++ b/scripts/cluster-scanner/internal/search_clusters.go @@ -1,15 +1,12 @@ package internal import ( - "log/slog" - "github.com/spectrocloud/palette-sdk-go/api/models" "github.com/spectrocloud/palette-sdk-go/client" ) -func SearchClusters (paletteClient *client.V1Client, logger *slog.Logger) ([]*models.V1SpectroClusterSummary, error) { - // Search for clusters - logger.Info("Searching for clusters...") +// SearchClusters retrieves a list of cluster summaries from the Palette client. +func SearchClusters (paletteClient *client.V1Client) ([]*models.V1SpectroClusterSummary, error) { + // Search for clusters using the provided Palette client with default filters and sorting. return paletteClient.SearchClusterSummaries(&models.V1SearchFilterSpec{}, []*models.V1SearchFilterSortSpec{}) - } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go index 8a23bab..d11366c 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters.go +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -8,19 +8,22 @@ import ( "github.com/spectrocloud/palette-sdk-go/api/models" ) +// SearchOldClusters checks for clusters that have been running for more than 24 hours. +// It returns a boolean indicating if any old clusters were found and a message +// with the clusters' information. func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog.Logger) (bool, string) { - // Variable to keep track of any found clusters older than 24h + // Variable to keep track of any found clusters older than 24 hours foundOldCluster := false + var message string - // List the clusters that are running for more than 24h + // Iterate through the clusters to find those running for more than 24 hours for _, cluster := range clusters { timeValue := time.Time(cluster.Metadata.CreationTimestamp) clusterAge := time.Now().Sub(timeValue) if clusterAge.Hours() >= 24 { foundOldCluster = true - message := fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, PrintFormattedAge(clusterAge)) - logger.Info(message) + message = fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, PrintFormattedAge(clusterAge)) } } diff --git a/scripts/cluster-scanner/main.go b/scripts/cluster-scanner/main.go index c0716a8..2fff572 100644 --- a/scripts/cluster-scanner/main.go +++ b/scripts/cluster-scanner/main.go @@ -10,25 +10,27 @@ import ( func main() { - // Read environment variables + // Read environment variables required to initialize the Palette client host := os.Getenv("PALETTE_HOST") apiKey := os.Getenv("PALETTE_API_KEY") projectUid := os.Getenv("PALETTE_PROJECT_UID") + // Initialize a logger for structured output logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - + // Ensure the required environment variables are provided if host == "" || apiKey == "" { logger.Error("You must specify the PALETTE_HOST and PALETTE_API_KEY environment variables.") os.Exit(1) } - // Initialize a Palette client + // Initialize a Palette client with the provided host and API key paletteClient := client.New( client.WithPaletteURI(host), client.WithAPIKey(apiKey), ) + // Set the scope for the client based on wether the project UID is provided if projectUid != "" { client.WithScopeProject(projectUid)(paletteClient) logger.Info("Setting scope to project.") @@ -37,20 +39,25 @@ func main() { logger.Info("Setting scope to tenant.") } - // Search for clusters - clusters, err := internal.SearchClusters(paletteClient, logger) + // Search for clusters using the Palette client and the SearchClusters function + logger.Info("Searching for clusters...") + clusters, err := internal.SearchClusters(paletteClient) if err != nil { logger.Error("Failed to search cluster summaries", "error", err) os.Exit(2) } - // Check active clusters + // Check if any clusters were found if len(clusters) == 0 { logger.Warn("There are no clusters running.") return } - foundOldCluster := internal.SearchOldClusters(clusters, logger) + // Check for clusters running for more than 24 hours using the SearchOldClusters function + foundOldCluster, message := internal.SearchOldClusters(clusters, logger) + if message != "" { + logger.Info(message) + } if !foundOldCluster { logger.Info("There are no clusters running for more than 24 hours.") From ab1ef407083da2faf6cc10eaab1f23e64c3c8fcc Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Thu, 14 Nov 2024 21:07:10 -0500 Subject: [PATCH 04/15] docs: add tests and code improvement --- .../cluster-scanner/internal/format_age.go | 57 ++++--- .../internal/format_age_test.go | 148 ++++++++++++++++++ .../internal/search_clusters.go | 2 +- .../internal/search_old_clusters.go | 27 ++-- .../internal/search_old_clusters_test.go | 112 +++++++++++++ scripts/cluster-scanner/main.go | 84 +++++----- 6 files changed, 355 insertions(+), 75 deletions(-) create mode 100644 scripts/cluster-scanner/internal/format_age_test.go create mode 100644 scripts/cluster-scanner/internal/search_old_clusters_test.go diff --git a/scripts/cluster-scanner/internal/format_age.go b/scripts/cluster-scanner/internal/format_age.go index cfce59c..5c42131 100644 --- a/scripts/cluster-scanner/internal/format_age.go +++ b/scripts/cluster-scanner/internal/format_age.go @@ -5,30 +5,45 @@ import ( "time" ) +type FormattedAge struct { + Days, Weeks, Hours int +} + // FormatAge returns the age of a cluster as the number of weeks, days, and hours. -func FormatAge (clusterAge time.Duration) (int, int, int) { - const hoursPerWeek int = 168 - weeks := int(clusterAge.Hours()) / hoursPerWeek - remainingHours := int(clusterAge.Hours()) - weeks * hoursPerWeek - days := remainingHours / 24 - hours := remainingHours % 24 - return weeks, days, hours +func FormatAge(clusterAge time.Duration) (*FormattedAge, error) { + const hoursPerWeek int = 168 + hoursAge := int(clusterAge.Hours()) + if hoursAge < 0 { + return nil, fmt.Errorf("%v is less than zero hours", hoursAge) + } + weeks := hoursAge / hoursPerWeek + remainingHours := hoursAge - weeks*hoursPerWeek + days := remainingHours / 24 + hours := remainingHours % 24 + return &FormattedAge{ + Days: days, + Weeks: weeks, + Hours: hours, + }, nil } -// PrintFormattedAge returns a formatted string representation of the cluster's age. +// GetFormattedAge returns a formatted string representation of the cluster's age. // It uses FormatAge to calculate the age in weeks, days, and hours and formats // the output accordingly. -func PrintFormattedAge (clusterAge time.Duration) (string) { - weeks, days, hours := FormatAge(clusterAge) - var formattedString string - if weeks > 0 { - formattedString = fmt.Sprintf("%d weeks ", weeks) - } - if days > 0 { - formattedString = fmt.Sprintf("%s %d days ", formattedString, days) - } - if hours > 0 { - formattedString = fmt.Sprintf("%s %d hours", formattedString, hours) - } - return formattedString +func GetFormattedAge(clusterAge time.Duration) (*string, error) { + fa, err := FormatAge(clusterAge) + if err != nil { + return nil, err + } + var formattedString string + if fa.Weeks > 0 { + formattedString = fmt.Sprintf("%d weeks ", fa.Weeks) + } + if fa.Days > 0 { + formattedString = fmt.Sprintf("%s%d days ", formattedString, fa.Days) + } + if fa.Hours > 0 { + formattedString = fmt.Sprintf("%s%d hours", formattedString, fa.Hours) + } + return &formattedString, nil } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/format_age_test.go b/scripts/cluster-scanner/internal/format_age_test.go new file mode 100644 index 0000000..441b436 --- /dev/null +++ b/scripts/cluster-scanner/internal/format_age_test.go @@ -0,0 +1,148 @@ +package internal_test + +import ( + "errors" + "testing" + "time" + + "github.com/spectrocloud/palette-samples/cluster-scanner/internal" +) + +type TestCase struct { + input string + expectedHours, expectedDays, expectedWeeks int + expectedError error + output string +} + +func TestFormatAge (t *testing.T) { + tc := map[string]TestCase{ + "positive duration":{ + input:"2h", + expectedHours:2, + }, + "duration with weeks": { + input: "336h", + expectedWeeks:2, + }, + "duration with days": { + input: "48h", + expectedDays:2, + }, + "duration with hours and days": { + input: "74h", + expectedDays:3, + expectedHours:2, + }, + "duration with hours, days, and weeks": { + input: "914h", + expectedDays:3, + expectedHours:2, + expectedWeeks:5, + }, + "zero duration":{ + input: "0h", + output: "", + }, + "negative duration":{ + input:"-2h", + expectedError: errors.New("-2 is less than zero hours"), + }, + } + + for key, value := range tc { + t.Run(key, func(t *testing.T){ + duration, err := time.ParseDuration(value.input) + if err != nil { + t.Errorf("Error parsing duration: %v", err) + } + + fa, err := internal.FormatAge(duration) + + if value.expectedError != nil && err == nil { + t.Errorf("Expected an error, but got none") + } + if value.expectedError == nil && err != nil { + t.Errorf("Unexpected error: %v", err) + } + if value.expectedError != nil && err != nil { + if value.expectedError.Error() != err.Error() { + t.Errorf("Errors do not match: got %v, want %v", err, value.expectedError.Error()) + } + } + if fa != nil { + if fa.Weeks != value.expectedWeeks { + t.Errorf("got %d weeks, want %d weeks", fa.Weeks, value.expectedWeeks) + } + if fa.Days != value.expectedDays { + t.Errorf("got %d days, want %d days", fa.Days, value.expectedDays) + } + if fa.Hours != value.expectedHours { + t.Errorf("got %d hours, want %d hours", fa.Hours, value.expectedHours) + } + } + }) + } +} + +func TestGetFormattedAge(t *testing.T) { + tc := map[string]TestCase{ + "duration with hours": { + input: "2h", + output: "2 hours", + }, + "duration with weeks": { + input: "336h", + output: "2 weeks ", + }, + "duration with days": { + input: "48h", + output: "2 days ", + }, + "duration with hours and days": { + input: "74h", + output: "3 days 2 hours", + }, + "duration with hours, days, and weeks": { + input: "914h", + output: "5 weeks 3 days 2 hours", + }, + "negative duration": { + input: "-2h", + expectedError: errors.New("-2 is less than zero hours"), + }, + "zero duration": { + input: "0h", + output: "", + }, + } + + for key, value := range tc { + t.Run(key, func(t *testing.T) { + duration, err := time.ParseDuration(value.input) + if err != nil { + t.Errorf("Error parsing duration: %v", err) + } + + faString, err := internal.GetFormattedAge(duration) + + if value.expectedError != nil && err == nil { + t.Errorf("Expected an error, but got none") + } + if value.expectedError == nil && err != nil { + t.Errorf("Unexpected error: %v", err) + } + if value.expectedError != nil && err != nil { + if value.expectedError.Error() != err.Error() { + t.Errorf("Errors do not match: got %v, want %v", err, value.expectedError.Error()) + } + } + if faString != nil { + if *faString != value.output { + t.Errorf("got '%s', want '%s'", *faString, value.output) + } + } + }) + } + +} diff --git a/scripts/cluster-scanner/internal/search_clusters.go b/scripts/cluster-scanner/internal/search_clusters.go index c351336..70a4cb7 100644 --- a/scripts/cluster-scanner/internal/search_clusters.go +++ b/scripts/cluster-scanner/internal/search_clusters.go @@ -6,7 +6,7 @@ import ( ) // SearchClusters retrieves a list of cluster summaries from the Palette client. -func SearchClusters (paletteClient *client.V1Client) ([]*models.V1SpectroClusterSummary, error) { +func SearchClusters(paletteClient *client.V1Client) ([]*models.V1SpectroClusterSummary, error) { // Search for clusters using the provided Palette client with default filters and sorting. return paletteClient.SearchClusterSummaries(&models.V1SearchFilterSpec{}, []*models.V1SearchFilterSortSpec{}) } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go index d11366c..30130ab 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters.go +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -2,19 +2,17 @@ package internal import ( "fmt" - "log/slog" "time" "github.com/spectrocloud/palette-sdk-go/api/models" ) // SearchOldClusters checks for clusters that have been running for more than 24 hours. -// It returns a boolean indicating if any old clusters were found and a message -// with the clusters' information. -func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog.Logger) (bool, string) { - // Variable to keep track of any found clusters older than 24 hours - foundOldCluster := false - var message string +// It takes a list of cluster summaries and returns a slice of messages with the +// information of the clusters that were found. +func SearchOldClusters(clusters []*models.V1SpectroClusterSummary) ([]string, error) { + // Slice of strings to keep track of any found clusters older than 24 hours + var messageArray []string // Iterate through the clusters to find those running for more than 24 hours for _, cluster := range clusters { @@ -22,10 +20,13 @@ func SearchOldClusters (clusters []*models.V1SpectroClusterSummary, logger *slog clusterAge := time.Now().Sub(timeValue) if clusterAge.Hours() >= 24 { - foundOldCluster = true - message = fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, PrintFormattedAge(clusterAge)) - } + age, err := GetFormattedAge(clusterAge) + if err != nil { + return nil, err + } + message := fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, *age) + messageArray = append(messageArray, message) + } } - - return foundOldCluster, message -} \ No newline at end of file + return messageArray, nil +} diff --git a/scripts/cluster-scanner/internal/search_old_clusters_test.go b/scripts/cluster-scanner/internal/search_old_clusters_test.go new file mode 100644 index 0000000..aa08fae --- /dev/null +++ b/scripts/cluster-scanner/internal/search_old_clusters_test.go @@ -0,0 +1,112 @@ +package internal_test + +import ( + "testing" + "time" + + "github.com/spectrocloud/palette-samples/cluster-scanner/internal" + "github.com/spectrocloud/palette-sdk-go/api/models" +) + +type TestCaseSearch struct { + input []*models.V1SpectroClusterSummary + output []string + expectedError error +} + +func TestSearchOldClusters (t *testing.T) { + now := time.Now() + parseTime := func (now time.Time, input string) time.Time { + creationDate, _ := time.ParseDuration(input) + age := now.Add(creationDate) + return age + } + + createSummary := func (creationTime string, name string, cloudType string) *models.V1SpectroClusterSummary { + return &models.V1SpectroClusterSummary { + Metadata: &models.V1ObjectMeta{ + CreationTimestamp: models.V1Time(parseTime(now, creationTime)), + Name: name, + }, + SpecSummary: &models.V1SpectroClusterSummarySpecSummary{ + CloudConfig: &models.V1CloudConfigMeta{ + CloudType: cloudType, + }, + }, + } + } + + tc := map[string]TestCaseSearch{ + "one cluster older than 24h":{ + input: []*models.V1SpectroClusterSummary{ + createSummary("-30h", "test-cluster", "aws"), + }, + output: []string{ + "The aws cluster named test-cluster has been running for 1 days 6 hours. Are you sure you need this cluster?", + }, + }, + "two clusters older than 24h":{ + input: []*models.V1SpectroClusterSummary{ + createSummary("-30h", "test-cluster", "aws"), + createSummary("-50h", "test-cluster-azure", "azure"), + }, + output: []string{ + "The aws cluster named test-cluster has been running for 1 days 6 hours. Are you sure you need this cluster?", + "The azure cluster named test-cluster-azure has been running for 2 days 2 hours. Are you sure you need this cluster?", + }, + }, + "one cluster with 24h":{ + input: []*models.V1SpectroClusterSummary{ + createSummary("-24h", "test-cluster", "aws"), + }, + output: []string{ + "The aws cluster named test-cluster has been running for 1 days . Are you sure you need this cluster?", + }, + }, + "one cluster with less than 24h":{ + input: []*models.V1SpectroClusterSummary{ + createSummary("-20h", "test-cluster", "aws"), + }, + output: nil, + }, + "one cluster with negative age":{ + input: []*models.V1SpectroClusterSummary{ + createSummary("20h", "test-cluster", "aws"), + }, + output: nil, + }, + } + + for key, value := range tc { + t.Run(key, func(t *testing.T) { + clustersGot, err := internal.SearchOldClusters(value.input) + + if value.expectedError != nil && err == nil { + t.Errorf("Expected an error, but got none") + } + if value.expectedError == nil && err != nil { + t.Errorf("Unexpected error: %v", err) + } + if value.expectedError != nil && err != nil { + if value.expectedError.Error() != err.Error() { + t.Errorf("Errors do not match: got %v, want %v", err, value.expectedError.Error()) + } + } + if len(clustersGot) != len(value.output) { + t.Errorf("Lenghts mismatch. Got %v elements, want %v elements", len(clustersGot), len(value.output)) + } + for _, got := range clustersGot { + found := false + for _, want := range value.output { + if got == want { + found = true + return + } + } + if !found { + t.Errorf("Got unexpected value %v, want %v", got, value.output) + } + } + }) + } +} \ No newline at end of file diff --git a/scripts/cluster-scanner/main.go b/scripts/cluster-scanner/main.go index 2fff572..3e1d736 100644 --- a/scripts/cluster-scanner/main.go +++ b/scripts/cluster-scanner/main.go @@ -10,56 +10,60 @@ import ( func main() { - // Read environment variables required to initialize the Palette client - host := os.Getenv("PALETTE_HOST") - apiKey := os.Getenv("PALETTE_API_KEY") - projectUid := os.Getenv("PALETTE_PROJECT_UID") + // Read environment variables required to initialize the Palette client. + host := os.Getenv("PALETTE_HOST") + apiKey := os.Getenv("PALETTE_API_KEY") + projectUid := os.Getenv("PALETTE_PROJECT_UID") - // Initialize a logger for structured output - logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + // Initialize a logger for structured output. + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) - // Ensure the required environment variables are provided - if host == "" || apiKey == "" { - logger.Error("You must specify the PALETTE_HOST and PALETTE_API_KEY environment variables.") - os.Exit(1) - } + // Ensure the required environment variables are provided. + if host == "" || apiKey == "" { + logger.Error("You must specify the PALETTE_HOST and PALETTE_API_KEY environment variables.") + os.Exit(1) + } - // Initialize a Palette client with the provided host and API key - paletteClient := client.New( - client.WithPaletteURI(host), - client.WithAPIKey(apiKey), - ) + // Initialize a Palette client with the provided host and API key. + paletteClient := client.New( + client.WithPaletteURI(host), + client.WithAPIKey(apiKey), + ) - // Set the scope for the client based on wether the project UID is provided - if projectUid != "" { - client.WithScopeProject(projectUid)(paletteClient) - logger.Info("Setting scope to project.") + // Set the scope for the client based on wether the project UID is provided. + if projectUid != "" { + client.WithScopeProject(projectUid)(paletteClient) + logger.Info("Setting scope to project.") } else { - client.WithScopeTenant()(paletteClient) - logger.Info("Setting scope to tenant.") - } + client.WithScopeTenant()(paletteClient) + logger.Info("Setting scope to tenant.") + } - // Search for clusters using the Palette client and the SearchClusters function - logger.Info("Searching for clusters...") - clusters, err := internal.SearchClusters(paletteClient) - if err != nil { - logger.Error("Failed to search cluster summaries", "error", err) + // Search for clusters using the Palette client and the SearchClusters function. + logger.Info("Searching for clusters...") + clusters, err := internal.SearchClusters(paletteClient) + if err != nil { + logger.Error("Failed to search cluster summaries", "error", err) os.Exit(2) } - // Check if any clusters were found + // Check if any clusters were found. if len(clusters) == 0 { logger.Warn("There are no clusters running.") return } - - // Check for clusters running for more than 24 hours using the SearchOldClusters function - foundOldCluster, message := internal.SearchOldClusters(clusters, logger) - if message != "" { - logger.Info(message) - } - - if !foundOldCluster { - logger.Info("There are no clusters running for more than 24 hours.") - } -} \ No newline at end of file + + // Check for clusters running for more than 24 hours using the SearchOldClusters function. + messageArray, err := internal.SearchOldClusters(clusters) + if err != nil { + logger.Error("Failed to search for old clusters", "error", err) + os.Exit(2) + } + if len(messageArray) != 0 { + for _, message := range messageArray { + logger.Info(message) + } + return + } + logger.Info("There are no clusters running for more than 24 hours.") +} From 7070e3758787ddf25ce171dd3132fff0ebf23558 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 18 Nov 2024 21:33:38 -0500 Subject: [PATCH 05/15] docs: update log message --- scripts/cluster-scanner/internal/search_old_clusters.go | 2 +- .../cluster-scanner/internal/search_old_clusters_test.go | 8 ++++---- scripts/cluster-scanner/main.go | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go index 30130ab..f9fd30b 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters.go +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -24,7 +24,7 @@ func SearchOldClusters(clusters []*models.V1SpectroClusterSummary) ([]string, er if err != nil { return nil, err } - message := fmt.Sprintf("The %s cluster named %s has been running for %s. Are you sure you need this cluster?", cluster.SpecSummary.CloudConfig.CloudType, cluster.Metadata.Name, *age) + message := fmt.Sprintf("-'%s' cluster (%s) - %s", cluster.Metadata.Name, cluster.SpecSummary.CloudConfig.CloudType, *age) messageArray = append(messageArray, message) } } diff --git a/scripts/cluster-scanner/internal/search_old_clusters_test.go b/scripts/cluster-scanner/internal/search_old_clusters_test.go index aa08fae..9997212 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters_test.go +++ b/scripts/cluster-scanner/internal/search_old_clusters_test.go @@ -42,7 +42,7 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-30h", "test-cluster", "aws"), }, output: []string{ - "The aws cluster named test-cluster has been running for 1 days 6 hours. Are you sure you need this cluster?", + "-'test-cluster' cluster (aws) - 1 days 6 hours", }, }, "two clusters older than 24h":{ @@ -51,8 +51,8 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-50h", "test-cluster-azure", "azure"), }, output: []string{ - "The aws cluster named test-cluster has been running for 1 days 6 hours. Are you sure you need this cluster?", - "The azure cluster named test-cluster-azure has been running for 2 days 2 hours. Are you sure you need this cluster?", + "-'test-cluster' cluster (aws) - 1 days 6 hours", + "-'test-cluster-azure' cluster (azure) - 2 days 2 hours", }, }, "one cluster with 24h":{ @@ -60,7 +60,7 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-24h", "test-cluster", "aws"), }, output: []string{ - "The aws cluster named test-cluster has been running for 1 days . Are you sure you need this cluster?", + "-'test-cluster' cluster (aws) - 1 days ", }, }, "one cluster with less than 24h":{ diff --git a/scripts/cluster-scanner/main.go b/scripts/cluster-scanner/main.go index 3e1d736..1db3978 100644 --- a/scripts/cluster-scanner/main.go +++ b/scripts/cluster-scanner/main.go @@ -60,6 +60,7 @@ func main() { os.Exit(2) } if len(messageArray) != 0 { + logger.Info("The following clusters have been running for more than 24 hours. Please delete them if they are no longer needed.") for _, message := range messageArray { logger.Info(message) } From e3e059884f4f0ae445a28b047815bf20037c603a Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Tue, 19 Nov 2024 22:05:40 -0500 Subject: [PATCH 06/15] docs: address review suggestions --- scripts/cluster-scanner/internal/search_old_clusters.go | 2 +- .../cluster-scanner/internal/search_old_clusters_test.go | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/scripts/cluster-scanner/internal/search_old_clusters.go b/scripts/cluster-scanner/internal/search_old_clusters.go index f9fd30b..f648f5b 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters.go +++ b/scripts/cluster-scanner/internal/search_old_clusters.go @@ -17,7 +17,7 @@ func SearchOldClusters(clusters []*models.V1SpectroClusterSummary) ([]string, er // Iterate through the clusters to find those running for more than 24 hours for _, cluster := range clusters { timeValue := time.Time(cluster.Metadata.CreationTimestamp) - clusterAge := time.Now().Sub(timeValue) + clusterAge := time.Since(timeValue) if clusterAge.Hours() >= 24 { age, err := GetFormattedAge(clusterAge) diff --git a/scripts/cluster-scanner/internal/search_old_clusters_test.go b/scripts/cluster-scanner/internal/search_old_clusters_test.go index 9997212..5871fff 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters_test.go +++ b/scripts/cluster-scanner/internal/search_old_clusters_test.go @@ -16,6 +16,7 @@ type TestCaseSearch struct { func TestSearchOldClusters (t *testing.T) { now := time.Now() + parseTime := func (now time.Time, input string) time.Time { creationDate, _ := time.ParseDuration(input) age := now.Add(creationDate) @@ -96,16 +97,12 @@ func TestSearchOldClusters (t *testing.T) { t.Errorf("Lenghts mismatch. Got %v elements, want %v elements", len(clustersGot), len(value.output)) } for _, got := range clustersGot { - found := false for _, want := range value.output { if got == want { - found = true return } } - if !found { - t.Errorf("Got unexpected value %v, want %v", got, value.output) - } + t.Errorf("Got unexpected value %v, want %v", got, value.output) } }) } From ba236bbe6f6ad910ee101eb46d333091850bc5b7 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 2 Dec 2024 11:30:02 -0500 Subject: [PATCH 07/15] docs: add first draft of GH actions pipeline --- .github/workflows/cluster_scanner.yaml | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/cluster_scanner.yaml diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml new file mode 100644 index 0000000..3ce1797 --- /dev/null +++ b/.github/workflows/cluster_scanner.yaml @@ -0,0 +1,44 @@ +name: Cluster Scanner + +on: + # For test purposes. Trigger it on pushes to the cluster-scanner branch. + push: + branches: + - cluster-scanner + # It runs every Friday, 9:30 UTC. The schedule job only works if the workflow is available in the main branch. + #schedule: + # - cron: "30 9 * * 5" + workflow_dispatch: + +env: + PALETTE_API_KEY: ${{ secrets.SPECTROCLOUD_API_KEY }} + PALETTE_HOST: ${{ secrets.SPECTROCLOUD_HOST }} + PALETTE_PROJECT_UID: ${{ secrets.PALETTE_PROJECT_UID }} + +jobs: + scan-clusters: + name: cluster-scan + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set Up Go + uses: actions/setup-go@v5 + with: + go-version-file: "palette-samples/scripts/cluster-scanner/go.mod" + + - name: Install dependencies + run: | + cd scripts/cluster-scanner/ + go get ./... + + - name: Execute tests + run: go test ./... + + - name: Execute the cluster scanner application + run: go run . + + # Get the output + # Ignore Slack output if there are no clusters running + # Send slack notification if output is different than what was planned From c14ec6d5c083970dd79f4d59bbc5144fa869e68c Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 2 Dec 2024 11:46:10 -0500 Subject: [PATCH 08/15] docs: add debug steps --- .github/workflows/cluster_scanner.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index 3ce1797..c5eabbc 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -23,10 +23,15 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 + - name: Debug working dir + run: | + pwd + ls + - name: Set Up Go uses: actions/setup-go@v5 with: - go-version-file: "palette-samples/scripts/cluster-scanner/go.mod" + go-version-file: "scripts/cluster-scanner/go.mod" - name: Install dependencies run: | From fb99692955469a84a5555952152e3dacf9ee0a6c Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 2 Dec 2024 12:26:09 -0500 Subject: [PATCH 09/15] docs: fix test step --- .github/workflows/cluster_scanner.yaml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index c5eabbc..3dcb31b 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -23,11 +23,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - - name: Debug working dir - run: | - pwd - ls - - name: Set Up Go uses: actions/setup-go@v5 with: @@ -39,10 +34,14 @@ jobs: go get ./... - name: Execute tests - run: go test ./... + run: | + cd scripts/cluster-scanner/ + go test ./... - name: Execute the cluster scanner application - run: go run . + run: | + cd scripts/cluster-scanner/ + go run . # Get the output # Ignore Slack output if there are no clusters running From 9fa04efe2cf381c981162c8a484521acd596052f Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 2 Dec 2024 14:40:07 -0500 Subject: [PATCH 10/15] docs: fix variable name --- .github/workflows/cluster_scanner.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index 3dcb31b..ba9220e 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -11,8 +11,8 @@ on: workflow_dispatch: env: - PALETTE_API_KEY: ${{ secrets.SPECTROCLOUD_API_KEY }} - PALETTE_HOST: ${{ secrets.SPECTROCLOUD_HOST }} + PALETTE_API_KEY: ${{ secrets.PALETTE_API_KEY }} + PALETTE_HOST: ${{ secrets.PALETTE_HOST }} PALETTE_PROJECT_UID: ${{ secrets.PALETTE_PROJECT_UID }} jobs: From 1ebb7ec59d615587627284a69ef9895697e5b051 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 23 Dec 2024 13:49:34 -0300 Subject: [PATCH 11/15] docs: add slack notification --- .github/workflows/cluster_scanner.yaml | 39 ++++++++++++++++++-------- .gitignore | 3 ++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index ba9220e..6e35426 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -29,20 +29,37 @@ jobs: go-version-file: "scripts/cluster-scanner/go.mod" - name: Install dependencies - run: | - cd scripts/cluster-scanner/ - go get ./... + working-directory: scripts/cluster-scanner + run: go get ./... - name: Execute tests - run: | - cd scripts/cluster-scanner/ - go test ./... + working-directory: scripts/cluster-scanner + run: go test ./... - name: Execute the cluster scanner application + id: run_cluster_scanner + working-directory: scripts/cluster-scanner + run: go run . | tee result.log + + - name: Check if clusters have been running for more than 24 hours + id: check_running_clusters + working-directory: scripts/cluster-scanner run: | - cd scripts/cluster-scanner/ - go run . + if grep -q "The following clusters have been running for more than 24 hours" result.log; then + echo "CLUSTERS_FOUND=true" >> $GITHUB_ENV + fi - # Get the output - # Ignore Slack output if there are no clusters running - # Send slack notification if output is different than what was planned + - name: Send Slack notification + if: env.CLUSTERS_FOUND == 'true' + uses: rtCamp/action-slack-notify@v2.3.2 + with: + SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} + SLACK_TOKEN: ${{ secrets.SLACK_PRIVATE_TEAM_TOKEN }} + #SLACK_WEBHOOK: ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }} + SLACK_USERNAME: "spectromate" + SLACK_ICON_EMOJI: ":robot_face:" + SLACK_COLOR: "good" + SLACKIFY_MARKDOWN: true + ENABLE_ESCAPES: true + MESSAGE: | + $(cat scripts/cluster-scanner/result.log) diff --git a/.gitignore b/.gitignore index 9b8a46e..7242c7f 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ override.tf.json # Ignore CLI configuration files .terraformrc terraform.rc + +# Ignore secrets +.secrets \ No newline at end of file From d78aa93500f15799c01fab13b1219fba2ffe18b2 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 23 Dec 2024 13:59:26 -0300 Subject: [PATCH 12/15] docs: change slack notification to webhook --- .github/workflows/cluster_scanner.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index 6e35426..cdfb7b0 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -53,13 +53,12 @@ jobs: if: env.CLUSTERS_FOUND == 'true' uses: rtCamp/action-slack-notify@v2.3.2 with: - SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} - SLACK_TOKEN: ${{ secrets.SLACK_PRIVATE_TEAM_TOKEN }} - #SLACK_WEBHOOK: ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }} + #SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} + #SLACK_TOKEN: ${{ secrets.SLACK_PRIVATE_TEAM_TOKEN }} + SLACK_WEBHOOK: ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }} SLACK_USERNAME: "spectromate" - SLACK_ICON_EMOJI: ":robot_face:" + SLACK_ICON_EMOJI: ":clock1:" SLACK_COLOR: "good" SLACKIFY_MARKDOWN: true - ENABLE_ESCAPES: true MESSAGE: | $(cat scripts/cluster-scanner/result.log) From 5aaeed7ec326070b3daa63991028fc02ae64277b Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Mon, 23 Dec 2024 18:04:59 -0300 Subject: [PATCH 13/15] docs: fix multiline log message --- .github/workflows/cluster_scanner.yaml | 31 +++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index cdfb7b0..bbc1e97 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -1,13 +1,13 @@ name: Cluster Scanner on: - # For test purposes. Trigger it on pushes to the cluster-scanner branch. + # For test purposes. Trigger the pipeline on pushes to the cluster-scanner branch. push: branches: - cluster-scanner # It runs every Friday, 9:30 UTC. The schedule job only works if the workflow is available in the main branch. - #schedule: - # - cron: "30 9 * * 5" + schedule: + - cron: "30 9 * * 5" workflow_dispatch: env: @@ -28,37 +28,38 @@ jobs: with: go-version-file: "scripts/cluster-scanner/go.mod" - - name: Install dependencies + - name: Install Dependencies working-directory: scripts/cluster-scanner run: go get ./... - - name: Execute tests + - name: Execute Tests working-directory: scripts/cluster-scanner run: go test ./... - - name: Execute the cluster scanner application - id: run_cluster_scanner + - name: Launch the Application and Capture Logs working-directory: scripts/cluster-scanner run: go run . | tee result.log - - name: Check if clusters have been running for more than 24 hours - id: check_running_clusters + - name: Get Clusters with More Than 24 Hours and Format Output working-directory: scripts/cluster-scanner run: | if grep -q "The following clusters have been running for more than 24 hours" result.log; then echo "CLUSTERS_FOUND=true" >> $GITHUB_ENV + { + echo 'LOG_MESSAGE<> "$GITHUB_ENV" fi - - name: Send Slack notification + - name: Send Slack Notification if: env.CLUSTERS_FOUND == 'true' uses: rtCamp/action-slack-notify@v2.3.2 - with: - #SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} - #SLACK_TOKEN: ${{ secrets.SLACK_PRIVATE_TEAM_TOKEN }} + env: SLACK_WEBHOOK: ${{ secrets.SLACK_PRIVATE_TEAM_WEBHOOK }} SLACK_USERNAME: "spectromate" SLACK_ICON_EMOJI: ":clock1:" SLACK_COLOR: "good" SLACKIFY_MARKDOWN: true - MESSAGE: | - $(cat scripts/cluster-scanner/result.log) + ENABLE_ESCAPES: true + SLACK_MESSAGE: ${{ env.LOG_MESSAGE }} From 04561ee34045c24fd22de51d9e44014ecf479457 Mon Sep 17 00:00:00 2001 From: Carolina Delwing Rosa Date: Tue, 24 Dec 2024 11:11:30 -0300 Subject: [PATCH 14/15] docs: improve log message, add emojis --- .github/workflows/cluster_scanner.yaml | 3 +-- scripts/cluster-scanner/internal/search_old_clusters.go | 2 +- .../cluster-scanner/internal/search_old_clusters_test.go | 8 ++++---- scripts/cluster-scanner/main.go | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cluster_scanner.yaml b/.github/workflows/cluster_scanner.yaml index bbc1e97..5292987 100644 --- a/.github/workflows/cluster_scanner.yaml +++ b/.github/workflows/cluster_scanner.yaml @@ -43,7 +43,7 @@ jobs: - name: Get Clusters with More Than 24 Hours and Format Output working-directory: scripts/cluster-scanner run: | - if grep -q "The following clusters have been running for more than 24 hours" result.log; then + if grep -q "The following clusters have been running" result.log; then echo "CLUSTERS_FOUND=true" >> $GITHUB_ENV { echo 'LOG_MESSAGE< Date: Tue, 24 Dec 2024 11:39:34 -0300 Subject: [PATCH 15/15] docs: use short form of duration --- scripts/cluster-scanner/internal/format_age.go | 12 +++++++++--- scripts/cluster-scanner/internal/format_age_test.go | 10 +++++----- .../internal/search_old_clusters_test.go | 8 ++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scripts/cluster-scanner/internal/format_age.go b/scripts/cluster-scanner/internal/format_age.go index 5c42131..bc9a75d 100644 --- a/scripts/cluster-scanner/internal/format_age.go +++ b/scripts/cluster-scanner/internal/format_age.go @@ -37,13 +37,19 @@ func GetFormattedAge(clusterAge time.Duration) (*string, error) { } var formattedString string if fa.Weeks > 0 { - formattedString = fmt.Sprintf("%d weeks ", fa.Weeks) + formattedString = fmt.Sprintf("%dw", fa.Weeks) } if fa.Days > 0 { - formattedString = fmt.Sprintf("%s%d days ", formattedString, fa.Days) + if len(formattedString) > 0 { + formattedString += " " + } + formattedString = fmt.Sprintf("%s%dd", formattedString, fa.Days) } if fa.Hours > 0 { - formattedString = fmt.Sprintf("%s%d hours", formattedString, fa.Hours) + if len(formattedString) > 0 { + formattedString += " " + } + formattedString = fmt.Sprintf("%s%dh", formattedString, fa.Hours) } return &formattedString, nil } \ No newline at end of file diff --git a/scripts/cluster-scanner/internal/format_age_test.go b/scripts/cluster-scanner/internal/format_age_test.go index 441b436..1ced4c0 100644 --- a/scripts/cluster-scanner/internal/format_age_test.go +++ b/scripts/cluster-scanner/internal/format_age_test.go @@ -89,23 +89,23 @@ func TestGetFormattedAge(t *testing.T) { tc := map[string]TestCase{ "duration with hours": { input: "2h", - output: "2 hours", + output: "2h", }, "duration with weeks": { input: "336h", - output: "2 weeks ", + output: "2w", }, "duration with days": { input: "48h", - output: "2 days ", + output: "2d", }, "duration with hours and days": { input: "74h", - output: "3 days 2 hours", + output: "3d 2h", }, "duration with hours, days, and weeks": { input: "914h", - output: "5 weeks 3 days 2 hours", + output: "5w 3d 2h", }, "negative duration": { input: "-2h", diff --git a/scripts/cluster-scanner/internal/search_old_clusters_test.go b/scripts/cluster-scanner/internal/search_old_clusters_test.go index 43f0c56..c7ef524 100644 --- a/scripts/cluster-scanner/internal/search_old_clusters_test.go +++ b/scripts/cluster-scanner/internal/search_old_clusters_test.go @@ -43,7 +43,7 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-30h", "test-cluster", "aws"), }, output: []string{ - "❗️aws cluster 'test-cluster' - 1 days 6 hours ⏳", + "❗️aws cluster 'test-cluster' - 1d 6h ⏳", }, }, "two clusters older than 24h":{ @@ -52,8 +52,8 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-50h", "test-cluster-azure", "azure"), }, output: []string{ - "❗️aws cluster 'test-cluster' - 1 days 6 hours ⏳", - "❗️azure cluster 'test-cluster-azure' - 2 days 2 hours ⏳", + "❗️aws cluster 'test-cluster' - 1d 6h ⏳", + "❗️azure cluster 'test-cluster-azure' - 2d 2h ⏳", }, }, "one cluster with 24h":{ @@ -61,7 +61,7 @@ func TestSearchOldClusters (t *testing.T) { createSummary("-24h", "test-cluster", "aws"), }, output: []string{ - "❗️aws cluster 'test-cluster' - 1 days ⏳", + "❗️aws cluster 'test-cluster' - 1d ⏳", }, }, "one cluster with less than 24h":{