-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add command to delete stale elastic indices #12
Draft
nicksantamaria
wants to merge
20
commits into
main
Choose a base branch
from
feature/add-elastic-client-changes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
4aabb04
Added elasticsearch client and creation date lookup poc.
GROwen 57f9aba
Added handling for ES API response.
GROwen 532a72c
Added timestamp comparison and dry-run flag.
GROwen 73dd0e7
Added deleteRequest and dry-run handling.
GROwen d285229
Refactored confirmation.
GROwen 56d9a75
Fixed cmd reference in readme.
GROwen 9116ee5
Refactored Elastic auth config.
GROwen 67b7140
Fixed operator for status code conditional.
GROwen 7db3f4f
Commit Nick's suggested usage text.
GROwen c86b1b7
Updated inputs. Added flag for index age.
GROwen e15917c
Refactored out envconfig
nicksantamaria 89ce20c
Refactored output
nicksantamaria 88cc404
Refactored flags
nicksantamaria e4bb6ac
Renamed update-stale.go
nicksantamaria af2a2b0
Merge remote-tracking branch 'origin/main' into feature/add-elastic-c…
GROwen b26869b
[add-elastic-client-changes] Fixed naming of deployment pkg.
GROwen 2625766
[add-elastic-client-changes] Added logic to handle indices with App S…
GROwen 5cd0857
Added hash to project name lookup.
GROwen 2d01d26
Updated placeholders to match data types
GROwen 2bc5909
Fixed string comparisons.
GROwen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package project_map | ||
package deployment | ||
|
||
import ( | ||
"encoding/json" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
package elastic_cloud | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
"time" | ||
|
||
"github.com/dpc-sdp/bay-cli/internal/helpers" | ||
elasticsearch "github.com/elastic/go-elasticsearch/v8" | ||
"github.com/elastic/go-elasticsearch/v8/esapi" | ||
"github.com/manifoldco/promptui" | ||
errors "github.com/pkg/errors" | ||
"github.com/urfave/cli/v2" | ||
lagoon_client "github.com/uselagoon/machinery/api/lagoon/client" | ||
"github.com/uselagoon/machinery/api/schema" | ||
) | ||
|
||
type IndexSettings struct { | ||
IndexItem struct { | ||
IndexDetail struct { | ||
CreationDate string `json:"creation_date"` | ||
} `json:"index"` | ||
} `json:"settings"` | ||
} | ||
|
||
type Indices map[string]IndexSettings | ||
|
||
type AliasAttr struct { | ||
IsHidden bool `json:"is_hidden"` | ||
} | ||
|
||
type Aliases struct { | ||
Aliases map[string]AliasAttr `json:"aliases"` | ||
} | ||
|
||
func DeleteStaleIndices(c *cli.Context) error { | ||
force := c.Bool("force") | ||
apiKey := c.String("deployment-api-key") | ||
cloudId := c.String("deployment-id") | ||
age := c.Int64("age") | ||
deleteList := make([]string, 0) | ||
|
||
hashes, err := NewHashMap() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
client, err := elasticsearch.NewClient(elasticsearch.Config{APIKey: apiKey, CloudID: cloudId}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
settings, err := esapi.IndicesGetSettingsRequest{FilterPath: []string{"*.settings.index.creation_date"}}.Do(context.TODO(), client) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
indicesList := Indices{} | ||
|
||
if err := json.NewDecoder(settings.Body).Decode(&indicesList); err != nil { | ||
return errors.Wrap(err, "Error parsing the response body") | ||
} else { | ||
for k, i := range indicesList { | ||
if strings.Contains(k, "elasticsearch_index") { | ||
a, err := esapi.IndicesGetAliasRequest{Index: []string{k}}.Do(context.TODO(), client) | ||
if err != nil { | ||
return err | ||
} | ||
aliasList := map[string]Aliases{} | ||
if err := json.NewDecoder(a.Body).Decode(&aliasList); err != nil { | ||
return errors.Wrap(err, "Error parsing the response body") | ||
} | ||
now := time.Now().UnixMilli() | ||
created, err := strconv.ParseInt(i.IndexItem.IndexDetail.CreationDate, 10, 64) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
diffInDays := (now - created) / (1000 * 60 * 60 * 24) | ||
|
||
if diffInDays > age { | ||
// Add helper function to compute hash from k. | ||
hash := strings.Split(k, "--")[0] | ||
project, err := hashes.LookupProjectFromHash(hash) | ||
if err != nil { | ||
fmt.Printf("Error looking up project for hash %+v\n", hash) | ||
} | ||
|
||
fmt.Printf("Project: %+v\n", project) | ||
|
||
if len(aliasList[k].Aliases) > 0 { | ||
for aliasName := range aliasList[k].Aliases { | ||
fmt.Fprintf(c.App.Writer, "The index %s is %d days old but will not be deleted because it has an associated alias %s\n", k, diffInDays, aliasName) | ||
} | ||
} else { | ||
fmt.Fprintf(c.App.Writer, "The index %s is %d days old and will be marked for deletion\n", k, diffInDays) | ||
deleteList = append(deleteList, k) | ||
} | ||
} | ||
} | ||
} | ||
if i := len(deleteList); i > 0 { | ||
if force { | ||
fmt.Fprint(c.App.Writer, "Deleting indices marked for deletion.") | ||
statusCode, err := deleteIndices(client, deleteList, i) | ||
if err != nil { | ||
return errors.Wrap(err, "error deleting indices") | ||
} else { | ||
if statusCode == 200 { | ||
fmt.Fprintf(c.App.Writer, "Deletion request failed. Status code %d", statusCode) | ||
} else { | ||
fmt.Fprintf(c.App.Writer, "%+v indices successfully deleted.", i) | ||
} | ||
} | ||
} else { | ||
prompt := promptui.Prompt{ | ||
Label: "Delete indices", | ||
IsConfirm: true, | ||
} | ||
|
||
prompt_result, _ := prompt.Run() | ||
|
||
if prompt_result == "y" { | ||
_, err := deleteIndices(client, deleteList, i) | ||
if err != nil { | ||
return err | ||
} | ||
} else { | ||
fmt.Printf("Operation cancelled.\nThere are %d indices marked for deletion.\n", i) | ||
} | ||
} | ||
} else { | ||
fmt.Printf("No indices meet the criteria for deletion.") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func deleteIndices(client *elasticsearch.Client, deleteList []string, c int) (int, error) { | ||
res, err := esapi.IndicesDeleteRequest{Index: deleteList}.Do(context.TODO(), client) | ||
if err != nil { | ||
return res.StatusCode, err | ||
} else { | ||
if res.StatusCode != 200 { | ||
fmt.Printf("Deletion request failed. Status code %+v", res.StatusCode) | ||
return res.StatusCode, errors.New("non 200 status code") | ||
} else { | ||
fmt.Printf("%+v indices successfully deleted.", c) | ||
return res.StatusCode, nil | ||
} | ||
} | ||
} | ||
|
||
type HashMap struct { | ||
Hashes map[string]string `json:"hashes"` | ||
} | ||
|
||
func (h *HashMap) LookupProjectFromHash(hash string) (string, error) { | ||
if val, ok := h.Hashes[hash]; ok { | ||
return val, nil | ||
} | ||
return "", errors.New("hash not found") | ||
|
||
} | ||
|
||
// Compute the lookup table of hash to project name. | ||
func NewHashMap() (HashMap, error) { | ||
hashMap := HashMap{ | ||
Hashes: make(map[string]string), | ||
} | ||
client, err := helpers.NewLagoonClient(nil) | ||
if err != nil { | ||
return hashMap, err | ||
} | ||
projects, _ := getLagoonProjects(context.TODO(), client) | ||
|
||
for _, project := range projects { | ||
searchHash, _ := getLagoonProjectVar(context.TODO(), client, project.Name, "SEARCH_HASH") | ||
hashMap.Hashes[searchHash] = project.Name | ||
} | ||
fmt.Printf("Hashmap: %+v\n", hashMap) | ||
return hashMap, nil | ||
} | ||
|
||
// Lookup Lagoon projects | ||
func getLagoonProjects(ctx context.Context, client *lagoon_client.Client) ([]schema.ProjectMetadata, error) { | ||
projects := make([]schema.ProjectMetadata, 0) | ||
|
||
err := client.ProjectsByMetadata(ctx, "type", "tide", &projects) | ||
return projects, err | ||
} | ||
|
||
// Lookup Lagoon projects | ||
func getLagoonProjectVar(ctx context.Context, client *lagoon_client.Client, projectName string, varName string) (string, error) { | ||
vars := []schema.EnvKeyValue{} | ||
err := client.GetEnvVariablesByProjectEnvironmentName(ctx, &schema.EnvVariableByProjectEnvironmentNameInput{Project: projectName}, &vars) | ||
for _, v := range vars { | ||
if v.Name == varName { | ||
return strings.ToLower(v.Value), nil | ||
} | ||
} | ||
return "", err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicksantamaria I'd like to refactor this so that indices are grouped by project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could build up a map of elements here, sort the map with something like gosortmap, then iterate over the sorted map in a separate loop.