...

Source file src/go.opencensus.io/resource/resource.go

Documentation: go.opencensus.io/resource

     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  }
   165  

View as plain text