...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package resource
18
19 import (
20 "context"
21 "fmt"
22 "os"
23 "regexp"
24 "sort"
25 "strconv"
26 "strings"
27 )
28
29
30 const (
31 EnvVarType = "OC_RESOURCE_TYPE"
32 EnvVarLabels = "OC_RESOURCE_LABELS"
33 )
34
35
36
37 type Resource struct {
38 Type string
39 Labels map[string]string
40 }
41
42
43 func EncodeLabels(labels map[string]string) string {
44 sortedKeys := make([]string, 0, len(labels))
45 for k := range labels {
46 sortedKeys = append(sortedKeys, k)
47 }
48 sort.Strings(sortedKeys)
49
50 s := ""
51 for i, k := range sortedKeys {
52 if i > 0 {
53 s += ","
54 }
55 s += k + "=" + strconv.Quote(labels[k])
56 }
57 return s
58 }
59
60 var labelRegex = regexp.MustCompile(`^\s*([[:ascii:]]{1,256}?)=("[[:ascii:]]{0,256}?")\s*,`)
61
62
63
64
65
66 func DecodeLabels(s string) (map[string]string, error) {
67 m := map[string]string{}
68
69 s = strings.TrimRight(strings.TrimSpace(s), ",") + ","
70
71 for len(s) > 0 {
72 match := labelRegex.FindStringSubmatch(s)
73 if len(match) == 0 {
74 return nil, fmt.Errorf("invalid label formatting, remainder: %s", s)
75 }
76 v := match[2]
77 if v == "" {
78 v = match[3]
79 } else {
80 var err error
81 if v, err = strconv.Unquote(v); err != nil {
82 return nil, fmt.Errorf("invalid label formatting, remainder: %s, err: %s", s, err)
83 }
84 }
85 m[match[1]] = v
86
87 s = s[len(match[0]):]
88 }
89 return m, nil
90 }
91
92
93
94 func FromEnv(context.Context) (*Resource, error) {
95 res := &Resource{
96 Type: strings.TrimSpace(os.Getenv(EnvVarType)),
97 }
98 labels := strings.TrimSpace(os.Getenv(EnvVarLabels))
99 if labels == "" {
100 return res, nil
101 }
102 var err error
103 if res.Labels, err = DecodeLabels(labels); err != nil {
104 return nil, err
105 }
106 return res, nil
107 }
108
109 var _ Detector = FromEnv
110
111
112 func merge(a, b *Resource) *Resource {
113 if a == nil {
114 return b
115 }
116 if b == nil {
117 return a
118 }
119 res := &Resource{
120 Type: a.Type,
121 Labels: map[string]string{},
122 }
123 if res.Type == "" {
124 res.Type = b.Type
125 }
126 for k, v := range b.Labels {
127 res.Labels[k] = v
128 }
129
130 for k, v := range a.Labels {
131 res.Labels[k] = v
132 }
133 return res
134 }
135
136
137
138
139
140 type Detector func(context.Context) (*Resource, error)
141
142
143
144
145
146 func MultiDetector(detectors ...Detector) Detector {
147 return func(ctx context.Context) (*Resource, error) {
148 return detectAll(ctx, detectors...)
149 }
150 }
151
152
153
154 func detectAll(ctx context.Context, detectors ...Detector) (*Resource, error) {
155 var res *Resource
156 for _, d := range detectors {
157 r, err := d(ctx)
158 if err != nil {
159 return nil, err
160 }
161 res = merge(res, r)
162 }
163 return res, nil
164 }
165
View as plain text