Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / go.opencensus.io / resource / resource.go
1 // Copyright 2018, OpenCensus Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Package resource provides functionality for resource, which capture
16 // identifying information about the entities for which signals are exported.
17 package resource
18
19 import (
20         "context"
21         "fmt"
22         "os"
23         "regexp"
24         "sort"
25         "strconv"
26         "strings"
27 )
28
29 // Environment variables used by FromEnv to decode a resource.
30 const (
31         EnvVarType   = "OC_RESOURCE_TYPE"
32         EnvVarLabels = "OC_RESOURCE_LABELS"
33 )
34
35 // Resource describes an entity about which identifying information and metadata is exposed.
36 // For example, a type "k8s.io/container" may hold labels describing the pod name and namespace.
37 type Resource struct {
38         Type   string
39         Labels map[string]string
40 }
41
42 // EncodeLabels encodes a labels map to a string as provided via the OC_RESOURCE_LABELS environment variable.
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 // DecodeLabels decodes a serialized label map as used in the OC_RESOURCE_LABELS variable.
63 // A list of labels of the form `<key1>="<value1>",<key2>="<value2>",...` is accepted.
64 // Domain names and paths are accepted as label keys.
65 // Most users will want to use FromEnv instead.
66 func DecodeLabels(s string) (map[string]string, error) {
67         m := map[string]string{}
68         // Ensure a trailing comma, which allows us to keep the regex simpler
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 // FromEnv is a detector that loads resource information from the OC_RESOURCE_TYPE
93 // and OC_RESOURCE_labelS environment variables.
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 // merge resource information from b into a. In case of a collision, a takes precedence.
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         // Labels from resource a overwrite labels from resource b.
130         for k, v := range a.Labels {
131                 res.Labels[k] = v
132         }
133         return res
134 }
135
136 // Detector attempts to detect resource information.
137 // If the detector cannot find resource information, the returned resource is nil but no
138 // error is returned.
139 // An error is only returned on unexpected failures.
140 type Detector func(context.Context) (*Resource, error)
141
142 // MultiDetector returns a Detector that calls all input detectors in order and
143 // merges each result with the previous one. In case a type of label key is already set,
144 // the first set value is takes precedence.
145 // It returns on the first error that a sub-detector encounters.
146 func MultiDetector(detectors ...Detector) Detector {
147         return func(ctx context.Context) (*Resource, error) {
148                 return detectAll(ctx, detectors...)
149         }
150 }
151
152 // detectall calls all input detectors sequentially an merges each result with the previous one.
153 // It returns on the first error that a sub-detector encounters.
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 }