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  }