github.com/lalkh/containerd@v1.4.3/content/proxy/content_store.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 proxy 18 19 import ( 20 "context" 21 "io" 22 23 contentapi "github.com/containerd/containerd/api/services/content/v1" 24 "github.com/containerd/containerd/content" 25 "github.com/containerd/containerd/errdefs" 26 protobuftypes "github.com/gogo/protobuf/types" 27 digest "github.com/opencontainers/go-digest" 28 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 29 ) 30 31 type proxyContentStore struct { 32 client contentapi.ContentClient 33 } 34 35 // NewContentStore returns a new content store which communicates over a GRPC 36 // connection using the containerd content GRPC API. 37 func NewContentStore(client contentapi.ContentClient) content.Store { 38 return &proxyContentStore{ 39 client: client, 40 } 41 } 42 43 func (pcs *proxyContentStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) { 44 resp, err := pcs.client.Info(ctx, &contentapi.InfoRequest{ 45 Digest: dgst, 46 }) 47 if err != nil { 48 return content.Info{}, errdefs.FromGRPC(err) 49 } 50 51 return infoFromGRPC(resp.Info), nil 52 } 53 54 func (pcs *proxyContentStore) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error { 55 session, err := pcs.client.List(ctx, &contentapi.ListContentRequest{ 56 Filters: filters, 57 }) 58 if err != nil { 59 return errdefs.FromGRPC(err) 60 } 61 62 for { 63 msg, err := session.Recv() 64 if err != nil { 65 if err != io.EOF { 66 return errdefs.FromGRPC(err) 67 } 68 69 break 70 } 71 72 for _, info := range msg.Info { 73 if err := fn(infoFromGRPC(info)); err != nil { 74 return err 75 } 76 } 77 } 78 79 return nil 80 } 81 82 func (pcs *proxyContentStore) Delete(ctx context.Context, dgst digest.Digest) error { 83 if _, err := pcs.client.Delete(ctx, &contentapi.DeleteContentRequest{ 84 Digest: dgst, 85 }); err != nil { 86 return errdefs.FromGRPC(err) 87 } 88 89 return nil 90 } 91 92 // ReaderAt ignores MediaType. 93 func (pcs *proxyContentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { 94 i, err := pcs.Info(ctx, desc.Digest) 95 if err != nil { 96 return nil, err 97 } 98 99 return &remoteReaderAt{ 100 ctx: ctx, 101 digest: desc.Digest, 102 size: i.Size, 103 client: pcs.client, 104 }, nil 105 } 106 107 func (pcs *proxyContentStore) Status(ctx context.Context, ref string) (content.Status, error) { 108 resp, err := pcs.client.Status(ctx, &contentapi.StatusRequest{ 109 Ref: ref, 110 }) 111 if err != nil { 112 return content.Status{}, errdefs.FromGRPC(err) 113 } 114 115 status := resp.Status 116 return content.Status{ 117 Ref: status.Ref, 118 StartedAt: status.StartedAt, 119 UpdatedAt: status.UpdatedAt, 120 Offset: status.Offset, 121 Total: status.Total, 122 Expected: status.Expected, 123 }, nil 124 } 125 126 func (pcs *proxyContentStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) { 127 resp, err := pcs.client.Update(ctx, &contentapi.UpdateRequest{ 128 Info: infoToGRPC(info), 129 UpdateMask: &protobuftypes.FieldMask{ 130 Paths: fieldpaths, 131 }, 132 }) 133 if err != nil { 134 return content.Info{}, errdefs.FromGRPC(err) 135 } 136 return infoFromGRPC(resp.Info), nil 137 } 138 139 func (pcs *proxyContentStore) ListStatuses(ctx context.Context, filters ...string) ([]content.Status, error) { 140 resp, err := pcs.client.ListStatuses(ctx, &contentapi.ListStatusesRequest{ 141 Filters: filters, 142 }) 143 if err != nil { 144 return nil, errdefs.FromGRPC(err) 145 } 146 147 var statuses []content.Status 148 for _, status := range resp.Statuses { 149 statuses = append(statuses, content.Status{ 150 Ref: status.Ref, 151 StartedAt: status.StartedAt, 152 UpdatedAt: status.UpdatedAt, 153 Offset: status.Offset, 154 Total: status.Total, 155 Expected: status.Expected, 156 }) 157 } 158 159 return statuses, nil 160 } 161 162 // Writer ignores MediaType. 163 func (pcs *proxyContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { 164 var wOpts content.WriterOpts 165 for _, opt := range opts { 166 if err := opt(&wOpts); err != nil { 167 return nil, err 168 } 169 } 170 wrclient, offset, err := pcs.negotiate(ctx, wOpts.Ref, wOpts.Desc.Size, wOpts.Desc.Digest) 171 if err != nil { 172 return nil, errdefs.FromGRPC(err) 173 } 174 175 return &remoteWriter{ 176 ref: wOpts.Ref, 177 client: wrclient, 178 offset: offset, 179 }, nil 180 } 181 182 // Abort implements asynchronous abort. It starts a new write session on the ref l 183 func (pcs *proxyContentStore) Abort(ctx context.Context, ref string) error { 184 if _, err := pcs.client.Abort(ctx, &contentapi.AbortRequest{ 185 Ref: ref, 186 }); err != nil { 187 return errdefs.FromGRPC(err) 188 } 189 190 return nil 191 } 192 193 func (pcs *proxyContentStore) negotiate(ctx context.Context, ref string, size int64, expected digest.Digest) (contentapi.Content_WriteClient, int64, error) { 194 wrclient, err := pcs.client.Write(ctx) 195 if err != nil { 196 return nil, 0, err 197 } 198 199 if err := wrclient.Send(&contentapi.WriteContentRequest{ 200 Action: contentapi.WriteActionStat, 201 Ref: ref, 202 Total: size, 203 Expected: expected, 204 }); err != nil { 205 return nil, 0, err 206 } 207 208 resp, err := wrclient.Recv() 209 if err != nil { 210 return nil, 0, err 211 } 212 213 return wrclient, resp.Offset, nil 214 } 215 216 func infoToGRPC(info content.Info) contentapi.Info { 217 return contentapi.Info{ 218 Digest: info.Digest, 219 Size_: info.Size, 220 CreatedAt: info.CreatedAt, 221 UpdatedAt: info.UpdatedAt, 222 Labels: info.Labels, 223 } 224 } 225 226 func infoFromGRPC(info contentapi.Info) content.Info { 227 return content.Info{ 228 Digest: info.Digest, 229 Size: info.Size_, 230 CreatedAt: info.CreatedAt, 231 UpdatedAt: info.UpdatedAt, 232 Labels: info.Labels, 233 } 234 }