github.com/demonoid81/containerd@v1.3.4/diff/stream.go (about)

     1  /*
     2     Copyright The containerd 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  
    17  package diff
    18  
    19  import (
    20  	"context"
    21  	"io"
    22  	"os"
    23  
    24  	"github.com/containerd/containerd/archive/compression"
    25  	"github.com/containerd/containerd/images"
    26  	"github.com/gogo/protobuf/types"
    27  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    28  	"github.com/pkg/errors"
    29  )
    30  
    31  var (
    32  	handlers []Handler
    33  
    34  	// ErrNoProcessor is returned when no stream processor is available for a media-type
    35  	ErrNoProcessor = errors.New("no processor for media-type")
    36  )
    37  
    38  func init() {
    39  	// register the default compression handler
    40  	RegisterProcessor(compressedHandler)
    41  }
    42  
    43  // RegisterProcessor registers a stream processor for media-types
    44  func RegisterProcessor(handler Handler) {
    45  	handlers = append(handlers, handler)
    46  }
    47  
    48  // GetProcessor returns the processor for a media-type
    49  func GetProcessor(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) {
    50  	// reverse this list so that user configured handlers come up first
    51  	for i := len(handlers) - 1; i >= 0; i-- {
    52  		processor, ok := handlers[i](ctx, stream.MediaType())
    53  		if ok {
    54  			return processor(ctx, stream, payloads)
    55  		}
    56  	}
    57  	return nil, ErrNoProcessor
    58  }
    59  
    60  // Handler checks a media-type and initializes the processor
    61  type Handler func(ctx context.Context, mediaType string) (StreamProcessorInit, bool)
    62  
    63  // StaticHandler returns the processor init func for a static media-type
    64  func StaticHandler(expectedMediaType string, fn StreamProcessorInit) Handler {
    65  	return func(ctx context.Context, mediaType string) (StreamProcessorInit, bool) {
    66  		if mediaType == expectedMediaType {
    67  			return fn, true
    68  		}
    69  		return nil, false
    70  	}
    71  }
    72  
    73  // StreamProcessorInit returns the initialized stream processor
    74  type StreamProcessorInit func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error)
    75  
    76  // RawProcessor provides access to direct fd for processing
    77  type RawProcessor interface {
    78  	// File returns the fd for the read stream of the underlying processor
    79  	File() *os.File
    80  }
    81  
    82  // StreamProcessor handles processing a content stream and transforming it into a different media-type
    83  type StreamProcessor interface {
    84  	io.ReadCloser
    85  
    86  	// MediaType is the resulting media-type that the processor processes the stream into
    87  	MediaType() string
    88  }
    89  
    90  func compressedHandler(ctx context.Context, mediaType string) (StreamProcessorInit, bool) {
    91  	compressed, err := images.DiffCompression(ctx, mediaType)
    92  	if err != nil {
    93  		return nil, false
    94  	}
    95  	if compressed != "" {
    96  		return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) {
    97  			ds, err := compression.DecompressStream(stream)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  
   102  			return &compressedProcessor{
   103  				rc: ds,
   104  			}, nil
   105  		}, true
   106  	}
   107  	return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) {
   108  		return &stdProcessor{
   109  			rc: stream,
   110  		}, nil
   111  	}, true
   112  }
   113  
   114  // NewProcessorChain initialized the root StreamProcessor
   115  func NewProcessorChain(mt string, r io.Reader) StreamProcessor {
   116  	return &processorChain{
   117  		mt: mt,
   118  		rc: r,
   119  	}
   120  }
   121  
   122  type processorChain struct {
   123  	mt string
   124  	rc io.Reader
   125  }
   126  
   127  func (c *processorChain) MediaType() string {
   128  	return c.mt
   129  }
   130  
   131  func (c *processorChain) Read(p []byte) (int, error) {
   132  	return c.rc.Read(p)
   133  }
   134  
   135  func (c *processorChain) Close() error {
   136  	return nil
   137  }
   138  
   139  type stdProcessor struct {
   140  	rc StreamProcessor
   141  }
   142  
   143  func (c *stdProcessor) MediaType() string {
   144  	return ocispec.MediaTypeImageLayer
   145  }
   146  
   147  func (c *stdProcessor) Read(p []byte) (int, error) {
   148  	return c.rc.Read(p)
   149  }
   150  
   151  func (c *stdProcessor) Close() error {
   152  	return nil
   153  }
   154  
   155  type compressedProcessor struct {
   156  	rc io.ReadCloser
   157  }
   158  
   159  func (c *compressedProcessor) MediaType() string {
   160  	return ocispec.MediaTypeImageLayer
   161  }
   162  
   163  func (c *compressedProcessor) Read(p []byte) (int, error) {
   164  	return c.rc.Read(p)
   165  }
   166  
   167  func (c *compressedProcessor) Close() error {
   168  	return c.rc.Close()
   169  }
   170  
   171  func BinaryHandler(id, returnsMediaType string, mediaTypes []string, path string, args []string) Handler {
   172  	set := make(map[string]struct{}, len(mediaTypes))
   173  	for _, m := range mediaTypes {
   174  		set[m] = struct{}{}
   175  	}
   176  	return func(_ context.Context, mediaType string) (StreamProcessorInit, bool) {
   177  		if _, ok := set[mediaType]; ok {
   178  			return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) {
   179  				payload := payloads[id]
   180  				return NewBinaryProcessor(ctx, mediaType, returnsMediaType, stream, path, args, payload)
   181  			}, true
   182  		}
   183  		return nil, false
   184  	}
   185  }
   186  
   187  const mediaTypeEnvVar = "STREAM_PROCESSOR_MEDIATYPE"