...

Source file src/github.com/sigstore/cosign/v2/pkg/oci/mutate/map.go

Documentation: github.com/sigstore/cosign/v2/pkg/oci/mutate

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package mutate
    17  
    18  import (
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  
    23  	v1 "github.com/google/go-containerregistry/pkg/v1"
    24  	"github.com/google/go-containerregistry/pkg/v1/empty"
    25  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    26  	"github.com/google/go-containerregistry/pkg/v1/types"
    27  	"github.com/sigstore/cosign/v2/pkg/oci"
    28  )
    29  
    30  // Fn is the signature of the callback supplied to Map.
    31  // The oci.SignedEntity is either an oci.SignedImageIndex or an oci.SignedImage.
    32  // This callback is called on oci.SignedImageIndex *before* its children are
    33  // processed with a context that returns IsBeforeChildren(ctx) == true.
    34  // If the images within the SignedImageIndex change after the Before pass, then
    35  // the Fn will be invoked again on the new SignedImageIndex with a context
    36  // that returns IsAfterChildren(ctx) == true.
    37  // If the returned entity is nil, it is filtered from the result of Map.
    38  type Fn func(context.Context, oci.SignedEntity) (oci.SignedEntity, error)
    39  
    40  // ErrSkipChildren is a special error that may be returned from a Mutator
    41  // to skip processing of an index's child entities.
    42  var ErrSkipChildren = errors.New("skip child entities")
    43  
    44  // Map calls `fn` on the signed entity and each of its constituent entities (`SignedImageIndex`
    45  // or `SignedImage`) transitively.
    46  // Any errors returned by an `fn` are returned by `Map`.
    47  func Map(ctx context.Context, parent oci.SignedEntity, fn Fn) (oci.SignedEntity, error) {
    48  	parent, err := fn(before(ctx), parent)
    49  	switch {
    50  	case errors.Is(err, ErrSkipChildren):
    51  		return parent, nil
    52  	case err != nil:
    53  		return nil, err
    54  	case parent == nil:
    55  		// If the function returns nil, it filters it.
    56  		return nil, nil
    57  	}
    58  
    59  	sii, ok := parent.(oci.SignedImageIndex)
    60  	if !ok {
    61  		return parent, nil
    62  	}
    63  	im, err := sii.IndexManifest()
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	// Track whether any of the child entities change.
    69  	changed := false
    70  
    71  	adds := []IndexAddendum{}
    72  	for _, desc := range im.Manifests {
    73  		switch desc.MediaType {
    74  		case types.OCIImageIndex, types.DockerManifestList:
    75  			x, err := sii.SignedImageIndex(desc.Digest)
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  
    80  			se, err := Map(ctx, x, fn)
    81  			if err != nil {
    82  				return nil, err
    83  			} else if se == nil {
    84  				// If the function returns nil, it filters it.
    85  				changed = true
    86  				continue
    87  			}
    88  
    89  			changed = changed || (x != se)
    90  			adds = append(adds, IndexAddendum{
    91  				Add: se.(oci.SignedImageIndex), // Must be an image index.
    92  				Descriptor: v1.Descriptor{
    93  					URLs:        desc.URLs,
    94  					MediaType:   desc.MediaType,
    95  					Annotations: desc.Annotations,
    96  					Platform:    desc.Platform,
    97  				},
    98  			})
    99  
   100  		case types.OCIManifestSchema1, types.DockerManifestSchema2:
   101  			x, err := sii.SignedImage(desc.Digest)
   102  			if err != nil {
   103  				return nil, err
   104  			}
   105  
   106  			se, err := fn(ctx, x)
   107  			if err != nil {
   108  				return nil, err
   109  			} else if se == nil {
   110  				// If the function returns nil, it filters it.
   111  				changed = true
   112  				continue
   113  			}
   114  
   115  			changed = changed || (x != se)
   116  			adds = append(adds, IndexAddendum{
   117  				Add: se.(oci.SignedImage), // Must be an image
   118  				Descriptor: v1.Descriptor{
   119  					URLs:        desc.URLs,
   120  					MediaType:   desc.MediaType,
   121  					Annotations: desc.Annotations,
   122  					Platform:    desc.Platform,
   123  				},
   124  			})
   125  
   126  		default:
   127  			return nil, fmt.Errorf("unknown mime type: %v", desc.MediaType)
   128  		}
   129  	}
   130  
   131  	if !changed {
   132  		return parent, nil
   133  	}
   134  
   135  	// Preserve the key attributes from the base IndexManifest.
   136  	e := mutate.IndexMediaType(empty.Index, im.MediaType)
   137  	e = mutate.Annotations(e, im.Annotations).(v1.ImageIndex)
   138  
   139  	// Construct a new ImageIndex from the new constituent signed images.
   140  	result := AppendManifests(e, adds...)
   141  
   142  	// Since the children changed, give the callback a crack at the new image index.
   143  	return fn(after(ctx), result)
   144  }
   145  
   146  // This is used to associate which pass of the Map a particular
   147  // callback is being invoked for.
   148  type mapPassKey struct{}
   149  
   150  // before decorates the context such that IsBeforeChildren(ctx) is true.
   151  func before(ctx context.Context) context.Context {
   152  	return context.WithValue(ctx, mapPassKey{}, "before")
   153  }
   154  
   155  // after decorates the context such that IsAfterChildren(ctx) is true.
   156  func after(ctx context.Context) context.Context {
   157  	return context.WithValue(ctx, mapPassKey{}, "after")
   158  }
   159  
   160  // IsBeforeChildren is true within a Mutator when it is called before the children
   161  // have been processed.
   162  func IsBeforeChildren(ctx context.Context) bool {
   163  	return ctx.Value(mapPassKey{}) == "before"
   164  }
   165  
   166  // IsAfterChildren is true within a Mutator when it is called after the children
   167  // have been processed; however, this call is only made if the set of children
   168  // changes since the Before call.
   169  func IsAfterChildren(ctx context.Context) bool {
   170  	return ctx.Value(mapPassKey{}) == "after"
   171  }
   172  

View as plain text