Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

165 řádky
4.7 KiB

  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. // Package resource provides functionality for resource, which capture
  15. // identifying information about the entities for which signals are exported.
  16. package resource
  17. import (
  18. "context"
  19. "fmt"
  20. "os"
  21. "regexp"
  22. "sort"
  23. "strconv"
  24. "strings"
  25. )
  26. // Environment variables used by FromEnv to decode a resource.
  27. const (
  28. EnvVarType = "OC_RESOURCE_TYPE"
  29. EnvVarLabels = "OC_RESOURCE_LABELS"
  30. )
  31. // Resource describes an entity about which identifying information and metadata is exposed.
  32. // For example, a type "k8s.io/container" may hold labels describing the pod name and namespace.
  33. type Resource struct {
  34. Type string
  35. Labels map[string]string
  36. }
  37. // EncodeLabels encodes a labels map to a string as provided via the OC_RESOURCE_LABELS environment variable.
  38. func EncodeLabels(labels map[string]string) string {
  39. sortedKeys := make([]string, 0, len(labels))
  40. for k := range labels {
  41. sortedKeys = append(sortedKeys, k)
  42. }
  43. sort.Strings(sortedKeys)
  44. s := ""
  45. for i, k := range sortedKeys {
  46. if i > 0 {
  47. s += ","
  48. }
  49. s += k + "=" + strconv.Quote(labels[k])
  50. }
  51. return s
  52. }
  53. var labelRegex = regexp.MustCompile(`^\s*([[:ascii:]]{1,256}?)=("[[:ascii:]]{0,256}?")\s*,`)
  54. // DecodeLabels decodes a serialized label map as used in the OC_RESOURCE_LABELS variable.
  55. // A list of labels of the form `<key1>="<value1>",<key2>="<value2>",...` is accepted.
  56. // Domain names and paths are accepted as label keys.
  57. // Most users will want to use FromEnv instead.
  58. func DecodeLabels(s string) (map[string]string, error) {
  59. m := map[string]string{}
  60. // Ensure a trailing comma, which allows us to keep the regex simpler
  61. s = strings.TrimRight(strings.TrimSpace(s), ",") + ","
  62. for len(s) > 0 {
  63. match := labelRegex.FindStringSubmatch(s)
  64. if len(match) == 0 {
  65. return nil, fmt.Errorf("invalid label formatting, remainder: %s", s)
  66. }
  67. v := match[2]
  68. if v == "" {
  69. v = match[3]
  70. } else {
  71. var err error
  72. if v, err = strconv.Unquote(v); err != nil {
  73. return nil, fmt.Errorf("invalid label formatting, remainder: %s, err: %s", s, err)
  74. }
  75. }
  76. m[match[1]] = v
  77. s = s[len(match[0]):]
  78. }
  79. return m, nil
  80. }
  81. // FromEnv is a detector that loads resource information from the OC_RESOURCE_TYPE
  82. // and OC_RESOURCE_labelS environment variables.
  83. func FromEnv(context.Context) (*Resource, error) {
  84. res := &Resource{
  85. Type: strings.TrimSpace(os.Getenv(EnvVarType)),
  86. }
  87. labels := strings.TrimSpace(os.Getenv(EnvVarLabels))
  88. if labels == "" {
  89. return res, nil
  90. }
  91. var err error
  92. if res.Labels, err = DecodeLabels(labels); err != nil {
  93. return nil, err
  94. }
  95. return res, nil
  96. }
  97. var _ Detector = FromEnv
  98. // merge resource information from b into a. In case of a collision, a takes precedence.
  99. func merge(a, b *Resource) *Resource {
  100. if a == nil {
  101. return b
  102. }
  103. if b == nil {
  104. return a
  105. }
  106. res := &Resource{
  107. Type: a.Type,
  108. Labels: map[string]string{},
  109. }
  110. if res.Type == "" {
  111. res.Type = b.Type
  112. }
  113. for k, v := range b.Labels {
  114. res.Labels[k] = v
  115. }
  116. // Labels from resource a overwrite labels from resource b.
  117. for k, v := range a.Labels {
  118. res.Labels[k] = v
  119. }
  120. return res
  121. }
  122. // Detector attempts to detect resource information.
  123. // If the detector cannot find resource information, the returned resource is nil but no
  124. // error is returned.
  125. // An error is only returned on unexpected failures.
  126. type Detector func(context.Context) (*Resource, error)
  127. // MultiDetector returns a Detector that calls all input detectors in order and
  128. // merges each result with the previous one. In case a type of label key is already set,
  129. // the first set value is takes precedence.
  130. // It returns on the first error that a sub-detector encounters.
  131. func MultiDetector(detectors ...Detector) Detector {
  132. return func(ctx context.Context) (*Resource, error) {
  133. return detectAll(ctx, detectors...)
  134. }
  135. }
  136. // detectall calls all input detectors sequentially an merges each result with the previous one.
  137. // It returns on the first error that a sub-detector encounters.
  138. func detectAll(ctx context.Context, detectors ...Detector) (*Resource, error) {
  139. var res *Resource
  140. for _, d := range detectors {
  141. r, err := d(ctx)
  142. if err != nil {
  143. return nil, err
  144. }
  145. res = merge(res, r)
  146. }
  147. return res, nil
  148. }