github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/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"