From 27d9fdbe9f51c6b6b3bb12a9d9e21910dc6ebd68 Mon Sep 17 00:00:00 2001 From: jiqb Date: Sun, 7 Jul 2019 11:16:42 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 + .goreleaser.yml | 52 +++++++++ Dockerfile | 3 + Makefile | 48 ++++++++ README.md | 204 ++++++++++++++++++++++++++++++++ VERSION | 1 + cli/app.go | 162 ++++++++++++++++++++++++++ elasticsearch/elasdx.go | 251 ++++++++++++++++++++++++++++++++++++++++ go.mod | 14 +++ go.sum | 20 ++++ main.go | 15 +++ 11 files changed, 773 insertions(+) create mode 100644 .gitignore create mode 100644 .goreleaser.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 VERSION create mode 100644 cli/app.go create mode 100644 elasticsearch/elasdx.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..accc8e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +vendor +templates \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..eb19b21 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,52 @@ +# @format + +project_name: elasdx + +env: + - GO111MODULE=on + +before: + hooks: + - go mod download + +builds: + - env: + - CGO_ENABLED=0 + binary: elasdx + ldflags: + - -X github.com/LGUG2Z/elasdx/cli.Version={{ .Version }} -X github.com/LGUG2Z/elasdx/cli.Commit={{ .Commit }} + goarch: + - amd64 + +dockers: + - image_templates: + - "lgug2z/elasdx:{{ .Tag }}" + - "lgug2z/elasdx:{{ .Major }}" + - "lgug2z/elasdx:{{ .Major }}.{{ .Minor }}" + - "lgug2z/elasdx:latest" + +brews: + - name: elasdx + github: + owner: LGUG2Z + name: homebrew-tap + folder: Formula + homepage: "https://github.com/LGUG2Z/elasdx" + description: "An ElasticSearch index template updating, reindexing and cleanup tool" + test: | + system "#{bin}/elasdx --version" + install: | + bin.install "elasdx" + +checksum: + name_template: "checksums.txt" + +snapshot: + name_template: "{{ .Tag }}-snapshot" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a07c14f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM scratch +COPY elasdx / +ENTRYPOINT ["/elasdx"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..03fe455 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# ############################################################################## # +# Makefile for Golang Project # +# Includes cross-compiling, installation, cleanup # +# Adapted from https://gist.github.com/cjbarker/5ce66fcca74a1928a155cfb3fea8fac4 # +# ############################################################################## # + +# Check for required command tools to build or stop immediately +EXECUTABLES = git go yq kustomize +K := $(foreach exec,$(EXECUTABLES),\ + $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH))) + +ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + +BINARY=elasdx +VERSION=`cat VERSION` +COMMIT=`git rev-parse HEAD` +PLATFORMS=darwin linux +ARCHITECTURES=amd64 + +# Setup linker flags option for build that interoperate with variable names in src code +LDFLAGS=-ldflags "-X github.com/LGUG2Z/elasdx/cli.Version=${VERSION} -X github.com/LGUG2Z/elasdx/cli.Commit=${COMMIT}" + +default: build + +all: clean build_all install + +build: + go build ${LDFLAGS} -o ${BINARY} + +build_all: + $(foreach GOOS, $(PLATFORMS),\ + $(foreach GOARCH, $(ARCHITECTURES), $(shell export GOOS=$(GOOS); export GOARCH=$(GOARCH); go build -v -o $(BINARY)-$(GOOS)-$(GOARCH)))) + +install: + go install ${LDFLAGS} + +fmt: + goimports -w cli elasticsearch main.go + +release: + goreleaser --rm-dist + +# Remove only what we've created +clean: + find ${ROOT_DIR} -name '${BINARY}[-?][a-zA-Z0-9]*[-?][a-zA-Z0-9]*' -delete + rm -rf dist + +.PHONY: check clean install build_all all diff --git a/README.md b/README.md new file mode 100644 index 0000000..b98c758 --- /dev/null +++ b/README.md @@ -0,0 +1,204 @@ +# elasdx +`elasdx` is a command line tool to make ElasticSearch index template updates, reindexing and cleanup a more simple and +CI-friendly process. + +## Installation +### Go Get +```bash +go get -u github.com/LGUG2Z/elasdx +cd ${GOPATH}/src/github.com/LGUG2Z/elasdx +make install +``` + +### Binary +```bash +export ELASDX_VERSION=x.x.x +curl -Lo elasdx.tar.gz https://github.com/LGUG2Z/elasdx/releases/download/v${ELASDX_VERSION}/elasdx_${ELASDX_VERSION}_linux_amd64.tar.gz +mkdir elasdx && tar -xzf elasdx.tar.gz -C elasdx && mv elasdx/elasdx /usr/local/bin/elasdx && chmod +x /usr/local/bin/elasdx && rm -rf elasdx.tar.gz elasdx +``` + +### Docker +```bash +export ELASDX_VERSION=x.x.x +docker pull lgug2z/elasdx:${ELASDX_VERSION} +docker pull lgug2z/elasdx:latest +``` + +### Homebrew +```bash +brew tap LGUG2Z/tap +brew install LGUG2Z/tap/elasdx +``` + +## Background +[ElasticSearch](https://www.elastic.co/products/elasticsearch) provides a comprehensive API to do just about anything, +so it's unfortunate that until now I have always been stuck writing `bash` scripts full of `curl` commands to interact +with it. One of the most painful things to do with `bash` and `curl` when it comes to ElasticSearch is reindexing. + +Typically this process requires the following steps: + +* Make sure environment variables are set correctly +* [Update a template](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html): +```bash +curl -X PUT "localhost:9200/_template/template_1" -H 'Content-Type: application/json' -d' +{ + "index_patterns": ["te*", "bar*"], + "settings": { + "number_of_shards": 1 + }, + "mappings": { + "_source": { + "enabled": false + }, + "properties": { + "host_name": { + "type": "keyword" + }, + "created_at": { + "type": "date", + "format": "EEE MMM dd HH:mm:ss Z yyyy" + } + } + } +} +' +``` + +* [Create a new index](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html): +```bash +curl -X PUT "localhost:9200/twitter" +``` + +* Update the settings on the index for +[bulk reindexing](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html#bulk): +```bash +curl -X PUT "localhost:9200/twitter/_settings" -H 'Content-Type: application/json' -d' +{ + "index" : { + "refresh_interval" : "-1" + } +} +' +``` + +* [Reindex](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html) from the old index to the new index: +```bash +curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d' +{ + "source": { + "index": "twitter" + }, + "dest": { + "index": "new_twitter" + } +} +' +``` + +* Set the desired refresh interval once the reindexing is done: +```bash +curl -X PUT "localhost:9200/twitter/_settings" -H 'Content-Type: application/json' -d' +{ + "index" : { + "refresh_interval" : "1s" + } +} +' +``` + +* [Associate an alias](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html) with the new index: +```bash +curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d' +{ + "actions" : [ + { "add" : { "index" : "test1", "alias" : "alias1" } } + ] +} +' +``` + +* Remove the old index from an alias: +```bash +curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d' +{ + "actions" : [ + { "remove" : { "index" : "test1", "alias" : "alias1" } } + ] +} +' +``` + +* [Clean up old indices](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-delete-index.html): +```bash +curl -X DELETE "localhost:9200/twitter" +``` + +## Usage +`elasdx` can operate either on a single index template `json` file or on a directory containing multiple index templates. +The ElasticSearch URL can be set using the `ELASDX_URL` environment variable or using the `--url` command line flag. +This value defaults to `http://127.0.0.1:9200`. If using basic auth, the username and password can be provided using the +`ELASDX_USERNAME`, `--username` or `ELASDX_PASSWORD`, `--password` environment variables and flags respectively. + +### Reindex +The `reindex` command assumes either a file or a directory of files named to match the index template and the eventual +alias desired. + +Running `elasdx reindex twitter.json` would create or update an index template with the name `twitter` +and create a new index and associate it with the alias `twitter`. Each new index will take the alias name and append a +timestamp: `twitter-2019-07-07-10:47:17`. + +If provisioning a fresh instance of ElasticSearch, this command will skip trying to reindex from a matching existing +index and simply create a new index and associate it with the correct alias. + +If reindexing, the `--bulk-indexing` flag can optionally be passed to optimise for bulk indexing by setting the refresh +rate of the new index to `-1` before restoring it to either the default refresh rate once the reindexing is finished, or +to the refresh rate defined in the index template `json` file. + +```text +❯ elasdx reindex --bulk-indexing twitter.json +INDEX TEMPLATE Updated twitter +INDEX Created twitter-2019-07-07-10:54:40 +DOCUMENTS Reindexed 100 from twitter-2019-07-07-10:54:33 to twitter-2019-07-07-10:54:40 +ALIAS Removed twitter-2019-07-07-10:54:33 from twitter +ALIAS Added twitter-2019-07-07-10:54:40 to twitter +``` +```json5 +// ❯ curl -X GET "localhost:9200/twitter/_settings" | jq +{ + "twitter-2019-07-07-10:54:40": { + "settings": { + "index": { + "refresh_interval": "10s", // Desired refresh interval from template is set + "number_of_shards": "5", + "provided_name": "twitter-2019-07-07-10:54:40", + "creation_date": "1562492836239", + "number_of_replicas": "1", + "uuid": "d9EkiyzwTMGt5-vaChULcQ", + "version": { + "created": "6020299" + } + } + } + } +} +``` +### Cleanup +The `cleanup` command is intended to work on the same file or directory structure used for the `reindex` command. + +The maximum history of indices to keep is set using the `--max-history` flag, and defaults to `2`, meaning that the +current index associated with the alias and the previous index will be kept, and all other previous indices will be +deleted. If `--max-history` is set to `0`, all indices including the current index associated with the alias will be +deleted; this can be useful during development. + +```text +❯ elasdx cleanup twitter.json +INDEX Deleted twitter-2019-07-07-10:15:31 +INDEX Deleted twitter-2019-07-07-10:15:39 +INDEX Deleted twitter-2019-07-07-10:47:15 +``` + +```text +❯ elasdx cleanup --max-history 0 twitter.json +INDEX Deleted twitter-2019-07-07-10:54:33 +INDEX Deleted twitter-2019-07-07-10:54:40 +``` \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..8a9ecc2 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.1 \ No newline at end of file diff --git a/cli/app.go b/cli/app.go new file mode 100644 index 0000000..49cb8df --- /dev/null +++ b/cli/app.go @@ -0,0 +1,162 @@ +package cli + +import ( + "fmt" + "path/filepath" + "strings" + "time" + + "github.com/LGUG2Z/elasdx/elasticsearch" + "github.com/olivere/elastic" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var ( + Version string + Commit string +) + +func App() *cli.App { + cli.VersionPrinter = func(c *cli.Context) { + fmt.Printf("elasdx version %s (commit %s)\n", c.App.Version, Commit) + } + + app := cli.NewApp() + + app.Name = "elasdx" + app.Usage = "An ElasticSearch index template updating, reindexing and cleanup tool" + app.EnableBashCompletion = true + app.Compiled = time.Now() + app.Version = Version + app.Authors = []cli.Author{{ + Name: "J. Iqbal", + Email: "jade@beamery.com", + }} + + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "url", EnvVar: "ELASDX_URL", Usage: "ElasticSearch URL to connect to", Value: elastic.DefaultURL}, + cli.StringFlag{Name: "username", EnvVar: "ELASDX_USERNAME", Usage: "ElasticSearch basic auth username"}, + cli.StringFlag{Name: "password", EnvVar: "ELASDX_PASSWORD", Usage: "ElasticSearch basic auth password"}, + } + + app.Commands = []cli.Command{ + Reindex(), + Cleanup(), + } + + return app +} + +func getClient(c *cli.Context) (*elastic.Client, error) { + url := c.GlobalString("url") + schemeAndURL := strings.Split(url, "://") + username := c.String("username") + password := c.String("password") + + hasBasicAuth := len(username) > 0 + if hasBasicAuth { + return elastic.NewClient( + elastic.SetScheme(schemeAndURL[0]), + elastic.SetURL(url), + elastic.SetBasicAuth(username, password), + elastic.SetSniff(false), + ) + } + + return elastic.NewClient( + elastic.SetScheme(schemeAndURL[0]), + elastic.SetURL(url), + elastic.SetSniff(false), + ) +} + +func Cleanup() cli.Command { + return cli.Command{ + Name: "cleanup", + Usage: "clean up old indices leaving only the specified maximum index history", + Flags: []cli.Flag{ + cli.IntFlag{Name: "max-history", Usage: "maximum number of index versions to keep (including current version)", Value: 2}, + }, + Action: func(c *cli.Context) error { + if c.NArg() == 0 || c.NArg() > 1 { + fmt.Printf("This command requires a json index template file path or a directory of json index templates\n\n") + cli.ShowCommandHelpAndExit(c, "cleanup", 1) + } + + client, err := getClient(c) + if err != nil { + return errors.Wrap(err, "error connecting to ElasticSearch") + } + + if strings.HasSuffix(c.Args().First(), ".json") { + filePath := c.Args().First() + _, fileName := filepath.Split(filePath) + alias := strings.TrimSuffix(fileName, ".json") + + if err := elasticsearch.CleanupOne(client, alias, c.Int("max-history")); err != nil { + return err + + } + + return nil + } + + directory := c.Args().First() + if err := elasticsearch.CleanupAll(client, directory, c.Int("max-history")); err != nil { + return err + } + + return nil + }, + } +} + +func Reindex() cli.Command { + return cli.Command{ + Name: "reindex", + Usage: "create a new index from an index template and reindex any existing documents", + Flags: []cli.Flag{ + cli.BoolFlag{Name: "bulk-indexing", Usage: "set refresh_interval to -1 when reindexing and revert afterwards"}, + }, + Action: func(c *cli.Context) error { + if c.NArg() == 0 || c.NArg() > 1 { + fmt.Printf("This command requires a json index template file path or a directory of json index templates\n\n") + cli.ShowCommandHelpAndExit(c, "reindex", 1) + } + + client, err := getClient(c) + if err != nil { + return errors.Wrap(err, "error connecting to ElasticSearch") + } + + if strings.HasSuffix(c.Args().First(), ".json") { + filePath := c.Args().First() + newIndex, err := elasticsearch.UpdateTemplateAndCreateNewIndex(client, filePath, c.Bool("bulk-indexing")) + if err != nil { + return err + } + + _, fileName := filepath.Split(filePath) + alias := strings.TrimSuffix(fileName, ".json") + if err = elasticsearch.ReindexOne(client, alias, newIndex); err != nil { + return err + } + + return nil + } + + directory := c.Args().First() + aliasToNewIndex, err := elasticsearch.UpdateTemplatesAndCreateNewIndices(client, directory, c.Bool("bulk-indexing")) + if err != nil { + return err + } + + if err = elasticsearch.ReindexAll(client, aliasToNewIndex); err != nil { + return err + } + + return nil + }, + } +} diff --git a/elasticsearch/elasdx.go b/elasticsearch/elasdx.go new file mode 100644 index 0000000..f84c71d --- /dev/null +++ b/elasticsearch/elasdx.go @@ -0,0 +1,251 @@ +package elasticsearch + +import ( + "context" + "fmt" + "io/ioutil" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/fatih/color" + "github.com/olivere/elastic" + "github.com/pkg/errors" +) + +var IndexTemplatePrefix = "INDEX TEMPLATE " +var IndexPrefix = "INDEX " +var DocumentsPrefix = "DOCUMENTS " +var AliasPrefix = "ALIAS " +var Added = color.GreenString("Added ") +var Created = color.GreenString("Created ") +var Removed = color.RedString("Removed ") +var Deleted = color.RedString("Deleted ") +var Updated = color.GreenString("Updated ") +var Reindexed = color.YellowString("Reindexed ") + +func UpdateTemplateAndCreateNewIndex(client *elastic.Client, filePath string, bulkIndexing bool) (string, error) { + bytes, err := ioutil.ReadFile(filePath) + if err != nil { + return "", errors.Wrapf(err, "failed reading file %s", filePath) + } + + // Our index name, index template + _, fileName := filepath.Split(filePath) + index := strings.TrimSuffix(fileName, ".json") + mapping := string(bytes) + + // Update the template + indexPutTemplate, err := client.IndexPutTemplate(index).BodyString(mapping).Do(context.Background()) + if err != nil { + return "", errors.Wrapf(err, "failed updating index template %s", index) + } + + if !indexPutTemplate.Acknowledged { + fmt.Printf("PUT index/%s not acknowledged\n", index) + } + + // Create a unique time-stamped index + dateSuffix := time.Now().Format("2006-01-02-15:04:05") + indexWithDate := fmt.Sprintf("%s-%s", index, dateSuffix) + + create, err := client.CreateIndex(indexWithDate).Do(context.Background()) + if err != nil { + return "", errors.Wrapf(err, "failed creating index %s", indexWithDate) + } + + bulkIndexingSettings := `{ + "index" : { + "refresh_interval" : "-1" + } +} +` + + if bulkIndexing { + settings, err := client.IndexPutSettings(indexWithDate).BodyString(bulkIndexingSettings).Do(context.Background()) + if err != nil { + return "", errors.Wrapf(err, "failed setting refresh_interval to -1 for index %s", indexWithDate) + } + + if !settings.Acknowledged { + fmt.Println("index settings not acknowledged") + } + } + + if !create.Acknowledged { + fmt.Println("index creation not acknowledged") + } + + fmt.Printf("%s %s %s\n", IndexTemplatePrefix, Updated, index) + fmt.Printf("%s %s %s\n", IndexPrefix, Created, indexWithDate) + return indexWithDate, nil +} + +func UpdateTemplatesAndCreateNewIndices(client *elastic.Client, templatesDir string, bulkIndexing bool) (map[string]string, error) { + files, err := ioutil.ReadDir(templatesDir) + if err != nil { + return nil, errors.Wrapf(err, "failed reading files in directory %s", templatesDir) + } + + aliasToNewIndex := map[string]string{} + + for _, file := range files { + filePath := filepath.Join(templatesDir, file.Name()) + newIndex, err := UpdateTemplateAndCreateNewIndex(client, filePath, bulkIndexing) + if err != nil { + return nil, errors.Wrapf(err, "failed create new index from updated template %s", filePath) + } + + alias := strings.TrimSuffix(file.Name(), ".json") + aliasToNewIndex[alias] = newIndex + } + + return aliasToNewIndex, err +} + +func ReindexOne(client *elastic.Client, alias, newIndex string) error { + // We assume that we are reindexing from an existing index on the alias + reindexingRequired := true + + // Try to find the alias + aliasResult, err := client.Aliases().Alias(alias).Do(context.Background()) + if err != nil { + // It's okay if we can't find the alias; it means we are provisioning from scratch + if !elastic.IsNotFound(err) { + return errors.Wrapf(err, "failed trying to lookup alias %s", alias) + } + + // We don't need to reindex from an old index to our new index when provisioning from scratch + reindexingRequired = false + } + + // If we have data in an existing index that needs to be reindex with the new template to the new index + if reindexingRequired { + // We should only ever get one result if our naming is sensible + indicesFromAlias := aliasResult.IndicesByAlias(alias) + for _, index := range indicesFromAlias { + // Reindex from the existing index as the source to the new index as the destination + src := elastic.NewReindexSource().Index(index) + dst := elastic.NewReindexDestination().Index(newIndex) + refresh, err := client.Reindex().Source(src).Destination(dst).Refresh("true").Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "failed reindexing from %s to %s", index, newIndex) + } + + fmt.Printf("%s %s %d from %s to %s\n", DocumentsPrefix, Reindexed, refresh.Total, index, newIndex) + + // Remove the existing index from the alias + removeAlias, err := client.Alias().Remove(index, alias).Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "failed removing index %s from alias %s", index, alias) + } + + if !removeAlias.Acknowledged { + fmt.Println("alias removal not acknowledged") + } + + fmt.Printf("%s %s %s from %s\n", AliasPrefix, Removed, index, alias) + } + } + + templates, err := client.IndexGetTemplate(alias).Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "failed to retrieve index template %s", alias) + } + + seconds := "null" + + if refreshInterval, ok := templates[alias].Settings["index"].(map[string]interface{})["refresh_interval"]; ok { + seconds = fmt.Sprintf(`"%s"`, refreshInterval.(string)) + } + + // Reset the refresh interval to either whatever is specified in the index template or the default (using null) + resetRefreshInterval := fmt.Sprintf(`{ + "index" : { + "refresh_interval" : %s + } +} +`, seconds) + + settings, err := client.IndexPutSettings(newIndex).BodyString(resetRefreshInterval).Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "failed setting refresh_interval to -1 for index %s", newIndex) + } + + if !settings.Acknowledged { + fmt.Println("index settings not acknowledged") + } + + // Add our new index which has been reindex with the existing data to the alias + addAlias, err := client.Alias().Add(newIndex, alias).Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "failed adding index %s to alias %s", newIndex, alias) + } + + if !addAlias.Acknowledged { + fmt.Println("alias addition not acknowledged") + } + + fmt.Printf("%s %s %s to %s\n", AliasPrefix, Added, newIndex, alias) + + return nil +} + +func ReindexAll(client *elastic.Client, aliasToNewIndex map[string]string) error { + for alias, newIndex := range aliasToNewIndex { + if err := ReindexOne(client, alias, newIndex); err != nil { + return errors.Wrapf(err, "failed reindexing to %s and adding to alias %s", newIndex, alias) + } + } + + return nil +} + +func CleanupOne(client *elastic.Client, indexTemplateName string, maxHistory int) error { + indices, err := client.IndexNames() + if err != nil { + return errors.Wrap(err, "could not get index names") + } + + var matches []string + + for _, index := range indices { + if strings.HasPrefix(index, indexTemplateName) { + matches = append(matches, index) + } + } + + sort.Strings(matches) + + for i := 0; i < len(matches)-maxHistory; i++ { + response, err := client.DeleteIndex(matches[i]).Do(context.Background()) + if err != nil { + return errors.Wrapf(err, "error deleting index %s, not continuing", matches[i]) + } + + if !response.Acknowledged { + fmt.Printf("index deletion not acknowledged") + } + + fmt.Printf("%s %s %s\n", IndexPrefix, Deleted, matches[i]) + } + + return nil +} + +func CleanupAll(client *elastic.Client, templatesDir string, maxHistory int) error { + files, err := ioutil.ReadDir(templatesDir) + if err != nil { + return errors.Wrapf(err, "failed reading files in directory %s", templatesDir) + } + + for _, file := range files { + indexTemplateName := strings.TrimSuffix(file.Name(), ".json") + if err := CleanupOne(client, indexTemplateName, maxHistory); err != nil { + return err + } + } + + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e4e98d0 --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module github.com/LGUG2Z/elasdx + +go 1.12 + +require ( + github.com/fatih/color v1.7.0 + github.com/fortytw2/leaktest v1.3.0 // indirect + github.com/google/go-cmp v0.3.0 // indirect + github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/olivere/elastic v6.2.19+incompatible + github.com/pkg/errors v0.8.1 + github.com/urfave/cli v1.20.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..53060bb --- /dev/null +++ b/go.sum @@ -0,0 +1,20 @@ +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/olivere/elastic v6.2.19+incompatible h1:FYiohzobKD2tib3sHgEHNBSzcw1onqApaumIBBstchQ= +github.com/olivere/elastic v6.2.19+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/main.go b/main.go new file mode 100644 index 0000000..b0ea795 --- /dev/null +++ b/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "os" + + "github.com/LGUG2Z/elasdx/cli" +) + +func main() { + if err := cli.App().Run(os.Args); err != nil { + fmt.Println(err) + os.Exit(1) + } +}