...

Source file src/edge-infra.dev/pkg/f8n/warehouse/lift/unpack/layers.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/lift/unpack

     1  package unpack
     2  
     3  import (
     4  	"fmt"
     5  
     6  	v1 "github.com/google/go-containerregistry/pkg/v1"
     7  	"sigs.k8s.io/kustomize/api/filters/namespace"
     8  
     9  	"edge-infra.dev/pkg/f8n/warehouse/cluster"
    10  	"edge-infra.dev/pkg/f8n/warehouse/lift"
    11  	"edge-infra.dev/pkg/f8n/warehouse/lift/render"
    12  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    13  	"edge-infra.dev/pkg/f8n/warehouse/oci/layer"
    14  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    15  )
    16  
    17  // Layers unpacks a single input artifact a into the desired processed layers
    18  // based on options. By default, all layers are returned.
    19  func Layers(a oci.Artifact, opts ...Option) ([]layer.Layer, error) {
    20  	options, err := makeOptions(opts...)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  
    25  	// resolve input to a Pallet
    26  	switch a := a.(type) {
    27  	// use it directly if a pallet was passed to us, after confirming options
    28  	// are valid
    29  	case pallet.Pallet:
    30  		// if the pallet isn't backed by an image, options.provider must be present
    31  		if _, ok := a.Unwrap().(v1.Image); !ok && options.provider == "" {
    32  			return nil, fmt.Errorf(
    33  				"%s: %s pallet requires one of %s",
    34  				cluster.ErrNoProviders, a.Name(), a.Providers(),
    35  			)
    36  		}
    37  		return unpackPallet(a, *options)
    38  	case v1.ImageIndex:
    39  		if options.provider == "" {
    40  			return nil, fmt.Errorf(
    41  				"%s: v1.ImageIndex requires a provider",
    42  				cluster.ErrNoProviders,
    43  			)
    44  		}
    45  	}
    46  
    47  	// construct pallet if we weren't given one
    48  	p, err := pallet.New(a)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	return unpackPallet(p, *options)
    54  }
    55  
    56  // unpackPallet processes layers from a single pallet based on the options
    57  func unpackPallet(p pallet.Pallet, opts options) ([]layer.Layer, error) {
    58  	// get raw layers
    59  	layers, err := p.Layers(opts.provider)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	switch p.Renderable() {
    65  	// add global rendering parameters via unpacking options and package info
    66  	case true:
    67  		params := map[string]string{lift.PalletNameRenderingParameter: p.Name()}
    68  		// TODO(aw185176): parameters declared by package should be parsed and error
    69  		// returned if it needs cluster provider but we don't have it
    70  		if opts.provider != "" {
    71  			params[lift.ClusterProviderRenderingParameter] = string(opts.provider)
    72  		}
    73  		opts.parameters = append(opts.parameters, params)
    74  	case false:
    75  		opts.render = false
    76  	}
    77  
    78  	// unless we are specifically requested for specific layers, return all of them
    79  	if len(opts.keys) == 0 && len(opts.types) == 0 {
    80  		for i, l := range layers {
    81  			layers[i], err = processLayer(l, p.Parameters(), opts)
    82  			if err != nil {
    83  				return nil, err
    84  			}
    85  		}
    86  
    87  		return layers, nil
    88  	}
    89  
    90  	// otherwise we filter and process requested layers from artifact
    91  	return filterLayers(layers, p.Parameters(), opts)
    92  }
    93  
    94  // filterLayers filters out undesirable layers in-place based and performs the
    95  // appropriate mutations (eg, rendering) on the unpacking options
    96  func filterLayers(layers []layer.Layer, palletParams []string, opts options) ([]layer.Layer, error) {
    97  	var (
    98  		err error
    99  		j   = 0
   100  	)
   101  
   102  	// if we find a desired layer, process it based on options
   103  	for _, l := range layers {
   104  		// NOTE: we avoid duplicated processing of layers by validating that both
   105  		// opts.keys and opts.types are not provided
   106  		for _, desired := range opts.keys {
   107  			if l.Key() == desired {
   108  				l, err = processLayer(l, palletParams, opts)
   109  				if err != nil {
   110  					return nil, err
   111  				}
   112  
   113  				layers[j] = l
   114  				j++
   115  			}
   116  		}
   117  
   118  		for _, t := range opts.types {
   119  			if l.Type() == t {
   120  				l, err = processLayer(l, palletParams, opts)
   121  				if err != nil {
   122  					return nil, err
   123  				}
   124  
   125  				layers[j] = l
   126  				j++
   127  			}
   128  		}
   129  	}
   130  	layers = layers[:j]
   131  
   132  	return layers, nil
   133  }
   134  
   135  func processLayer(l layer.Layer, palletParams []string, opts options) (layer.Layer, error) {
   136  	var err error
   137  
   138  	if opts.render {
   139  		l, err = render.Layer(l, palletParams, opts.parameters...)
   140  		if err != nil {
   141  			return nil, err
   142  		}
   143  	}
   144  
   145  	if opts.infraNamespace != "" && l.Type() == layer.Infra {
   146  		l, err = layer.Filter(l, namespace.Filter{Namespace: opts.infraNamespace})
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  	}
   151  
   152  	return l, nil
   153  }
   154  

View as plain text