pull_v2.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. package distribution // import "github.com/docker/docker/distribution"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "os"
  8. "runtime"
  9. "strings"
  10. "time"
  11. "github.com/containerd/containerd/platforms"
  12. "github.com/containerd/log"
  13. "github.com/distribution/reference"
  14. "github.com/docker/distribution"
  15. "github.com/docker/distribution/manifest/manifestlist"
  16. "github.com/docker/distribution/manifest/ocischema"
  17. "github.com/docker/distribution/manifest/schema1"
  18. "github.com/docker/distribution/manifest/schema2"
  19. "github.com/docker/distribution/registry/client/transport"
  20. "github.com/docker/docker/distribution/metadata"
  21. "github.com/docker/docker/distribution/xfer"
  22. "github.com/docker/docker/image"
  23. v1 "github.com/docker/docker/image/v1"
  24. "github.com/docker/docker/layer"
  25. "github.com/docker/docker/pkg/ioutils"
  26. "github.com/docker/docker/pkg/progress"
  27. "github.com/docker/docker/pkg/stringid"
  28. refstore "github.com/docker/docker/reference"
  29. "github.com/docker/docker/registry"
  30. "github.com/opencontainers/go-digest"
  31. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  32. "github.com/pkg/errors"
  33. archvariant "github.com/tonistiigi/go-archvariant"
  34. )
  35. var (
  36. errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
  37. errRootFSInvalid = errors.New("invalid rootfs in image configuration")
  38. )
  39. // imageConfigPullError is an error pulling the image config blob
  40. // (only applies to schema2).
  41. type imageConfigPullError struct {
  42. Err error
  43. }
  44. // Error returns the error string for imageConfigPullError.
  45. func (e imageConfigPullError) Error() string {
  46. return "error pulling image configuration: " + e.Err.Error()
  47. }
  48. // newPuller returns a puller to pull from a v2 registry.
  49. func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, config *ImagePullConfig, local ContentStore) *puller {
  50. return &puller{
  51. metadataService: metadata.NewV2MetadataService(config.MetadataStore),
  52. endpoint: endpoint,
  53. config: config,
  54. repoInfo: repoInfo,
  55. manifestStore: &manifestStore{
  56. local: local,
  57. },
  58. }
  59. }
  60. type puller struct {
  61. metadataService metadata.V2MetadataService
  62. endpoint registry.APIEndpoint
  63. config *ImagePullConfig
  64. repoInfo *registry.RepositoryInfo
  65. repo distribution.Repository
  66. manifestStore *manifestStore
  67. }
  68. func (p *puller) pull(ctx context.Context, ref reference.Named) (err error) {
  69. // TODO(tiborvass): was ReceiveTimeout
  70. p.repo, err = newRepository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
  71. if err != nil {
  72. log.G(ctx).Warnf("Error getting v2 registry: %v", err)
  73. return err
  74. }
  75. p.manifestStore.remote, err = p.repo.Manifests(ctx)
  76. if err != nil {
  77. return err
  78. }
  79. return p.pullRepository(ctx, ref)
  80. }
  81. func (p *puller) pullRepository(ctx context.Context, ref reference.Named) (err error) {
  82. var layersDownloaded bool
  83. if !reference.IsNameOnly(ref) {
  84. layersDownloaded, err = p.pullTag(ctx, ref, p.config.Platform)
  85. if err != nil {
  86. return err
  87. }
  88. } else {
  89. tags, err := p.repo.Tags(ctx).All(ctx)
  90. if err != nil {
  91. return err
  92. }
  93. for _, tag := range tags {
  94. tagRef, err := reference.WithTag(ref, tag)
  95. if err != nil {
  96. return err
  97. }
  98. pulledNew, err := p.pullTag(ctx, tagRef, p.config.Platform)
  99. if err != nil {
  100. // Since this is the pull-all-tags case, don't
  101. // allow an error pulling a particular tag to
  102. // make the whole pull fall back to v1.
  103. if fallbackErr, ok := err.(fallbackError); ok {
  104. return fallbackErr.err
  105. }
  106. return err
  107. }
  108. // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
  109. // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
  110. layersDownloaded = layersDownloaded || pulledNew
  111. }
  112. }
  113. p.writeStatus(reference.FamiliarString(ref), layersDownloaded)
  114. return nil
  115. }
  116. // writeStatus writes a status message to out. If layersDownloaded is true, the
  117. // status message indicates that a newer image was downloaded. Otherwise, it
  118. // indicates that the image is up to date. requestedTag is the tag the message
  119. // will refer to.
  120. func (p *puller) writeStatus(requestedTag string, layersDownloaded bool) {
  121. if layersDownloaded {
  122. progress.Message(p.config.ProgressOutput, "", "Status: Downloaded newer image for "+requestedTag)
  123. } else {
  124. progress.Message(p.config.ProgressOutput, "", "Status: Image is up to date for "+requestedTag)
  125. }
  126. }
  127. type layerDescriptor struct {
  128. digest digest.Digest
  129. diffID layer.DiffID
  130. repoInfo *registry.RepositoryInfo
  131. repo distribution.Repository
  132. metadataService metadata.V2MetadataService
  133. tmpFile *os.File
  134. verifier digest.Verifier
  135. src distribution.Descriptor
  136. }
  137. func (ld *layerDescriptor) Key() string {
  138. return "v2:" + ld.digest.String()
  139. }
  140. func (ld *layerDescriptor) ID() string {
  141. return stringid.TruncateID(ld.digest.String())
  142. }
  143. func (ld *layerDescriptor) DiffID() (layer.DiffID, error) {
  144. if ld.diffID != "" {
  145. return ld.diffID, nil
  146. }
  147. return ld.metadataService.GetDiffID(ld.digest)
  148. }
  149. func (ld *layerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
  150. log.G(ctx).Debugf("pulling blob %q", ld.digest)
  151. var (
  152. err error
  153. offset int64
  154. )
  155. if ld.tmpFile == nil {
  156. ld.tmpFile, err = createDownloadFile()
  157. if err != nil {
  158. return nil, 0, xfer.DoNotRetry{Err: err}
  159. }
  160. } else {
  161. offset, err = ld.tmpFile.Seek(0, io.SeekEnd)
  162. if err != nil {
  163. log.G(ctx).Debugf("error seeking to end of download file: %v", err)
  164. offset = 0
  165. ld.tmpFile.Close()
  166. if err := os.Remove(ld.tmpFile.Name()); err != nil {
  167. log.G(ctx).Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  168. }
  169. ld.tmpFile, err = createDownloadFile()
  170. if err != nil {
  171. return nil, 0, xfer.DoNotRetry{Err: err}
  172. }
  173. } else if offset != 0 {
  174. log.G(ctx).Debugf("attempting to resume download of %q from %d bytes", ld.digest, offset)
  175. }
  176. }
  177. tmpFile := ld.tmpFile
  178. layerDownload, err := ld.open(ctx)
  179. if err != nil {
  180. log.G(ctx).Errorf("Error initiating layer download: %v", err)
  181. return nil, 0, retryOnError(err)
  182. }
  183. if offset != 0 {
  184. _, err := layerDownload.Seek(offset, io.SeekStart)
  185. if err != nil {
  186. if err := ld.truncateDownloadFile(); err != nil {
  187. return nil, 0, xfer.DoNotRetry{Err: err}
  188. }
  189. return nil, 0, err
  190. }
  191. }
  192. size, err := layerDownload.Seek(0, io.SeekEnd)
  193. if err != nil {
  194. // Seek failed, perhaps because there was no Content-Length
  195. // header. This shouldn't fail the download, because we can
  196. // still continue without a progress bar.
  197. size = 0
  198. } else {
  199. if size != 0 && offset > size {
  200. log.G(ctx).Debug("Partial download is larger than full blob. Starting over")
  201. offset = 0
  202. if err := ld.truncateDownloadFile(); err != nil {
  203. return nil, 0, xfer.DoNotRetry{Err: err}
  204. }
  205. }
  206. // Restore the seek offset either at the beginning of the
  207. // stream, or just after the last byte we have from previous
  208. // attempts.
  209. _, err = layerDownload.Seek(offset, io.SeekStart)
  210. if err != nil {
  211. return nil, 0, err
  212. }
  213. }
  214. reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerDownload), progressOutput, size-offset, ld.ID(), "Downloading")
  215. defer reader.Close()
  216. if ld.verifier == nil {
  217. ld.verifier = ld.digest.Verifier()
  218. }
  219. _, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
  220. if err != nil {
  221. if err == transport.ErrWrongCodeForByteRange {
  222. if err := ld.truncateDownloadFile(); err != nil {
  223. return nil, 0, xfer.DoNotRetry{Err: err}
  224. }
  225. return nil, 0, err
  226. }
  227. return nil, 0, retryOnError(err)
  228. }
  229. progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
  230. if !ld.verifier.Verified() {
  231. err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
  232. log.G(ctx).Error(err)
  233. // Allow a retry if this digest verification error happened
  234. // after a resumed download.
  235. if offset != 0 {
  236. if err := ld.truncateDownloadFile(); err != nil {
  237. return nil, 0, xfer.DoNotRetry{Err: err}
  238. }
  239. return nil, 0, err
  240. }
  241. return nil, 0, xfer.DoNotRetry{Err: err}
  242. }
  243. progress.Update(progressOutput, ld.ID(), "Download complete")
  244. log.G(ctx).Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
  245. _, err = tmpFile.Seek(0, io.SeekStart)
  246. if err != nil {
  247. tmpFile.Close()
  248. if err := os.Remove(tmpFile.Name()); err != nil {
  249. log.G(ctx).Errorf("Failed to remove temp file: %s", tmpFile.Name())
  250. }
  251. ld.tmpFile = nil
  252. ld.verifier = nil
  253. return nil, 0, xfer.DoNotRetry{Err: err}
  254. }
  255. // hand off the temporary file to the download manager, so it will only
  256. // be closed once
  257. ld.tmpFile = nil
  258. return ioutils.NewReadCloserWrapper(tmpFile, func() error {
  259. tmpFile.Close()
  260. err := os.RemoveAll(tmpFile.Name())
  261. if err != nil {
  262. log.G(ctx).Errorf("Failed to remove temp file: %s", tmpFile.Name())
  263. }
  264. return err
  265. }), size, nil
  266. }
  267. func (ld *layerDescriptor) Close() {
  268. if ld.tmpFile != nil {
  269. ld.tmpFile.Close()
  270. if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
  271. log.G(context.TODO()).Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  272. }
  273. }
  274. }
  275. func (ld *layerDescriptor) truncateDownloadFile() error {
  276. // Need a new hash context since we will be redoing the download
  277. ld.verifier = nil
  278. if _, err := ld.tmpFile.Seek(0, io.SeekStart); err != nil {
  279. log.G(context.TODO()).Errorf("error seeking to beginning of download file: %v", err)
  280. return err
  281. }
  282. if err := ld.tmpFile.Truncate(0); err != nil {
  283. log.G(context.TODO()).Errorf("error truncating download file: %v", err)
  284. return err
  285. }
  286. return nil
  287. }
  288. func (ld *layerDescriptor) Registered(diffID layer.DiffID) {
  289. // Cache mapping from this layer's DiffID to the blobsum
  290. _ = ld.metadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
  291. }
  292. func (p *puller) pullTag(ctx context.Context, ref reference.Named, platform *ocispec.Platform) (tagUpdated bool, err error) {
  293. var (
  294. tagOrDigest string // Used for logging/progress only
  295. dgst digest.Digest
  296. mt string
  297. size int64
  298. tagged reference.NamedTagged
  299. isTagged bool
  300. )
  301. if digested, isDigested := ref.(reference.Canonical); isDigested {
  302. dgst = digested.Digest()
  303. tagOrDigest = digested.String()
  304. } else if tagged, isTagged = ref.(reference.NamedTagged); isTagged {
  305. tagService := p.repo.Tags(ctx)
  306. desc, err := tagService.Get(ctx, tagged.Tag())
  307. if err != nil {
  308. return false, err
  309. }
  310. dgst = desc.Digest
  311. tagOrDigest = tagged.Tag()
  312. mt = desc.MediaType
  313. size = desc.Size
  314. } else {
  315. return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
  316. }
  317. ctx = log.WithLogger(ctx, log.G(ctx).WithFields(log.Fields{
  318. "digest": dgst,
  319. "remote": ref,
  320. }))
  321. desc := ocispec.Descriptor{
  322. MediaType: mt,
  323. Digest: dgst,
  324. Size: size,
  325. }
  326. manifest, err := p.manifestStore.Get(ctx, desc, ref)
  327. if err != nil {
  328. if isTagged && isNotFound(errors.Cause(err)) {
  329. log.G(ctx).WithField("ref", ref).WithError(err).Debug("Falling back to pull manifest by tag")
  330. msg := `%s Failed to pull manifest by the resolved digest. This registry does not
  331. appear to conform to the distribution registry specification; falling back to
  332. pull by tag. This fallback is DEPRECATED, and will be removed in a future
  333. release. Please contact admins of %s. %s
  334. `
  335. warnEmoji := "\U000026A0\U0000FE0F"
  336. progress.Messagef(p.config.ProgressOutput, "WARNING", msg, warnEmoji, p.endpoint.URL, warnEmoji)
  337. // Fetch by tag worked, but fetch by digest didn't.
  338. // This is a broken registry implementation.
  339. // We'll fallback to the old behavior and get the manifest by tag.
  340. var ms distribution.ManifestService
  341. ms, err = p.repo.Manifests(ctx)
  342. if err != nil {
  343. return false, err
  344. }
  345. manifest, err = ms.Get(ctx, "", distribution.WithTag(tagged.Tag()))
  346. err = errors.Wrap(err, "error after falling back to get manifest by tag")
  347. }
  348. if err != nil {
  349. return false, err
  350. }
  351. }
  352. if manifest == nil {
  353. return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
  354. }
  355. if m, ok := manifest.(*schema2.DeserializedManifest); ok {
  356. if err := p.validateMediaType(m.Manifest.Config.MediaType); err != nil {
  357. return false, err
  358. }
  359. }
  360. log.G(ctx).Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
  361. progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
  362. var (
  363. id digest.Digest
  364. manifestDigest digest.Digest
  365. )
  366. switch v := manifest.(type) {
  367. case *schema1.SignedManifest:
  368. msg := DeprecatedSchema1ImageMessage(ref)
  369. log.G(ctx).Warn(msg)
  370. progress.Message(p.config.ProgressOutput, "", msg)
  371. id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
  372. if err != nil {
  373. return false, err
  374. }
  375. case *schema2.DeserializedManifest:
  376. id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform)
  377. if err != nil {
  378. return false, err
  379. }
  380. case *ocischema.DeserializedManifest:
  381. id, manifestDigest, err = p.pullOCI(ctx, ref, v, platform)
  382. if err != nil {
  383. return false, err
  384. }
  385. case *manifestlist.DeserializedManifestList:
  386. id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
  387. if err != nil {
  388. return false, err
  389. }
  390. default:
  391. return false, invalidManifestFormatError{}
  392. }
  393. progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
  394. if p.config.ReferenceStore != nil {
  395. oldTagID, err := p.config.ReferenceStore.Get(ref)
  396. if err == nil {
  397. if oldTagID == id {
  398. return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
  399. }
  400. } else if err != refstore.ErrDoesNotExist {
  401. return false, err
  402. }
  403. if canonical, ok := ref.(reference.Canonical); ok {
  404. if err = p.config.ReferenceStore.AddDigest(canonical, id, true); err != nil {
  405. return false, err
  406. }
  407. } else {
  408. if err = addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil {
  409. return false, err
  410. }
  411. if err = p.config.ReferenceStore.AddTag(ref, id, true); err != nil {
  412. return false, err
  413. }
  414. }
  415. }
  416. return true, nil
  417. }
  418. // validateMediaType validates if the given mediaType is accepted by the puller's
  419. // configuration.
  420. func (p *puller) validateMediaType(mediaType string) error {
  421. var allowedMediaTypes []string
  422. if len(p.config.Schema2Types) > 0 {
  423. allowedMediaTypes = p.config.Schema2Types
  424. } else {
  425. allowedMediaTypes = defaultImageTypes
  426. }
  427. for _, t := range allowedMediaTypes {
  428. if mediaType == t {
  429. return nil
  430. }
  431. }
  432. configClass := mediaTypeClasses[mediaType]
  433. if configClass == "" {
  434. configClass = "unknown"
  435. }
  436. return invalidManifestClassError{mediaType, configClass}
  437. }
  438. func (p *puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *ocispec.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  439. if platform != nil {
  440. // Early bath if the requested OS doesn't match that of the configuration.
  441. // This avoids doing the download, only to potentially fail later.
  442. if err := image.CheckOS(platform.OS); err != nil {
  443. return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", runtime.GOOS, platform.OS)
  444. }
  445. }
  446. var verifiedManifest *schema1.Manifest
  447. verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
  448. if err != nil {
  449. return "", "", err
  450. }
  451. rootFS := image.NewRootFS()
  452. // remove duplicate layers and check parent chain validity
  453. err = fixManifestLayers(verifiedManifest)
  454. if err != nil {
  455. return "", "", err
  456. }
  457. var descriptors []xfer.DownloadDescriptor
  458. // Image history converted to the new format
  459. var history []image.History
  460. // Note that the order of this loop is in the direction of bottom-most
  461. // to top-most, so that the downloads slice gets ordered correctly.
  462. for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
  463. blobSum := verifiedManifest.FSLayers[i].BlobSum
  464. if err = blobSum.Validate(); err != nil {
  465. return "", "", errors.Wrapf(err, "could not validate layer digest %q", blobSum)
  466. }
  467. var throwAway struct {
  468. ThrowAway bool `json:"throwaway,omitempty"`
  469. }
  470. if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
  471. return "", "", err
  472. }
  473. h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
  474. if err != nil {
  475. return "", "", err
  476. }
  477. history = append(history, h)
  478. if throwAway.ThrowAway {
  479. continue
  480. }
  481. layerDescriptor := &layerDescriptor{
  482. digest: blobSum,
  483. repoInfo: p.repoInfo,
  484. repo: p.repo,
  485. metadataService: p.metadataService,
  486. }
  487. descriptors = append(descriptors, layerDescriptor)
  488. }
  489. resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
  490. if err != nil {
  491. return "", "", err
  492. }
  493. defer release()
  494. config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
  495. if err != nil {
  496. return "", "", err
  497. }
  498. imageID, err := p.config.ImageStore.Put(ctx, config)
  499. if err != nil {
  500. return "", "", err
  501. }
  502. manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
  503. return imageID, manifestDigest, nil
  504. }
  505. func checkSupportedMediaType(mediaType string) error {
  506. lowerMt := strings.ToLower(mediaType)
  507. for _, mt := range supportedMediaTypes {
  508. // The should either be an exact match, or have a valid prefix
  509. // we append a "." when matching prefixes to exclude "false positives";
  510. // for example, we don't want to match "application/vnd.oci.images_are_fun_yolo".
  511. if lowerMt == mt || strings.HasPrefix(lowerMt, mt+".") {
  512. return nil
  513. }
  514. }
  515. return unsupportedMediaTypeError{MediaType: mediaType}
  516. }
  517. func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *ocispec.Platform) (id digest.Digest, err error) {
  518. if _, err := p.config.ImageStore.Get(ctx, target.Digest); err == nil {
  519. // If the image already exists locally, no need to pull
  520. // anything.
  521. return target.Digest, nil
  522. }
  523. if err := checkSupportedMediaType(target.MediaType); err != nil {
  524. return "", err
  525. }
  526. var descriptors []xfer.DownloadDescriptor
  527. // Note that the order of this loop is in the direction of bottom-most
  528. // to top-most, so that the downloads slice gets ordered correctly.
  529. for _, d := range layers {
  530. if err := d.Digest.Validate(); err != nil {
  531. return "", errors.Wrapf(err, "could not validate layer digest %q", d.Digest)
  532. }
  533. if err := checkSupportedMediaType(d.MediaType); err != nil {
  534. return "", err
  535. }
  536. layerDescriptor := &layerDescriptor{
  537. digest: d.Digest,
  538. repo: p.repo,
  539. repoInfo: p.repoInfo,
  540. metadataService: p.metadataService,
  541. src: d,
  542. }
  543. descriptors = append(descriptors, layerDescriptor)
  544. }
  545. configChan := make(chan []byte, 1)
  546. configErrChan := make(chan error, 1)
  547. layerErrChan := make(chan error, 1)
  548. downloadsDone := make(chan struct{})
  549. var cancel func()
  550. ctx, cancel = context.WithCancel(ctx)
  551. defer cancel()
  552. // Pull the image config
  553. go func() {
  554. configJSON, err := p.pullSchema2Config(ctx, target.Digest)
  555. if err != nil {
  556. configErrChan <- imageConfigPullError{Err: err}
  557. cancel()
  558. return
  559. }
  560. configChan <- configJSON
  561. }()
  562. var (
  563. configJSON []byte // raw serialized image config
  564. downloadedRootFS *image.RootFS // rootFS from registered layers
  565. configRootFS *image.RootFS // rootFS from configuration
  566. release func() // release resources from rootFS download
  567. configPlatform *ocispec.Platform // for LCOW when registering downloaded layers
  568. )
  569. layerStoreOS := runtime.GOOS
  570. if platform != nil {
  571. layerStoreOS = platform.OS
  572. }
  573. // https://github.com/docker/docker/issues/24766 - Err on the side of caution,
  574. // explicitly blocking images intended for linux from the Windows daemon. On
  575. // Windows, we do this before the attempt to download, effectively serialising
  576. // the download slightly slowing it down. We have to do it this way, as
  577. // chances are the download of layers itself would fail due to file names
  578. // which aren't suitable for NTFS. At some point in the future, if a similar
  579. // check to block Windows images being pulled on Linux is implemented, it
  580. // may be necessary to perform the same type of serialisation.
  581. if runtime.GOOS == "windows" {
  582. configJSON, configRootFS, configPlatform, err = receiveConfig(configChan, configErrChan)
  583. if err != nil {
  584. return "", err
  585. }
  586. if configRootFS == nil {
  587. return "", errRootFSInvalid
  588. }
  589. if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil {
  590. return "", err
  591. }
  592. if len(descriptors) != len(configRootFS.DiffIDs) {
  593. return "", errRootFSMismatch
  594. }
  595. if platform == nil {
  596. // Early bath if the requested OS doesn't match that of the configuration.
  597. // This avoids doing the download, only to potentially fail later.
  598. if err := image.CheckOS(configPlatform.OS); err != nil {
  599. return "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
  600. }
  601. layerStoreOS = configPlatform.OS
  602. }
  603. // Populate diff ids in descriptors to avoid downloading foreign layers
  604. // which have been side loaded
  605. for i := range descriptors {
  606. descriptors[i].(*layerDescriptor).diffID = configRootFS.DiffIDs[i]
  607. }
  608. }
  609. // Assume that the operating system is the host OS if blank, and validate it
  610. // to ensure we don't cause a panic by an invalid index into the layerstores.
  611. if layerStoreOS != "" {
  612. if err := image.CheckOS(layerStoreOS); err != nil {
  613. return "", err
  614. }
  615. }
  616. if p.config.DownloadManager != nil {
  617. go func() {
  618. var (
  619. err error
  620. rootFS image.RootFS
  621. )
  622. downloadRootFS := *image.NewRootFS()
  623. rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
  624. if err != nil {
  625. // Intentionally do not cancel the config download here
  626. // as the error from config download (if there is one)
  627. // is more interesting than the layer download error
  628. layerErrChan <- err
  629. return
  630. }
  631. downloadedRootFS = &rootFS
  632. close(downloadsDone)
  633. }()
  634. } else {
  635. // We have nothing to download
  636. close(downloadsDone)
  637. }
  638. if configJSON == nil {
  639. configJSON, configRootFS, _, err = receiveConfig(configChan, configErrChan)
  640. if err == nil && configRootFS == nil {
  641. err = errRootFSInvalid
  642. }
  643. if err != nil {
  644. cancel()
  645. select {
  646. case <-downloadsDone:
  647. case <-layerErrChan:
  648. }
  649. return "", err
  650. }
  651. }
  652. select {
  653. case <-downloadsDone:
  654. case err = <-layerErrChan:
  655. return "", err
  656. }
  657. if release != nil {
  658. defer release()
  659. }
  660. if downloadedRootFS != nil {
  661. // The DiffIDs returned in rootFS MUST match those in the config.
  662. // Otherwise the image config could be referencing layers that aren't
  663. // included in the manifest.
  664. if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) {
  665. return "", errRootFSMismatch
  666. }
  667. for i := range downloadedRootFS.DiffIDs {
  668. if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] {
  669. return "", errRootFSMismatch
  670. }
  671. }
  672. }
  673. imageID, err := p.config.ImageStore.Put(ctx, configJSON)
  674. if err != nil {
  675. return "", err
  676. }
  677. return imageID, nil
  678. }
  679. func (p *puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *ocispec.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  680. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  681. if err != nil {
  682. return "", "", err
  683. }
  684. id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
  685. return id, manifestDigest, err
  686. }
  687. func (p *puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *ocispec.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  688. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  689. if err != nil {
  690. return "", "", err
  691. }
  692. id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
  693. return id, manifestDigest, err
  694. }
  695. func receiveConfig(configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *ocispec.Platform, error) {
  696. select {
  697. case configJSON := <-configChan:
  698. rootfs, err := rootFSFromConfig(configJSON)
  699. if err != nil {
  700. return nil, nil, nil, err
  701. }
  702. platform, err := platformFromConfig(configJSON)
  703. if err != nil {
  704. return nil, nil, nil, err
  705. }
  706. return configJSON, rootfs, platform, nil
  707. case err := <-errChan:
  708. return nil, nil, nil, err
  709. // Don't need a case for ctx.Done in the select because cancellation
  710. // will trigger an error in p.pullSchema2ImageConfig.
  711. }
  712. }
  713. // pullManifestList handles "manifest lists" which point to various
  714. // platform-specific manifests.
  715. func (p *puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *ocispec.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) {
  716. manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
  717. if err != nil {
  718. return "", "", err
  719. }
  720. var platform ocispec.Platform
  721. if pp != nil {
  722. platform = *pp
  723. }
  724. log.G(ctx).Debugf("%s resolved to a manifestList object with %d entries; looking for a %s match", ref, len(mfstList.Manifests), platforms.Format(platform))
  725. manifestMatches := filterManifests(mfstList.Manifests, platform)
  726. for _, match := range manifestMatches {
  727. if err := checkImageCompatibility(match.Platform.OS, match.Platform.OSVersion); err != nil {
  728. return "", "", err
  729. }
  730. desc := ocispec.Descriptor{
  731. Digest: match.Digest,
  732. Size: match.Size,
  733. MediaType: match.MediaType,
  734. }
  735. manifest, err := p.manifestStore.Get(ctx, desc, ref)
  736. if err != nil {
  737. return "", "", err
  738. }
  739. manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), match.Digest)
  740. if err != nil {
  741. return "", "", err
  742. }
  743. switch v := manifest.(type) {
  744. case *schema1.SignedManifest:
  745. msg := DeprecatedSchema1ImageMessage(ref)
  746. log.G(ctx).Warn(msg)
  747. progress.Message(p.config.ProgressOutput, "", msg)
  748. platform := toOCIPlatform(match.Platform)
  749. id, _, err = p.pullSchema1(ctx, manifestRef, v, platform)
  750. if err != nil {
  751. return "", "", err
  752. }
  753. case *schema2.DeserializedManifest:
  754. platform := toOCIPlatform(match.Platform)
  755. id, _, err = p.pullSchema2(ctx, manifestRef, v, platform)
  756. if err != nil {
  757. return "", "", err
  758. }
  759. case *ocischema.DeserializedManifest:
  760. platform := toOCIPlatform(match.Platform)
  761. id, _, err = p.pullOCI(ctx, manifestRef, v, platform)
  762. if err != nil {
  763. return "", "", err
  764. }
  765. case *manifestlist.DeserializedManifestList:
  766. id, _, err = p.pullManifestList(ctx, manifestRef, v, pp)
  767. if err != nil {
  768. var noMatches noMatchesErr
  769. if !errors.As(err, &noMatches) {
  770. // test the next match
  771. continue
  772. }
  773. }
  774. default:
  775. // OCI spec requires to skip unknown manifest types
  776. continue
  777. }
  778. return id, manifestListDigest, err
  779. }
  780. return "", "", noMatchesErr{platform: platform}
  781. }
  782. const (
  783. defaultSchemaPullBackoff = 250 * time.Millisecond
  784. defaultMaxSchemaPullAttempts = 5
  785. )
  786. func (p *puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
  787. blobs := p.repo.Blobs(ctx)
  788. err = retry(ctx, defaultMaxSchemaPullAttempts, defaultSchemaPullBackoff, func(ctx context.Context) (err error) {
  789. configJSON, err = blobs.Get(ctx, dgst)
  790. return err
  791. })
  792. if err != nil {
  793. return nil, err
  794. }
  795. // Verify image config digest
  796. verifier := dgst.Verifier()
  797. if _, err := verifier.Write(configJSON); err != nil {
  798. return nil, err
  799. }
  800. if !verifier.Verified() {
  801. err := fmt.Errorf("image config verification failed for digest %s", dgst)
  802. log.G(ctx).Error(err)
  803. return nil, err
  804. }
  805. return configJSON, nil
  806. }
  807. type noMatchesErr struct {
  808. platform ocispec.Platform
  809. }
  810. func (e noMatchesErr) Error() string {
  811. return fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(e.platform))
  812. }
  813. func retry(ctx context.Context, maxAttempts int, sleep time.Duration, f func(ctx context.Context) error) (err error) {
  814. attempt := 0
  815. for ; attempt < maxAttempts; attempt++ {
  816. err = retryOnError(f(ctx))
  817. if err == nil {
  818. return nil
  819. }
  820. if xfer.IsDoNotRetryError(err) {
  821. break
  822. }
  823. if attempt+1 < maxAttempts {
  824. timer := time.NewTimer(sleep)
  825. select {
  826. case <-ctx.Done():
  827. timer.Stop()
  828. return ctx.Err()
  829. case <-timer.C:
  830. log.G(ctx).WithError(err).WithField("attempts", attempt+1).Debug("retrying after error")
  831. sleep *= 2
  832. }
  833. }
  834. }
  835. return errors.Wrapf(err, "download failed after attempts=%d", attempt+1)
  836. }
  837. // schema2ManifestDigest computes the manifest digest, and, if pulling by
  838. // digest, ensures that it matches the requested digest.
  839. func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
  840. _, canonical, err := mfst.Payload()
  841. if err != nil {
  842. return "", err
  843. }
  844. // If pull by digest, then verify the manifest digest.
  845. if digested, isDigested := ref.(reference.Canonical); isDigested {
  846. verifier := digested.Digest().Verifier()
  847. if _, err := verifier.Write(canonical); err != nil {
  848. return "", err
  849. }
  850. if !verifier.Verified() {
  851. err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
  852. log.G(context.TODO()).Error(err)
  853. return "", err
  854. }
  855. return digested.Digest(), nil
  856. }
  857. return digest.FromBytes(canonical), nil
  858. }
  859. func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
  860. // If pull by digest, then verify the manifest digest. NOTE: It is
  861. // important to do this first, before any other content validation. If the
  862. // digest cannot be verified, don't even bother with those other things.
  863. if digested, isCanonical := ref.(reference.Canonical); isCanonical {
  864. verifier := digested.Digest().Verifier()
  865. if _, err := verifier.Write(signedManifest.Canonical); err != nil {
  866. return nil, err
  867. }
  868. if !verifier.Verified() {
  869. err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
  870. log.G(context.TODO()).Error(err)
  871. return nil, err
  872. }
  873. }
  874. m = &signedManifest.Manifest
  875. if m.SchemaVersion != 1 {
  876. return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
  877. }
  878. if len(m.FSLayers) != len(m.History) {
  879. return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
  880. }
  881. if len(m.FSLayers) == 0 {
  882. return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
  883. }
  884. return m, nil
  885. }
  886. // fixManifestLayers removes repeated layers from the manifest and checks the
  887. // correctness of the parent chain.
  888. func fixManifestLayers(m *schema1.Manifest) error {
  889. imgs := make([]*image.V1Image, len(m.FSLayers))
  890. for i := range m.FSLayers {
  891. img := &image.V1Image{}
  892. if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
  893. return err
  894. }
  895. imgs[i] = img
  896. if err := v1.ValidateID(img.ID); err != nil {
  897. return err
  898. }
  899. }
  900. if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
  901. // Windows base layer can point to a base layer parent that is not in manifest.
  902. return errors.New("invalid parent ID in the base layer of the image")
  903. }
  904. // check general duplicates to error instead of a deadlock
  905. idmap := make(map[string]struct{})
  906. var lastID string
  907. for _, img := range imgs {
  908. // skip IDs that appear after each other, we handle those later
  909. if _, exists := idmap[img.ID]; img.ID != lastID && exists {
  910. return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
  911. }
  912. lastID = img.ID
  913. idmap[lastID] = struct{}{}
  914. }
  915. // backwards loop so that we keep the remaining indexes after removing items
  916. for i := len(imgs) - 2; i >= 0; i-- {
  917. if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
  918. m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
  919. m.History = append(m.History[:i], m.History[i+1:]...)
  920. } else if imgs[i].Parent != imgs[i+1].ID {
  921. return fmt.Errorf("invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent)
  922. }
  923. }
  924. return nil
  925. }
  926. func createDownloadFile() (*os.File, error) {
  927. return os.CreateTemp("", "GetImageBlob")
  928. }
  929. func toOCIPlatform(p manifestlist.PlatformSpec) *ocispec.Platform {
  930. // distribution pkg does define platform as pointer so this hack for empty struct
  931. // is necessary. This is temporary until correct OCI image-spec package is used.
  932. if p.OS == "" && p.Architecture == "" && p.Variant == "" && p.OSVersion == "" && p.OSFeatures == nil && p.Features == nil {
  933. return nil
  934. }
  935. return &ocispec.Platform{
  936. OS: p.OS,
  937. Architecture: p.Architecture,
  938. Variant: p.Variant,
  939. OSFeatures: p.OSFeatures,
  940. OSVersion: p.OSVersion,
  941. }
  942. }
  943. // maximumSpec returns the distribution platform with maximum compatibility for the current node.
  944. func maximumSpec() ocispec.Platform {
  945. p := platforms.DefaultSpec()
  946. if p.Architecture == "amd64" {
  947. p.Variant = archvariant.AMD64Variant()
  948. }
  949. return p
  950. }