From c6cb674053a8ed5164950ff44a6cf4acdfc8dd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20=C5=A0trauch?= Date: Sun, 5 Sep 2021 17:30:21 +0200 Subject: [PATCH] Search by labels and prefixes Implemented in API and ctl. --- README.md | 14 +++++++------- client/main.go | 29 ++++++++++++++++++++++++++-- ctl/main.go | 47 ++++++++++++++++++++++++++++++++++++++------- daemon/handlers.go | 3 +++ server/discovery.go | 28 +++++++++++++++++++++++++++ 5 files changed, 105 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e667161..6cf3414 100644 --- a/README.md +++ b/README.md @@ -179,13 +179,13 @@ It uses Go client library also located in this repository. So far the REST API is super simple and it has only two endpoints: ``` -GET / # Same as /v1/discoveries -GET /v1/discovery # Returns current local discovery packet -GET /v1/discoveries # Returns list of all discovered servers and their labels. -GET /v1/discoveries?labels=LABELS # output will be filtered based on one or multiple labels separated by comma -GET /v1/prometheus/:name # Generates output for Prometheus's SD config, name is group of the monitoring services described above. -POST /v1/labels # Add runtime labels that will persist over daemon restarts. Labels should be in the body of the request, one line per one label. -DELETE /v1/labels # Delete runtime labels. One label per line. Can't affect the labels from environment variables or labels added from the LabelPath. +GET / # Same as /v1/discoveries +GET /v1/discovery # Returns current local discovery packet +GET /v1/discoveries # Returns list of all discovered servers and their labels. +GET /v1/discoveries?labels=LABELS&prefixes=PREFIXES # output will be filtered based on one or multiple labels separated by comma or it can search for given prefixes, only one of those will be used +GET /v1/prometheus/:name # Generates output for Prometheus's SD config, name is group of the monitoring services described above. +POST /v1/labels # Add runtime labels that will persist over daemon restarts. Labels should be in the body of the request, one line per one label. +DELETE /v1/labels # Delete runtime labels. One label per line. Can't affect the labels from environment variables or labels added from the LabelPath. ``` If there is an error the error message is returned as plain text. diff --git a/client/main.go b/client/main.go index 8345672..064bb8b 100644 --- a/client/main.go +++ b/client/main.go @@ -115,13 +115,38 @@ func (l *LobbyClient) GetDiscoveries() ([]server.Discovery, error) { } // Find discoveries by their labels -func (l *LobbyClient) FindByLabels(labels server.Labels) (server.Discoveries, error) { +func (l *LobbyClient) FindByLabels(labels server.Labels) ([]server.Discovery, error) { l.init() path := fmt.Sprintf("/v1/discoveries?labels=%s", strings.Join(labels.StringSlice(), ",")) method := "GET" - var discoveries server.Discoveries + var discoveries []server.Discovery + + status, body, err := l.call(method, path, "") + if err != nil { + return discoveries, err + } + if status != 200 { + return discoveries, fmt.Errorf("non-200 response: %s", body) + } + + err = json.Unmarshal([]byte(body), &discoveries) + if err != nil { + return discoveries, fmt.Errorf("response parsing error: %v", err) + } + + return discoveries, nil +} + +// Find discoveries by label prefixes +func (l *LobbyClient) FindByPrefixes(prefixes []string) ([]server.Discovery, error) { + l.init() + + path := fmt.Sprintf("/v1/discoveries?prefixes=%s", strings.Join(prefixes, ",")) + method := "GET" + + var discoveries []server.Discovery status, body, err := l.call(method, path, "") if err != nil { diff --git a/ctl/main.go b/ctl/main.go index 8cb910c..7a36d82 100644 --- a/ctl/main.go +++ b/ctl/main.go @@ -14,10 +14,12 @@ func Usage() { flag.Usage() fmt.Println("") fmt.Println("Commands:") - fmt.Println(" discovery returns discovery packet of the server where the client is connected to") - fmt.Println(" discoveries returns list of all registered discovery packets") - fmt.Println(" labels add LABEL [LABEL] ... adds new runtime labels") - fmt.Println(" labels del LABEL [LABEL] ... deletes runtime labels") + fmt.Println(" discovery returns discovery packet of the server where the client is connected to") + fmt.Println(" discoveries returns list of all registered discovery packets") + fmt.Println(" discoveries labels [LABEL] ... returns list of all registered discovery packets with given labels (OR)") + fmt.Println(" discoveries search [LABEL] ... returns list of all registered discovery packets with given label prefixes (OR)") + fmt.Println(" labels add LABEL [LABEL] ... adds new runtime labels") + fmt.Println(" labels del LABEL [LABEL] ... deletes runtime labels") } func main() { @@ -66,10 +68,41 @@ func main() { switch flag.Args()[0] { case "discoveries": - discoveries, err := client.GetDiscoveries() - if err != nil { - fmt.Println(err) + var discoveries []server.Discovery + var err error + + if len(flag.Args()) > 2 { + if flag.Arg(1) == "labels" { + labels := []server.Label{} + for _, label := range flag.Args()[2:] { + labels = append(labels, server.Label(label)) + } + + discoveries, err = client.FindByLabels(labels) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } else if flag.Arg(1) == "search" { + discoveries, err = client.FindByPrefixes(flag.Args()[2:]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } else { + fmt.Println("ERROR: unknown usage of discoveries arguments") + fmt.Println("") + Usage() + os.Exit(0) + } + } else { + discoveries, err = client.GetDiscoveries() + if err != nil { + fmt.Println(err) + os.Exit(1) + } } + printDiscoveries(discoveries) case "discovery": discovery, err := client.GetDiscovery() diff --git a/daemon/handlers.go b/daemon/handlers.go index 82a1299..0f362b6 100644 --- a/daemon/handlers.go +++ b/daemon/handlers.go @@ -12,12 +12,15 @@ import ( func listHandler(c echo.Context) error { labels := c.QueryParam("labels") + prefixes := c.QueryParam("prefixes") var discoveries []server.Discovery if len(labels) > 0 { labelsFilterSlice := strings.Split(labels, ",") discoveries = discoveryStorage.Filter(labelsFilterSlice) + } else if len(prefixes) > 0 { + discoveries = discoveryStorage.FilterPrefix(strings.Split(prefixes, ",")) } else { discoveries = discoveryStorage.GetAll() } diff --git a/server/discovery.go b/server/discovery.go index f42df26..c125d18 100644 --- a/server/discovery.go +++ b/server/discovery.go @@ -148,6 +148,7 @@ func (d *Discoveries) GetAll() []Discovery { return d.activeServers } +// Filter returns list of discoveries based on given labels func (d *Discoveries) Filter(labelsFilter []string) []Discovery { newSet := []Discovery{} @@ -174,6 +175,33 @@ func (d *Discoveries) Filter(labelsFilter []string) []Discovery { return newSet } +// Filter returns list of discoveries based on given label prefixes. +func (d *Discoveries) FilterPrefix(prefixes []string) []Discovery { + newSet := []Discovery{} + + var found bool + if len(prefixes) > 0 { + for _, discovery := range d.activeServers { + found = false + for _, label := range discovery.Labels { + for _, prefix := range prefixes { + if strings.HasPrefix(label.String(), prefix) { + newSet = append(newSet, discovery) + found = true + break + } + } + if found { + break + } + } + } + + } + + return newSet +} + // Clean checks loops over last check values for each discovery object and removes it if it's passed func (d *Discoveries) Clean() { newSet := []Discovery{}