pull.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package containerd
  14. import (
  15. "context"
  16. "github.com/containerd/containerd/errdefs"
  17. "github.com/containerd/containerd/images"
  18. "github.com/containerd/containerd/platforms"
  19. "github.com/containerd/containerd/remotes"
  20. "github.com/containerd/containerd/remotes/docker"
  21. "github.com/containerd/containerd/remotes/docker/schema1"
  22. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  23. "github.com/pkg/errors"
  24. "golang.org/x/sync/semaphore"
  25. )
  26. // Pull downloads the provided content into containerd's content store
  27. // and returns a platform specific image object
  28. func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) {
  29. pullCtx := defaultRemoteContext()
  30. for _, o := range opts {
  31. if err := o(c, pullCtx); err != nil {
  32. return nil, err
  33. }
  34. }
  35. if pullCtx.PlatformMatcher == nil {
  36. if len(pullCtx.Platforms) > 1 {
  37. return nil, errors.New("cannot pull multiplatform image locally, try Fetch")
  38. } else if len(pullCtx.Platforms) == 0 {
  39. pullCtx.PlatformMatcher = platforms.Default()
  40. } else {
  41. p, err := platforms.Parse(pullCtx.Platforms[0])
  42. if err != nil {
  43. return nil, errors.Wrapf(err, "invalid platform %s", pullCtx.Platforms[0])
  44. }
  45. pullCtx.PlatformMatcher = platforms.Only(p)
  46. }
  47. }
  48. ctx, done, err := c.WithLease(ctx)
  49. if err != nil {
  50. return nil, err
  51. }
  52. defer done(ctx)
  53. img, err := c.fetch(ctx, pullCtx, ref, 1)
  54. if err != nil {
  55. return nil, err
  56. }
  57. i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher)
  58. if pullCtx.Unpack {
  59. if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil {
  60. return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter)
  61. }
  62. }
  63. return i, nil
  64. }
  65. func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) {
  66. store := c.ContentStore()
  67. name, desc, err := rCtx.Resolver.Resolve(ctx, ref)
  68. if err != nil {
  69. return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref)
  70. }
  71. fetcher, err := rCtx.Resolver.Fetcher(ctx, name)
  72. if err != nil {
  73. return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name)
  74. }
  75. var (
  76. handler images.Handler
  77. isConvertible bool
  78. converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error)
  79. limiter *semaphore.Weighted
  80. )
  81. if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
  82. schema1Converter := schema1.NewConverter(store, fetcher)
  83. handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
  84. isConvertible = true
  85. converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) {
  86. return schema1Converter.Convert(ctx)
  87. }
  88. } else {
  89. // Get all the children for a descriptor
  90. childrenHandler := images.ChildrenHandler(store)
  91. // Set any children labels for that content
  92. childrenHandler = images.SetChildrenLabels(store, childrenHandler)
  93. // Filter manifests by platforms but allow to handle manifest
  94. // and configuration for not-target platforms
  95. childrenHandler = remotes.FilterManifestByPlatformHandler(childrenHandler, rCtx.PlatformMatcher)
  96. // Sort and limit manifests if a finite number is needed
  97. if limit > 0 {
  98. childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit)
  99. }
  100. // set isConvertible to true if there is application/octet-stream media type
  101. convertibleHandler := images.HandlerFunc(
  102. func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
  103. if desc.MediaType == docker.LegacyConfigMediaType {
  104. isConvertible = true
  105. }
  106. return []ocispec.Descriptor{}, nil
  107. },
  108. )
  109. handlers := append(rCtx.BaseHandlers,
  110. remotes.FetchHandler(store, fetcher),
  111. convertibleHandler,
  112. childrenHandler,
  113. )
  114. // append distribution source label to blob data
  115. if rCtx.AppendDistributionSourceLabel {
  116. appendDistSrcLabelHandler, err := docker.AppendDistributionSourceLabel(store, ref)
  117. if err != nil {
  118. return images.Image{}, err
  119. }
  120. handlers = append(handlers, appendDistSrcLabelHandler)
  121. }
  122. handler = images.Handlers(handlers...)
  123. converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) {
  124. return docker.ConvertManifest(ctx, store, desc)
  125. }
  126. }
  127. if rCtx.HandlerWrapper != nil {
  128. handler = rCtx.HandlerWrapper(handler)
  129. }
  130. if rCtx.MaxConcurrentDownloads > 0 {
  131. limiter = semaphore.NewWeighted(int64(rCtx.MaxConcurrentDownloads))
  132. }
  133. if err := images.Dispatch(ctx, handler, limiter, desc); err != nil {
  134. return images.Image{}, err
  135. }
  136. if isConvertible {
  137. if desc, err = converterFunc(ctx, desc); err != nil {
  138. return images.Image{}, err
  139. }
  140. }
  141. img := images.Image{
  142. Name: name,
  143. Target: desc,
  144. Labels: rCtx.Labels,
  145. }
  146. is := c.ImageService()
  147. for {
  148. if created, err := is.Create(ctx, img); err != nil {
  149. if !errdefs.IsAlreadyExists(err) {
  150. return images.Image{}, err
  151. }
  152. updated, err := is.Update(ctx, img)
  153. if err != nil {
  154. // if image was removed, try create again
  155. if errdefs.IsNotFound(err) {
  156. continue
  157. }
  158. return images.Image{}, err
  159. }
  160. img = updated
  161. } else {
  162. img = created
  163. }
  164. return img, nil
  165. }
  166. }