...

Source file src/edge-infra.dev/pkg/f8n/warehouse/pallet/metadata.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/pallet

     1  package pallet
     2  
     3  import (
     4  	"fmt"
     5  
     6  	wh "edge-infra.dev/pkg/f8n/warehouse"
     7  	"edge-infra.dev/pkg/f8n/warehouse/oci"
     8  )
     9  
    10  var (
    11  	// errInvalidMetadata occurs when a pallet's metadata is invalid or incomplete.
    12  	errInvalidMetadata = fmt.Errorf("invalid pallet metadata")
    13  )
    14  
    15  // K8s Annotations are added to every K8s object bundled into a pallet package.
    16  const (
    17  	KnnotationSource   = "pallet.edge.ncr.com/source"
    18  	KnnotationCreated  = "pallet.edge.ncr.com/created"
    19  	KnnotationTeam     = "pallet.edge.ncr.com/team"
    20  	KnnotationName     = "pallet.edge.ncr.com/name"
    21  	KnnotationVersion  = "pallet.edge.ncr.com/version"
    22  	KnnotationVendor   = "pallet.edge.ncr.com/vendor"
    23  	KnnotationRevision = "pallet.edge.ncr.com/revision"
    24  )
    25  
    26  // Metadata defines information about the package itself.
    27  type Metadata struct {
    28  	// Required
    29  	Name   string
    30  	Team   string
    31  	Vendor string
    32  	BuildInfo
    33  
    34  	// Optional
    35  	Description string
    36  	Readme      string
    37  }
    38  
    39  // BuildInfo is build information that gets added to the produced artifacts using
    40  // predefined OCI annotations:
    41  // https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
    42  type BuildInfo struct {
    43  	Source   string
    44  	Version  string
    45  	Revision string
    46  	Created  string
    47  }
    48  
    49  func (b *BuildInfo) Validate() error {
    50  	if b.Source == "" {
    51  		return fmt.Errorf("%w: source must be provided", errInvalidMetadata)
    52  	}
    53  	if b.Revision == "" {
    54  		return fmt.Errorf("%w: revision must be provided", errInvalidMetadata)
    55  	}
    56  	if b.Version == "" {
    57  		return fmt.Errorf("%w: version must be provided", errInvalidMetadata)
    58  	}
    59  	if b.Created == "" {
    60  		return fmt.Errorf("%w: created must be provided", errInvalidMetadata)
    61  	}
    62  	return nil
    63  }
    64  
    65  func (m *Metadata) Validate() error {
    66  	if m.Name == "" {
    67  		return fmt.Errorf("%w: name must be provided", errInvalidMetadata)
    68  	}
    69  	if m.Team == "" {
    70  		return fmt.Errorf("%w: team must be provided", errInvalidMetadata)
    71  	}
    72  
    73  	return m.BuildInfo.Validate()
    74  }
    75  
    76  // OCIAnnotations translates the contents of the Meatdata struct to a map of
    77  // appropriate OCI artifact annotations. Where possible, it maps data to
    78  // existing OCI annotations (https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/annotations.go),
    79  // but extends them with Warehouse specific annotations where appropriate.
    80  func (m Metadata) OCIAnnotations() map[string]string {
    81  	// set a default vendor if one is not provided.
    82  	if m.Vendor == "" {
    83  		m.Vendor = "NCR"
    84  	}
    85  	r := map[string]string{
    86  		wh.AnnotationSource:   m.Source,
    87  		wh.AnnotationVersion:  m.Version,
    88  		wh.AnnotationCreated:  m.Created,
    89  		wh.AnnotationRevision: m.Revision,
    90  		wh.AnnotationVendor:   m.Vendor,
    91  		wh.AnnotationTitle:    m.Name,
    92  		// Additionally store name using the Pallet-specific annotation so that usage
    93  		// doesn't compete with integrations based on v1.AnnotationTitle in the future
    94  		wh.AnnotationName: m.Name,
    95  		wh.AnnotationTeam: m.Team,
    96  	}
    97  	// Add optional values if they are present
    98  	if m.Description != "" {
    99  		r[wh.AnnotationDescription] = m.Description
   100  	}
   101  	if m.Readme != "" {
   102  		r[wh.AnnotationDocumentation] = m.Readme
   103  	}
   104  	return r
   105  }
   106  
   107  func (m Metadata) K8sAnnotations() map[string]string {
   108  	r := map[string]string{
   109  		KnnotationTeam:     m.Team,
   110  		KnnotationName:     m.Name,
   111  		KnnotationVersion:  m.Version,
   112  		KnnotationRevision: m.Revision,
   113  	}
   114  	if m.Created != "" {
   115  		r[KnnotationCreated] = m.Created
   116  	}
   117  	if m.Source != "" {
   118  		r[KnnotationSource] = m.Source
   119  	}
   120  	return r
   121  }
   122  
   123  var requiredMetaAnnos = []string{
   124  	wh.AnnotationTeam,
   125  	wh.AnnotationName,
   126  	wh.AnnotationRevision,
   127  	wh.AnnotationSource,
   128  	wh.AnnotationVersion,
   129  	wh.AnnotationCreated,
   130  	wh.AnnotationVendor,
   131  }
   132  
   133  // metadataFromAnnotations creates a Metadata struct from an annotations map
   134  // pulled from a valid pallet OCI artifact. If the input map does not contain
   135  // all of the required annotations, an error is returned
   136  func metadataFromAnnotations(aa map[string]string) (Metadata, error) {
   137  	if _, ok := aa[wh.AnnotationVendor]; !ok {
   138  		aa[wh.AnnotationVendor] = "NCR"
   139  	}
   140  
   141  	for _, a := range requiredMetaAnnos {
   142  		if _, ok := aa[a]; !ok {
   143  			return Metadata{}, fmt.Errorf(
   144  				"%w: invalid pallet metadata: required annotation %s not found",
   145  				oci.ErrInvalidArtifact,
   146  				a,
   147  			)
   148  		}
   149  	}
   150  
   151  	meta := Metadata{
   152  		Name: aa[wh.AnnotationName],
   153  		Team: aa[wh.AnnotationTeam],
   154  		BuildInfo: BuildInfo{
   155  			Version:  aa[wh.AnnotationVersion],
   156  			Source:   aa[wh.AnnotationSource],
   157  			Created:  aa[wh.AnnotationCreated],
   158  			Revision: aa[wh.AnnotationRevision],
   159  		},
   160  		Vendor: aa[wh.AnnotationVendor],
   161  	}
   162  
   163  	for _, a := range aa {
   164  		switch a {
   165  		case wh.AnnotationDocumentation:
   166  			meta.Readme = aa[wh.AnnotationDocumentation]
   167  		case wh.AnnotationDescription:
   168  			meta.Description = aa[wh.AnnotationDescription]
   169  		}
   170  	}
   171  
   172  	return meta, nil
   173  }
   174  

View as plain text