github.com/elfadel/cilium@v1.6.12/pkg/labels/labels.go (about)

     1  // Copyright 2016-2019 Authors of Cilium
     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 labels
    16  
    17  import (
    18  	"bytes"
    19  	"crypto/sha512"
    20  	"encoding/json"
    21  	"fmt"
    22  	"net"
    23  	"sort"
    24  	"strings"
    25  )
    26  
    27  const (
    28  	// PathDelimiter is the delimiter used in the labels paths.
    29  	PathDelimiter = "."
    30  
    31  	// IDNameHost is the label used for the hostname ID.
    32  	IDNameHost = "host"
    33  
    34  	// IDNameWorld is the label used for the world ID.
    35  	IDNameWorld = "world"
    36  
    37  	// IDNameCluster is the label used to identify an unspecified endpoint
    38  	// inside the cluster
    39  	IDNameCluster = "cluster"
    40  
    41  	// IDNameHealth is the label used for the local cilium-health endpoint
    42  	IDNameHealth = "health"
    43  
    44  	// IDNameInit is the label used to identify any endpoint that has not
    45  	// received any labels yet.
    46  	IDNameInit = "init"
    47  
    48  	// IDNameNone is the label used to identify no endpoint or other L3 entity.
    49  	// It will never be assigned and this "label" is here for consistency with
    50  	// other Entities.
    51  	IDNameNone = "none"
    52  
    53  	// IDNameUnmanaged is the label used to identify unmanaged endpoints
    54  	IDNameUnmanaged = "unmanaged"
    55  
    56  	// IDNameUnknown is the label used to to identify an endpoint with an
    57  	// unknown identity.
    58  	IDNameUnknown = "unknown"
    59  )
    60  
    61  var (
    62  	// LabelHealth is the label used for health.
    63  	LabelHealth = Labels{IDNameHealth: NewLabel(IDNameHealth, "", LabelSourceReserved)}
    64  )
    65  
    66  const (
    67  	// LabelSourceUnspec is a label with unspecified source
    68  	LabelSourceUnspec = "unspec"
    69  
    70  	// LabelSourceAny is a label that matches any source
    71  	LabelSourceAny = "any"
    72  
    73  	// LabelSourceAnyKeyPrefix is prefix of a "any" label
    74  	LabelSourceAnyKeyPrefix = LabelSourceAny + "."
    75  
    76  	// LabelSourceK8s is a label imported from Kubernetes
    77  	LabelSourceK8s = "k8s"
    78  
    79  	// LabelSourceMesos is a label imported from Mesos
    80  	LabelSourceMesos = "mesos"
    81  
    82  	// LabelSourceK8sKeyPrefix is prefix of a Kubernetes label
    83  	LabelSourceK8sKeyPrefix = LabelSourceK8s + "."
    84  
    85  	// LabelSourceContainer is a label imported from the container runtime
    86  	LabelSourceContainer = "container"
    87  
    88  	// LabelSourceReserved is the label source for reserved types.
    89  	LabelSourceReserved = "reserved"
    90  
    91  	// LabelSourceCIDR is the label source for generated CIDRs.
    92  	LabelSourceCIDR = "cidr"
    93  
    94  	// LabelSourceReservedKeyPrefix is the prefix of a reserved label
    95  	LabelSourceReservedKeyPrefix = LabelSourceReserved + "."
    96  
    97  	// LabelKeyFixedIdentity is the label that can be used to define a fixed
    98  	// identity.
    99  	LabelKeyFixedIdentity = "io.cilium.fixed-identity"
   100  
   101  	// LabelSourceCiliumGenerated is for labels auto-generated by cilium without
   102  	// user input
   103  	LabelSourceCiliumGenerated = "cilium-generated"
   104  )
   105  
   106  // Label is the cilium's representation of a container label.
   107  type Label struct {
   108  	Key   string `json:"key"`
   109  	Value string `json:"value,omitempty"`
   110  	// Source can be one of the values present in const.go (e.g.: LabelSourceContainer)
   111  	Source string `json:"source"`
   112  }
   113  
   114  // Labels is a map of labels where the map's key is the same as the label's key.
   115  type Labels map[string]Label
   116  
   117  // GetPrintableModel turns the Labels into a sorted list of strings
   118  // representing the labels, with CIDRs deduplicated (ie, only provide the most
   119  // specific CIDR).
   120  func (l Labels) GetPrintableModel() (res []string) {
   121  	cidr := ""
   122  	prefixLength := 0
   123  	for _, v := range l {
   124  		if v.Source == LabelSourceCIDR {
   125  			vStr := strings.Replace(v.String(), "-", ":", -1)
   126  			prefix := strings.Replace(v.Key, "-", ":", -1)
   127  			_, ipnet, _ := net.ParseCIDR(prefix)
   128  			ones, _ := ipnet.Mask.Size()
   129  			if ones > prefixLength {
   130  				cidr = vStr
   131  				prefixLength = ones
   132  			}
   133  			continue
   134  		}
   135  		res = append(res, v.String())
   136  	}
   137  	if cidr != "" {
   138  		res = append(res, cidr)
   139  	}
   140  
   141  	sort.Strings(res)
   142  	return res
   143  }
   144  
   145  // String returns the map of labels as human readable string
   146  func (l Labels) String() string {
   147  	return strings.Join(l.GetPrintableModel(), ",")
   148  }
   149  
   150  // AppendPrefixInKey appends the given prefix to all the Key's of the map and the
   151  // respective Labels' Key.
   152  func (l Labels) AppendPrefixInKey(prefix string) Labels {
   153  	newLabels := Labels{}
   154  	for k, v := range l {
   155  		newLabels[prefix+k] = Label{
   156  			Key:    prefix + v.Key,
   157  			Value:  v.Value,
   158  			Source: v.Source,
   159  		}
   160  	}
   161  	return newLabels
   162  }
   163  
   164  // Equals returns true if the two Labels contain the same set of labels.
   165  func (l Labels) Equals(other Labels) bool {
   166  	if len(l) != len(other) {
   167  		return false
   168  	}
   169  
   170  	for k, lbl1 := range l {
   171  		if lbl2, ok := other[k]; ok {
   172  			if lbl1.Source == lbl2.Source && lbl1.Key == lbl2.Key && lbl1.Value == lbl2.Value {
   173  				continue
   174  			}
   175  		}
   176  		return false
   177  	}
   178  	return true
   179  }
   180  
   181  // GetFromSource returns all labels that are from the given source.
   182  func (l Labels) GetFromSource(source string) Labels {
   183  	lbls := Labels{}
   184  	for k, v := range l {
   185  		if v.Source == source {
   186  			lbls[k] = v
   187  		}
   188  	}
   189  	return lbls
   190  }
   191  
   192  // NewLabel returns a new label from the given key, value and source. If source is empty,
   193  // the default value will be LabelSourceUnspec. If key starts with '$', the source
   194  // will be overwritten with LabelSourceReserved. If key contains ':', the value
   195  // before ':' will be used as source if given source is empty, otherwise the value before
   196  // ':' will be deleted and unused.
   197  func NewLabel(key string, value string, source string) Label {
   198  	var src string
   199  	src, key = parseSource(key, ':')
   200  	if source == "" {
   201  		if src == "" {
   202  			source = LabelSourceUnspec
   203  		} else {
   204  			source = src
   205  		}
   206  	}
   207  	if src == LabelSourceReserved && key == "" {
   208  		key = value
   209  		value = ""
   210  	}
   211  
   212  	return Label{
   213  		Key:    key,
   214  		Value:  value,
   215  		Source: source,
   216  	}
   217  }
   218  
   219  // Equals returns true if source, Key and Value are equal and false otherwise.
   220  func (l *Label) Equals(b *Label) bool {
   221  	if !l.IsAnySource() && l.Source != b.Source {
   222  		return false
   223  	}
   224  	return l.Key == b.Key && l.Value == b.Value
   225  }
   226  
   227  // IsAnySource return if the label was set with source "any".
   228  func (l *Label) IsAnySource() bool {
   229  	return l.Source == LabelSourceAny
   230  }
   231  
   232  // IsReservedSource return if the label was set with source "Reserved".
   233  func (l *Label) IsReservedSource() bool {
   234  	return l.Source == LabelSourceReserved
   235  }
   236  
   237  // matches returns true if l matches the target
   238  func (l *Label) matches(target *Label) bool {
   239  	return l.Equals(target)
   240  }
   241  
   242  // String returns the string representation of Label in the for of Source:Key=Value or
   243  // Source:Key if Value is empty.
   244  func (l *Label) String() string {
   245  	if len(l.Value) != 0 {
   246  		return fmt.Sprintf("%s:%s=%s", l.Source, l.Key, l.Value)
   247  	}
   248  	return fmt.Sprintf("%s:%s", l.Source, l.Key)
   249  }
   250  
   251  // IsValid returns true if Key != "".
   252  func (l *Label) IsValid() bool {
   253  	return l.Key != ""
   254  }
   255  
   256  // UnmarshalJSON TODO create better explanation about unmarshall with examples
   257  func (l *Label) UnmarshalJSON(data []byte) error {
   258  	decoder := json.NewDecoder(bytes.NewReader(data))
   259  
   260  	if l == nil {
   261  		return fmt.Errorf("cannot unmarhshal to nil pointer")
   262  	}
   263  
   264  	if len(data) == 0 {
   265  		return fmt.Errorf("invalid Label: empty data")
   266  	}
   267  
   268  	var aux struct {
   269  		Source string `json:"source"`
   270  		Key    string `json:"key"`
   271  		Value  string `json:"value,omitempty"`
   272  	}
   273  
   274  	err := decoder.Decode(&aux)
   275  	if err != nil {
   276  		// If parsing of the full representation failed then try the short
   277  		// form in the format:
   278  		//
   279  		// [SOURCE:]KEY[=VALUE]
   280  		var aux string
   281  
   282  		decoder = json.NewDecoder(bytes.NewReader(data))
   283  		if err := decoder.Decode(&aux); err != nil {
   284  			return fmt.Errorf("decode of Label as string failed: %+v", err)
   285  		}
   286  
   287  		if aux == "" {
   288  			return fmt.Errorf("invalid Label: Failed to parse %s as a string", data)
   289  		}
   290  
   291  		*l = ParseLabel(aux)
   292  	} else {
   293  		if aux.Key == "" {
   294  			return fmt.Errorf("invalid Label: '%s' does not contain label key", data)
   295  		}
   296  
   297  		l.Source = aux.Source
   298  		l.Key = aux.Key
   299  		l.Value = aux.Value
   300  	}
   301  
   302  	return nil
   303  }
   304  
   305  // GetExtendedKey returns the key of a label with the source encoded.
   306  func (l *Label) GetExtendedKey() string {
   307  	return l.Source + PathDelimiter + l.Key
   308  }
   309  
   310  // GetCiliumKeyFrom returns the label's source and key from the an extended key
   311  // in the format SOURCE:KEY.
   312  func GetCiliumKeyFrom(extKey string) string {
   313  	i := strings.IndexByte(extKey, PathDelimiter[0])
   314  	if i >= 0 {
   315  		return extKey[:i] + ":" + extKey[i+1:]
   316  	}
   317  	return LabelSourceAny + ":" + extKey
   318  }
   319  
   320  // GetExtendedKeyFrom returns the extended key of a label string.
   321  // For example:
   322  // `k8s:foo=bar` returns `k8s.foo`
   323  // `container:foo=bar` returns `container.foo`
   324  // `foo=bar` returns `any.foo=bar`
   325  func GetExtendedKeyFrom(str string) string {
   326  	src, next := parseSource(str, ':')
   327  	if src == "" {
   328  		src = LabelSourceAny
   329  	}
   330  	// Remove an eventually value
   331  	i := strings.IndexByte(next, '=')
   332  	if i >= 0 {
   333  		return src + PathDelimiter + next[:i]
   334  	}
   335  	return src + PathDelimiter + next
   336  }
   337  
   338  // Map2Labels transforms in the form: map[key(string)]value(string) into Labels. The
   339  // source argument will overwrite the source written in the key of the given map.
   340  // Example:
   341  // l := Map2Labels(map[string]string{"k8s:foo": "bar"}, "cilium")
   342  // fmt.Printf("%+v\n", l)
   343  //   map[string]Label{"foo":Label{Key:"foo", Value:"bar", Source:"cilium"}}
   344  func Map2Labels(m map[string]string, source string) Labels {
   345  	o := Labels{}
   346  	for k, v := range m {
   347  		l := NewLabel(k, v, source)
   348  		o[l.Key] = l
   349  	}
   350  	return o
   351  }
   352  
   353  // StringMap converts Labels into map[string]string
   354  func (l Labels) StringMap() map[string]string {
   355  	o := map[string]string{}
   356  	for _, v := range l {
   357  		o[v.Source+":"+v.Key] = v.Value
   358  	}
   359  	return o
   360  }
   361  
   362  // NewLabelsFromModel creates labels from string array.
   363  func NewLabelsFromModel(base []string) Labels {
   364  	lbls := make(Labels, len(base))
   365  	for _, v := range base {
   366  		if lbl := ParseLabel(v); lbl.Key != "" {
   367  			lbls[lbl.Key] = lbl
   368  		}
   369  	}
   370  
   371  	return lbls
   372  }
   373  
   374  // NewLabelsFromSortedList returns labels based on the output of SortedList()
   375  func NewLabelsFromSortedList(list string) Labels {
   376  	return NewLabelsFromModel(strings.Split(list, ";"))
   377  }
   378  
   379  // NewSelectLabelArrayFromModel parses a slice of strings and converts them
   380  // into an array of selecting labels, sorted by the key.
   381  func NewSelectLabelArrayFromModel(base []string) LabelArray {
   382  	lbls := make(LabelArray, 0, len(base))
   383  	for i := range base {
   384  		lbls = append(lbls, ParseSelectLabel(base[i]))
   385  	}
   386  
   387  	return lbls.Sort()
   388  }
   389  
   390  // GetModel returns model with all the values of the labels.
   391  func (l Labels) GetModel() []string {
   392  	res := make([]string, 0, len(l))
   393  	for _, v := range l {
   394  		res = append(res, v.String())
   395  	}
   396  	return res
   397  }
   398  
   399  // MergeLabels merges labels from into to. It overwrites all labels with the same Key as
   400  // from written into to.
   401  // Example:
   402  // to := Labels{Label{key1, value1, source1}, Label{key2, value3, source4}}
   403  // from := Labels{Label{key1, value3, source4}}
   404  // to.MergeLabels(from)
   405  // fmt.Printf("%+v\n", to)
   406  //   Labels{Label{key1, value3, source4}, Label{key2, value3, source4}}
   407  func (l Labels) MergeLabels(from Labels) {
   408  	for k, v := range from {
   409  		l[k] = v
   410  	}
   411  }
   412  
   413  // SHA256Sum calculates l' internal SHA256Sum. For a particular set of labels is
   414  // guarantee that it will always have the same SHA256Sum.
   415  func (l Labels) SHA256Sum() string {
   416  	return fmt.Sprintf("%x", sha512.Sum512_256(l.SortedList()))
   417  }
   418  
   419  // FormatForKVStore returns the label as a formatted string, ending in
   420  // a semicolon
   421  //
   422  // DO NOT BREAK THE FORMAT OF THIS. THE RETURNED STRING IS USED AS
   423  // PART OF THE KEY IN THE KEY-VALUE STORE.
   424  //
   425  // Non-pointer receiver allows this to be called on a value in a map.
   426  func (l Label) FormatForKVStore() string {
   427  	// We don't care if the values already have a '=' since this method is
   428  	// only used to calculate a SHA256Sum
   429  	//
   430  	// We absolutely care that the final character is a semi-colon.
   431  	// Identity allocation in the kvstore depends on this (see
   432  	// kvstore.prefixMatchesKey())
   433  	return fmt.Sprintf(`%s:%s=%s;`, l.Source, l.Key, l.Value)
   434  }
   435  
   436  // SortedList returns the labels as a sorted list, separated by semicolon
   437  //
   438  // DO NOT BREAK THE FORMAT OF THIS. THE RETURNED STRING IS USED AS KEY IN
   439  // THE KEY-VALUE STORE.
   440  func (l Labels) SortedList() []byte {
   441  	var keys []string
   442  	for k := range l {
   443  		keys = append(keys, k)
   444  	}
   445  	sort.Strings(keys)
   446  
   447  	result := ""
   448  	for _, k := range keys {
   449  		result += l[k].FormatForKVStore()
   450  	}
   451  
   452  	return []byte(result)
   453  }
   454  
   455  // ToSlice returns a slice of label with the values of the given
   456  // Labels' map, sorted by the key.
   457  func (l Labels) ToSlice() []Label {
   458  	return l.LabelArray()
   459  }
   460  
   461  // LabelArray returns the labels as label array, sorted by the key.
   462  func (l Labels) LabelArray() LabelArray {
   463  	labels := make(LabelArray, 0, len(l))
   464  	for _, v := range l {
   465  		labels = append(labels, v)
   466  	}
   467  	return labels.Sort()
   468  }
   469  
   470  // FindReserved locates all labels with reserved source in the labels and
   471  // returns a copy of them. If there are no reserved labels, returns nil.
   472  // TODO: return LabelArray as it is likely faster
   473  func (l Labels) FindReserved() Labels {
   474  	lbls := Labels{}
   475  
   476  	for k, lbl := range l {
   477  		if lbl.Source == LabelSourceReserved {
   478  			lbls[k] = lbl
   479  		}
   480  	}
   481  
   482  	if len(lbls) > 0 {
   483  		return lbls
   484  	}
   485  	return nil
   486  }
   487  
   488  // IsReserved returns true if any of the labels has a reserved source.
   489  func (l Labels) IsReserved() bool {
   490  	for _, lbl := range l {
   491  		if lbl.Source == LabelSourceReserved {
   492  			return true
   493  		}
   494  	}
   495  	return false
   496  }
   497  
   498  // parseSource returns the parsed source of the given str. It also returns the next piece
   499  // of text that is after the source.
   500  // Example:
   501  //  src, next := parseSource("foo:bar==value")
   502  // Println(src) // foo
   503  // Println(next) // bar==value
   504  // For Cilium format 'delim' must be passed in as ':'
   505  // For k8s format 'delim' must be passed in as '.'
   506  func parseSource(str string, delim byte) (src, next string) {
   507  	if str == "" {
   508  		return "", ""
   509  	}
   510  	if str[0] == '$' {
   511  		return LabelSourceReserved, str[1:]
   512  	}
   513  	i := strings.IndexByte(str, delim)
   514  	if i < 0 {
   515  		if delim != '.' && strings.HasPrefix(str, LabelSourceReservedKeyPrefix) {
   516  			return LabelSourceReserved, strings.TrimPrefix(str, LabelSourceReservedKeyPrefix)
   517  		}
   518  		return "", str
   519  	}
   520  	return str[:i], str[i+1:]
   521  }
   522  
   523  // ParseLabel returns the label representation of the given string. The str should be
   524  // in the form of Source:Key=Value or Source:Key if Value is empty. It also parses short
   525  // forms, for example: $host will be Label{Key: "host", Source: "reserved", Value: ""}.
   526  func ParseLabel(str string) Label {
   527  	return parseLabel(str, ':')
   528  }
   529  
   530  // parseLabel returns the label representation of the given string by value.
   531  // For Cilium format 'delim' must be passed in as ':'
   532  // For k8s format 'delim' must be passed in as '.'
   533  func parseLabel(str string, delim byte) (lbl Label) {
   534  	src, next := parseSource(str, delim)
   535  	if src != "" {
   536  		lbl.Source = src
   537  	} else {
   538  		lbl.Source = LabelSourceUnspec
   539  	}
   540  
   541  	i := strings.IndexByte(next, '=')
   542  	if i < 0 {
   543  		lbl.Key = next
   544  	} else {
   545  		if i == 0 && src == LabelSourceReserved {
   546  			lbl.Key = next[i+1:]
   547  		} else {
   548  			lbl.Key = next[:i]
   549  			lbl.Value = next[i+1:]
   550  		}
   551  	}
   552  	return lbl
   553  }
   554  
   555  // ParseSelectLabel returns a selecting label representation of the given
   556  // string. Unlike ParseLabel, if source is unspecified, the source defaults to
   557  // LabelSourceAny
   558  func ParseSelectLabel(str string) Label {
   559  	return parseSelectLabel(str, ':')
   560  }
   561  
   562  // parseSelectLabel returns a selecting label representation of the given
   563  // string by value.
   564  // For Cilium format 'delim' must be passed in as ':'
   565  // For k8s format 'delim' must be passed in as '.'
   566  func parseSelectLabel(str string, delim byte) Label {
   567  	lbl := parseLabel(str, delim)
   568  
   569  	if lbl.Source == LabelSourceUnspec {
   570  		lbl.Source = LabelSourceAny
   571  	}
   572  
   573  	return lbl
   574  }
   575  
   576  // generateLabelString generates the string representation of a label with
   577  // the provided source, key, and value in the format "source:key=value".
   578  func generateLabelString(source, key, value string) string {
   579  	return fmt.Sprintf("%s:%s=%s", source, key, value)
   580  }
   581  
   582  // GenerateK8sLabelString generates the string representation of a label with
   583  // the provided source, key, and value in the format "LabelSourceK8s:key=value".
   584  func GenerateK8sLabelString(k, v string) string {
   585  	return generateLabelString(LabelSourceK8s, k, v)
   586  }