├── README.md ├── go.mod ├── CONTRIBUTING.md ├── LICENSE ├── CHANGELOG.md ├── go.sum ├── main └── erg.go └── erg.go /README.md: -------------------------------------------------------------------------------- 1 | erg 2 | === 3 | 4 | Command line client for querying range servers. 5 | 6 | ## Install 7 | 8 | # OSX 9 | brew tap square/self 10 | brew install erg 11 | 12 | erg --help 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/square/erg 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/droundy/goopt v0.0.0-20170604162106-0b8effe182da 7 | github.com/fvbommel/sortorder v1.0.2 8 | github.com/square/grange v0.0.0-20201015231752-48d66acdd125 9 | ) 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | If you would like to contribute code to this project you can do so through 5 | GitHub by forking the repository and sending a pull request. 6 | 7 | When submitting code, please make every effort to follow existing conventions 8 | and style in order to keep the code as readable as possible. 9 | 10 | Before your code can be accepted into the project you must also sign the 11 | [Individual Contributor License Agreement (CLA)][1]. 12 | 13 | [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2014 Square 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Erg History 2 | 3 | # 1.2.1 - 04 Nov 2016 4 | 5 | * Fix arg clash on -h 6 | * Move to square repo 7 | 8 | # 1.2.0 - 26 Oct 2015 9 | 10 | * Non-zero exit code when the server does not return an HTTP 200 success code. 11 | * Expanded output is naturally sorted (9/10/11 rather than 10/11/9). 12 | 13 | # 1.1.1 - 28 Oct 2014 14 | 15 | * Internal refactor to expose `Erg` type for use by other libraries. 16 | * Add SSL support via `--ssl` flag and `RANGE_SSL` environment variable. 17 | 18 | # 1.1.0 - 16 May 2014 19 | 20 | * Respect `RANGE_HOST` and `RANGE_PORT` environment variables. 21 | 22 | # 1.0.1 - 4 May 2014 23 | 24 | * Output usage when no arguments provided. 25 | * Add `--sort` and `--no-sort` options. 26 | 27 | # 1.0.0 - 4 May 2014 28 | 29 | * Initial release 30 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/deckarep/golang-set v0.0.0-20170202203032-fc8930a5e645 h1:P2qhNT0y1A7XeBvSwkvXV2nZTd28Ax5n709pred+3Ys= 2 | github.com/deckarep/golang-set v0.0.0-20170202203032-fc8930a5e645/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= 3 | github.com/droundy/goopt v0.0.0-20170604162106-0b8effe182da h1:79H+mNJWOObWrQgbkSvvZ3t/D2lKWaTi9mu/v7fNRvg= 4 | github.com/droundy/goopt v0.0.0-20170604162106-0b8effe182da/go.mod h1:ytRJ64WkuW4kf6/tuYqBATBCRFUP8X9+LDtgcvE+koI= 5 | github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= 6 | github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= 7 | github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= 8 | github.com/orcaman/concurrent-map v0.0.0-20160823150647-8bf1e9bacbf6 h1:df8k17NbGFBiBwHnkSCGQ3F9c6TrF8zmGs2jJ9OsQGc= 9 | github.com/orcaman/concurrent-map v0.0.0-20160823150647-8bf1e9bacbf6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= 10 | github.com/square/grange v0.0.0-20201015231752-48d66acdd125 h1:qqbKfPhyZZvtwWDoiuJv80kKTBGdW3I7PSm2YwpLtLA= 11 | github.com/square/grange v0.0.0-20201015231752-48d66acdd125/go.mod h1:jz8buhA9r4b2adTVP/sblJ00sR+MFXCcMELdYVdsJ+k= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 14 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 15 | -------------------------------------------------------------------------------- /main/erg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | 8 | goopt "github.com/droundy/goopt" 9 | "github.com/square/erg" 10 | ) 11 | 12 | var port = goopt.Int([]string{"-p", "--port"}, 8080, "Port to connect to. Can also be set with RANGE_PORT environment variable.") 13 | var host = goopt.String([]string{"-H", "--host"}, "localhost", "Host to connect to. Can also be set with RANGE_HOST environment variable.") 14 | var ssl = goopt.Flag([]string{"-s", "--ssl"}, []string{"--no-ssl"}, 15 | "Use SSL. Can also be set with RANGE_SSL environment variable.", "Don't use SSL") 16 | var expand = goopt.Flag([]string{"-e", "--expand"}, []string{"--no-expand"}, 17 | "Do not compress results", "Compress results (default)") 18 | 19 | func main() { 20 | if envHost := os.Getenv("RANGE_HOST"); len(envHost) > 0 { 21 | *host = envHost 22 | } 23 | 24 | if envSsl := os.Getenv("RANGE_SSL"); len(envSsl) > 0 { 25 | *ssl = true 26 | } 27 | 28 | if envPort := os.Getenv("RANGE_PORT"); len(envPort) > 0 { 29 | x, err := strconv.Atoi(envPort) 30 | if err == nil { 31 | *port = x 32 | } else { 33 | fmt.Fprintf(os.Stderr, "Invalid port in RANGE_PORT: %s\n", envPort) 34 | os.Exit(1) 35 | } 36 | } 37 | goopt.Parse(nil) 38 | 39 | var query string 40 | switch len(goopt.Args) { 41 | case 1: 42 | query = goopt.Args[0] 43 | default: 44 | fmt.Fprintln(os.Stderr, goopt.Usage()) 45 | os.Exit(1) 46 | } 47 | 48 | var e *erg.Erg 49 | 50 | if *ssl { 51 | e = erg.NewWithSsl(*host, *port) 52 | } else { 53 | e = erg.New(*host, *port) 54 | } 55 | 56 | result, err := e.Expand(query) 57 | if err != nil { 58 | fmt.Fprintln(os.Stderr, "Error: ", err.Error()) 59 | os.Exit(1) 60 | } 61 | 62 | if *expand { 63 | for _, node := range result { 64 | fmt.Println(node) 65 | } 66 | } else { 67 | fmt.Println(e.Compress(result)) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /erg.go: -------------------------------------------------------------------------------- 1 | package erg 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | "sort" 11 | 12 | "github.com/fvbommel/sortorder" 13 | "github.com/square/grange" 14 | ) 15 | 16 | // Erg type 17 | // Sort boolean - turn it off/on for sorting on expand 18 | // default is true 19 | type Erg struct { 20 | host string 21 | port int 22 | ssl bool 23 | Sort bool 24 | client *http.Client 25 | } 26 | 27 | // New(address string) returns a new erg 28 | // takes two arguments 29 | // host - hostname default - localhost 30 | // port - port default - 8080 31 | // ssl - use https or not default - false 32 | func New(host string, port int) *Erg { 33 | return &Erg{ 34 | host: host, 35 | port: port, 36 | ssl: false, 37 | Sort: true, 38 | client: &http.Client{}, 39 | } 40 | } 41 | 42 | func NewWithSsl(host string, port int) *Erg { 43 | e := New(host, port) 44 | e.ssl = true 45 | return e 46 | } 47 | 48 | func NewWithClient(client *http.Client, host string, port int, ssl bool) *Erg { 49 | return &Erg{ 50 | host: host, 51 | port: port, 52 | ssl: ssl, 53 | Sort: true, 54 | client: client, 55 | } 56 | } 57 | 58 | // Expand takes a range expression as argument 59 | // and returns an slice of strings as result 60 | // err is set to nil on success 61 | func (e *Erg) Expand(query string) (result []string, err error) { 62 | protocol := "http" 63 | 64 | if e.ssl { 65 | protocol = "https" 66 | } 67 | 68 | resp, err := e.client.Get(fmt.Sprintf("%s://%s:%d/range/list?%s", 69 | protocol, 70 | e.host, 71 | e.port, 72 | url.QueryEscape(query), 73 | )) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | // "When err is nil, resp always contains a non-nil resp.Body. Caller should 79 | // close resp.Body when done reading from it." https://golang.org/pkg/net/http/#Client.Get 80 | defer resp.Body.Close() 81 | 82 | if resp.StatusCode != 200 { 83 | body, readErr := ioutil.ReadAll(resp.Body) 84 | if readErr != nil { 85 | return nil, readErr 86 | } 87 | return nil, errors.New(string(body)) 88 | } 89 | 90 | scanner := bufio.NewScanner(resp.Body) 91 | grangeResult := grange.NewResult() 92 | for scanner.Scan() { 93 | grangeResult.Add(scanner.Text()) 94 | } 95 | 96 | if grangeResult.Cardinality() > 0 { 97 | for node := range grangeResult.Iter() { 98 | result = append(result, node.(string)) 99 | } 100 | if e.Sort { 101 | sort.Sort(sortorder.Natural(result)) 102 | } 103 | } 104 | 105 | return result, nil 106 | } 107 | 108 | // Compress takes a slice of strings as argument 109 | // and returns a compressed form. 110 | func (*Erg) Compress(nodes []string) (result string) { 111 | grangeResult := grange.NewResult() 112 | for _, node := range nodes { 113 | grangeResult.Add(node) 114 | } 115 | return grange.Compress(&grangeResult) 116 | } 117 | --------------------------------------------------------------------------------