...

Source file src/github.com/Microsoft/hcsshim/internal/oci/annotations.go

Documentation: github.com/Microsoft/hcsshim/internal/oci

     1  package oci
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/Microsoft/hcsshim/internal/log"
    10  	"github.com/Microsoft/hcsshim/internal/logfields"
    11  	"github.com/Microsoft/hcsshim/pkg/annotations"
    12  	"github.com/opencontainers/runtime-spec/specs-go"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  var ErrAnnotationExpansionConflict = errors.New("annotation expansion conflict")
    17  
    18  // ProcessAnnotations expands annotations into their corresponding annotation groups
    19  func ProcessAnnotations(ctx context.Context, s *specs.Spec) (err error) {
    20  	// Named `Process` and not `Expand` since this function may be expanded (pun intended) to
    21  	// deal with other annotation issues and validation.
    22  
    23  	// Rather than give up part of the way through on error, this just emits a warning (similar
    24  	// to the `parseAnnotation*` functions) and continues through, so the spec is not left in a
    25  	// (partially) unusable form.
    26  	// If multiple different errors are to be raised, they should be combined or, if they
    27  	// are logged, only the last kept, depending on their severity.
    28  
    29  	// expand annotations
    30  	for key, exps := range annotations.AnnotationExpansions {
    31  		// check if annotation is present
    32  		if val, ok := s.Annotations[key]; ok {
    33  			// ideally, some normalization would occur here (ie, "True" -> "true")
    34  			// but strings may be case-sensitive
    35  			for _, k := range exps {
    36  				if v, ok := s.Annotations[k]; ok && val != v {
    37  					err = ErrAnnotationExpansionConflict
    38  					log.G(ctx).WithFields(logrus.Fields{
    39  						logfields.OCIAnnotation:               key,
    40  						logfields.Value:                       val,
    41  						logfields.OCIAnnotation + "-conflict": k,
    42  						logfields.Value + "-conflict":         v,
    43  					}).WithError(err).Warning("annotation expansion would overwrite conflicting value")
    44  					continue
    45  				}
    46  				s.Annotations[k] = val
    47  			}
    48  		}
    49  	}
    50  
    51  	return err
    52  }
    53  
    54  // handle specific annotations
    55  
    56  // ParseAnnotationsDisableGMSA searches for the boolean value which specifies
    57  // if providing a gMSA credential should be disallowed. Returns the value found,
    58  // if parsable, otherwise returns false otherwise.
    59  func ParseAnnotationsDisableGMSA(ctx context.Context, s *specs.Spec) bool {
    60  	return ParseAnnotationsBool(ctx, s.Annotations, annotations.WCOWDisableGMSA, false)
    61  }
    62  
    63  // general annotation parsing
    64  
    65  // ParseAnnotationsBool searches `a` for `key` and if found verifies that the
    66  // value is `true` or `false` in any case. If `key` is not found returns `def`.
    67  func ParseAnnotationsBool(ctx context.Context, a map[string]string, key string, def bool) bool {
    68  	if v, ok := a[key]; ok {
    69  		switch strings.ToLower(v) {
    70  		case "true":
    71  			return true
    72  		case "false":
    73  			return false
    74  		default:
    75  			logAnnotationParseError(ctx, key, v, logfields.Bool, nil)
    76  		}
    77  	}
    78  	return def
    79  }
    80  
    81  // parseAnnotationsUint32 searches `a` for `key` and if found verifies that the
    82  // value is a 32 bit unsigned integer. If `key` is not found returns `def`.
    83  func parseAnnotationsUint32(ctx context.Context, a map[string]string, key string, def uint32) uint32 {
    84  	if v, ok := a[key]; ok {
    85  		countu, err := strconv.ParseUint(v, 10, 32)
    86  		if err == nil {
    87  			v := uint32(countu)
    88  			return v
    89  		}
    90  		logAnnotationParseError(ctx, key, v, logfields.Uint32, err)
    91  	}
    92  	return def
    93  }
    94  
    95  // parseAnnotationsUint64 searches `a` for `key` and if found verifies that the
    96  // value is a 64 bit unsigned integer. If `key` is not found returns `def`.
    97  func parseAnnotationsUint64(ctx context.Context, a map[string]string, key string, def uint64) uint64 {
    98  	if v, ok := a[key]; ok {
    99  		countu, err := strconv.ParseUint(v, 10, 64)
   100  		if err == nil {
   101  			return countu
   102  		}
   103  		logAnnotationParseError(ctx, key, v, logfields.Uint64, err)
   104  	}
   105  	return def
   106  }
   107  
   108  // parseAnnotationsString searches `a` for `key`. If `key` is not found returns `def`.
   109  func parseAnnotationsString(a map[string]string, key string, def string) string {
   110  	if v, ok := a[key]; ok {
   111  		return v
   112  	}
   113  	return def
   114  }
   115  
   116  // ParseAnnotationCommaSeparated searches `annotations` for `annotation` corresponding to a
   117  // list of comma separated strings
   118  func ParseAnnotationCommaSeparated(annotation string, annotations map[string]string) []string {
   119  	cs, ok := annotations[annotation]
   120  	if !ok || cs == "" {
   121  		return nil
   122  	}
   123  	results := strings.Split(cs, ",")
   124  	return results
   125  }
   126  
   127  func logAnnotationParseError(ctx context.Context, k, v, et string, err error) {
   128  	entry := log.G(ctx).WithFields(logrus.Fields{
   129  		logfields.OCIAnnotation: k,
   130  		logfields.Value:         v,
   131  		logfields.ExpectedType:  et,
   132  	})
   133  	if err != nil {
   134  		entry = entry.WithError(err)
   135  	}
   136  	entry.Warning("annotation could not be parsed")
   137  }
   138  

View as plain text