-
Notifications
You must be signed in to change notification settings - Fork 101
/
utils.go
149 lines (126 loc) · 3.51 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
import (
"errors"
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/bsiegert/ranges"
)
var ErrParsePorts = fmt.Errorf("cannot parse ports argument")
// ParseVars returns parsed and validated slice of strings with
// variables names that will be used for monitoring.
func ParseVars(vars string) ([]VarName, error) {
if vars == "" {
return nil, errors.New("no vars specified")
}
ss := strings.FieldsFunc(vars, func(r rune) bool { return r == ',' })
var ret []VarName
for _, s := range ss {
ret = append(ret, VarName(s))
}
return ret, nil
}
// BaseCommand returns cleaned command name from Cmdline array.
//
// I.e. "./some.service/binary.name -arg 1 -arg" will be "binary.name".
func BaseCommand(cmdline []string) string {
if len(cmdline) == 0 {
return ""
}
return filepath.Base(cmdline[0])
}
// flattenURLs returns URLs for the given addr and set of ports.
//
// Note, rawurl shouldn't contain port, as port will be appended.
func flattenURLs(rawurl string, ports []string) ([]url.URL, error) {
var urls []url.URL
// Add http by default
if !strings.HasPrefix(rawurl, "http") {
rawurl = fmt.Sprintf("http://%s", rawurl)
}
// Make URL from rawurl
baseURL, err := url.Parse(rawurl)
if err != nil {
return nil, err
}
if baseURL.Path == "" {
baseURL.Path = DefaultEndpoint
}
// Create new URL for each port
for _, port := range ports {
u := *baseURL
u.Host = fmt.Sprintf("%s:%s", u.Host, port)
urls = append(urls, u)
}
return urls, nil
}
// ParsePorts parses and flattens comma-separated ports/urls into URLs slice
func ParsePorts(s string) ([]url.URL, error) {
var urls []url.URL
fields := strings.FieldsFunc(s, func(r rune) bool { return r == ',' })
for _, field := range fields {
rawurl, portsRange := extractURLAndPorts(field)
ports, err := parseRange(portsRange)
if err != nil {
return nil, ErrParsePorts
}
purls, err := flattenURLs(rawurl, ports)
if err != nil {
return nil, ErrParsePorts
}
urls = append(urls, purls...)
}
return urls, nil
}
// extractUrlAndPorts attempts to split url and extract raw url
// for the single port and range of ports to parse.
//
// i.e. "http://name:1234-1236/_endpoint" would return "http://name/_endpoint" and
// "1234-1236"
func extractURLAndPorts(s string) (string, string) {
var rawurl, ports string
parts := strings.Split(s, ":")
switch len(parts) {
case 1:
// "1234-234"
rawurl = "http://localhost"
ports = parts[0]
case 2:
// "localhost:1234"
rawurl, ports = parts[0], parts[1]
default:
// "https://user:[email protected]:1234" or "http://name:1234-1236/_endpoint"
// construct endpoint from the first part of URI, before ports appera
rawurl = strings.Join(parts[:len(parts)-1], ":")
// get either "1234-1235" or "1234-1235/_endpoint"
lastPart := parts[len(parts)-1]
// try to find endpoint and attach it to rawurl
fields := strings.SplitN(lastPart, "/", 2)
ports = fields[0]
if len(fields) > 1 {
rawurl = fmt.Sprintf("%s/%s", rawurl, fields[1])
}
}
return rawurl, ports
}
// parseRange flattens port ranges, such as "1234-1240,1333"
func parseRange(s string) ([]string, error) {
portsInt, err := ranges.Parse(s)
if err != nil {
return nil, err
}
var ports []string
for _, port := range portsInt {
ports = append(ports, fmt.Sprintf("%d", port))
}
return ports, nil
}
// NewURL returns net.URL for the given port, with expvarmon defaults set.
func NewURL(port string) url.URL {
return url.URL{
Scheme: "http",
Host: fmt.Sprintf("localhost:%s", port),
Path: "/debug/vars",
}
}