pull_v2.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867
  1. package distribution
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/url"
  9. "os"
  10. "runtime"
  11. "github.com/Sirupsen/logrus"
  12. "github.com/docker/distribution"
  13. "github.com/docker/distribution/manifest/manifestlist"
  14. "github.com/docker/distribution/manifest/schema1"
  15. "github.com/docker/distribution/manifest/schema2"
  16. "github.com/docker/distribution/reference"
  17. "github.com/docker/distribution/registry/api/errcode"
  18. "github.com/docker/distribution/registry/client/auth"
  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. "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. "golang.org/x/net/context"
  32. )
  33. var (
  34. errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
  35. errRootFSInvalid = errors.New("invalid rootfs in image configuration")
  36. )
  37. // ImageConfigPullError is an error pulling the image config blob
  38. // (only applies to schema2).
  39. type ImageConfigPullError struct {
  40. Err error
  41. }
  42. // Error returns the error string for ImageConfigPullError.
  43. func (e ImageConfigPullError) Error() string {
  44. return "error pulling image configuration: " + e.Err.Error()
  45. }
  46. type v2Puller struct {
  47. V2MetadataService metadata.V2MetadataService
  48. endpoint registry.APIEndpoint
  49. config *ImagePullConfig
  50. repoInfo *registry.RepositoryInfo
  51. repo distribution.Repository
  52. // confirmedV2 is set to true if we confirm we're talking to a v2
  53. // registry. This is used to limit fallbacks to the v1 protocol.
  54. confirmedV2 bool
  55. }
  56. func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
  57. // TODO(tiborvass): was ReceiveTimeout
  58. p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
  59. if err != nil {
  60. logrus.Warnf("Error getting v2 registry: %v", err)
  61. return err
  62. }
  63. if err = p.pullV2Repository(ctx, ref); err != nil {
  64. if _, ok := err.(fallbackError); ok {
  65. return err
  66. }
  67. if continueOnError(err) {
  68. logrus.Errorf("Error trying v2 registry: %v", err)
  69. return fallbackError{
  70. err: err,
  71. confirmedV2: p.confirmedV2,
  72. transportOK: true,
  73. }
  74. }
  75. }
  76. return err
  77. }
  78. func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) {
  79. var layersDownloaded bool
  80. if !reference.IsNameOnly(ref) {
  81. layersDownloaded, err = p.pullV2Tag(ctx, ref)
  82. if err != nil {
  83. return err
  84. }
  85. } else {
  86. tags, err := p.repo.Tags(ctx).All(ctx)
  87. if err != nil {
  88. // If this repository doesn't exist on V2, we should
  89. // permit a fallback to V1.
  90. return allowV1Fallback(err)
  91. }
  92. // The v2 registry knows about this repository, so we will not
  93. // allow fallback to the v1 protocol even if we encounter an
  94. // error later on.
  95. p.confirmedV2 = true
  96. for _, tag := range tags {
  97. tagRef, err := reference.WithTag(ref, tag)
  98. if err != nil {
  99. return err
  100. }
  101. pulledNew, err := p.pullV2Tag(ctx, tagRef)
  102. if err != nil {
  103. // Since this is the pull-all-tags case, don't
  104. // allow an error pulling a particular tag to
  105. // make the whole pull fall back to v1.
  106. if fallbackErr, ok := err.(fallbackError); ok {
  107. return fallbackErr.err
  108. }
  109. return err
  110. }
  111. // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
  112. // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
  113. layersDownloaded = layersDownloaded || pulledNew
  114. }
  115. }
  116. writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
  117. return nil
  118. }
  119. type v2LayerDescriptor struct {
  120. digest digest.Digest
  121. repoInfo *registry.RepositoryInfo
  122. repo distribution.Repository
  123. V2MetadataService metadata.V2MetadataService
  124. tmpFile *os.File
  125. verifier digest.Verifier
  126. src distribution.Descriptor
  127. }
  128. func (ld *v2LayerDescriptor) Key() string {
  129. return "v2:" + ld.digest.String()
  130. }
  131. func (ld *v2LayerDescriptor) ID() string {
  132. return stringid.TruncateID(ld.digest.String())
  133. }
  134. func (ld *v2LayerDescriptor) DiffID() (layer.DiffID, error) {
  135. return ld.V2MetadataService.GetDiffID(ld.digest)
  136. }
  137. func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
  138. logrus.Debugf("pulling blob %q", ld.digest)
  139. var (
  140. err error
  141. offset int64
  142. )
  143. if ld.tmpFile == nil {
  144. ld.tmpFile, err = createDownloadFile()
  145. if err != nil {
  146. return nil, 0, xfer.DoNotRetry{Err: err}
  147. }
  148. } else {
  149. offset, err = ld.tmpFile.Seek(0, os.SEEK_END)
  150. if err != nil {
  151. logrus.Debugf("error seeking to end of download file: %v", err)
  152. offset = 0
  153. ld.tmpFile.Close()
  154. if err := os.Remove(ld.tmpFile.Name()); err != nil {
  155. logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  156. }
  157. ld.tmpFile, err = createDownloadFile()
  158. if err != nil {
  159. return nil, 0, xfer.DoNotRetry{Err: err}
  160. }
  161. } else if offset != 0 {
  162. logrus.Debugf("attempting to resume download of %q from %d bytes", ld.digest, offset)
  163. }
  164. }
  165. tmpFile := ld.tmpFile
  166. layerDownload, err := ld.open(ctx)
  167. if err != nil {
  168. logrus.Errorf("Error initiating layer download: %v", err)
  169. return nil, 0, retryOnError(err)
  170. }
  171. if offset != 0 {
  172. _, err := layerDownload.Seek(offset, os.SEEK_SET)
  173. if err != nil {
  174. if err := ld.truncateDownloadFile(); err != nil {
  175. return nil, 0, xfer.DoNotRetry{Err: err}
  176. }
  177. return nil, 0, err
  178. }
  179. }
  180. size, err := layerDownload.Seek(0, os.SEEK_END)
  181. if err != nil {
  182. // Seek failed, perhaps because there was no Content-Length
  183. // header. This shouldn't fail the download, because we can
  184. // still continue without a progress bar.
  185. size = 0
  186. } else {
  187. if size != 0 && offset > size {
  188. logrus.Debug("Partial download is larger than full blob. Starting over")
  189. offset = 0
  190. if err := ld.truncateDownloadFile(); err != nil {
  191. return nil, 0, xfer.DoNotRetry{Err: err}
  192. }
  193. }
  194. // Restore the seek offset either at the beginning of the
  195. // stream, or just after the last byte we have from previous
  196. // attempts.
  197. _, err = layerDownload.Seek(offset, os.SEEK_SET)
  198. if err != nil {
  199. return nil, 0, err
  200. }
  201. }
  202. reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerDownload), progressOutput, size-offset, ld.ID(), "Downloading")
  203. defer reader.Close()
  204. if ld.verifier == nil {
  205. ld.verifier = ld.digest.Verifier()
  206. }
  207. _, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
  208. if err != nil {
  209. if err == transport.ErrWrongCodeForByteRange {
  210. if err := ld.truncateDownloadFile(); err != nil {
  211. return nil, 0, xfer.DoNotRetry{Err: err}
  212. }
  213. return nil, 0, err
  214. }
  215. return nil, 0, retryOnError(err)
  216. }
  217. progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
  218. if !ld.verifier.Verified() {
  219. err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
  220. logrus.Error(err)
  221. // Allow a retry if this digest verification error happened
  222. // after a resumed download.
  223. if offset != 0 {
  224. if err := ld.truncateDownloadFile(); err != nil {
  225. return nil, 0, xfer.DoNotRetry{Err: err}
  226. }
  227. return nil, 0, err
  228. }
  229. return nil, 0, xfer.DoNotRetry{Err: err}
  230. }
  231. progress.Update(progressOutput, ld.ID(), "Download complete")
  232. logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
  233. _, err = tmpFile.Seek(0, os.SEEK_SET)
  234. if err != nil {
  235. tmpFile.Close()
  236. if err := os.Remove(tmpFile.Name()); err != nil {
  237. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  238. }
  239. ld.tmpFile = nil
  240. ld.verifier = nil
  241. return nil, 0, xfer.DoNotRetry{Err: err}
  242. }
  243. // hand off the temporary file to the download manager, so it will only
  244. // be closed once
  245. ld.tmpFile = nil
  246. return ioutils.NewReadCloserWrapper(tmpFile, func() error {
  247. tmpFile.Close()
  248. err := os.RemoveAll(tmpFile.Name())
  249. if err != nil {
  250. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  251. }
  252. return err
  253. }), size, nil
  254. }
  255. func (ld *v2LayerDescriptor) Close() {
  256. if ld.tmpFile != nil {
  257. ld.tmpFile.Close()
  258. if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
  259. logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  260. }
  261. }
  262. }
  263. func (ld *v2LayerDescriptor) truncateDownloadFile() error {
  264. // Need a new hash context since we will be redoing the download
  265. ld.verifier = nil
  266. if _, err := ld.tmpFile.Seek(0, os.SEEK_SET); err != nil {
  267. logrus.Errorf("error seeking to beginning of download file: %v", err)
  268. return err
  269. }
  270. if err := ld.tmpFile.Truncate(0); err != nil {
  271. logrus.Errorf("error truncating download file: %v", err)
  272. return err
  273. }
  274. return nil
  275. }
  276. func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
  277. // Cache mapping from this layer's DiffID to the blobsum
  278. ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
  279. }
  280. func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
  281. manSvc, err := p.repo.Manifests(ctx)
  282. if err != nil {
  283. return false, err
  284. }
  285. var (
  286. manifest distribution.Manifest
  287. tagOrDigest string // Used for logging/progress only
  288. )
  289. if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
  290. manifest, err = manSvc.Get(ctx, "", distribution.WithTag(tagged.Tag()))
  291. if err != nil {
  292. return false, allowV1Fallback(err)
  293. }
  294. tagOrDigest = tagged.Tag()
  295. } else if digested, isDigested := ref.(reference.Canonical); isDigested {
  296. manifest, err = manSvc.Get(ctx, digested.Digest())
  297. if err != nil {
  298. return false, err
  299. }
  300. tagOrDigest = digested.Digest().String()
  301. } else {
  302. return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
  303. }
  304. if manifest == nil {
  305. return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
  306. }
  307. if m, ok := manifest.(*schema2.DeserializedManifest); ok {
  308. var allowedMediatype bool
  309. for _, t := range p.config.Schema2Types {
  310. if m.Manifest.Config.MediaType == t {
  311. allowedMediatype = true
  312. break
  313. }
  314. }
  315. if !allowedMediatype {
  316. configClass := mediaTypeClasses[m.Manifest.Config.MediaType]
  317. if configClass == "" {
  318. configClass = "unknown"
  319. }
  320. return false, fmt.Errorf("target is %s", configClass)
  321. }
  322. }
  323. // If manSvc.Get succeeded, we can be confident that the registry on
  324. // the other side speaks the v2 protocol.
  325. p.confirmedV2 = true
  326. logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
  327. progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
  328. var (
  329. id digest.Digest
  330. manifestDigest digest.Digest
  331. )
  332. switch v := manifest.(type) {
  333. case *schema1.SignedManifest:
  334. if p.config.RequireSchema2 {
  335. return false, fmt.Errorf("invalid manifest: not schema2")
  336. }
  337. id, manifestDigest, err = p.pullSchema1(ctx, ref, v)
  338. if err != nil {
  339. return false, err
  340. }
  341. case *schema2.DeserializedManifest:
  342. id, manifestDigest, err = p.pullSchema2(ctx, ref, v)
  343. if err != nil {
  344. return false, err
  345. }
  346. case *manifestlist.DeserializedManifestList:
  347. id, manifestDigest, err = p.pullManifestList(ctx, ref, v)
  348. if err != nil {
  349. return false, err
  350. }
  351. default:
  352. return false, errors.New("unsupported manifest format")
  353. }
  354. progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
  355. if p.config.ReferenceStore != nil {
  356. oldTagID, err := p.config.ReferenceStore.Get(ref)
  357. if err == nil {
  358. if oldTagID == id {
  359. return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
  360. }
  361. } else if err != refstore.ErrDoesNotExist {
  362. return false, err
  363. }
  364. if canonical, ok := ref.(reference.Canonical); ok {
  365. if err = p.config.ReferenceStore.AddDigest(canonical, id, true); err != nil {
  366. return false, err
  367. }
  368. } else {
  369. if err = addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil {
  370. return false, err
  371. }
  372. if err = p.config.ReferenceStore.AddTag(ref, id, true); err != nil {
  373. return false, err
  374. }
  375. }
  376. }
  377. return true, nil
  378. }
  379. func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
  380. var verifiedManifest *schema1.Manifest
  381. verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
  382. if err != nil {
  383. return "", "", err
  384. }
  385. rootFS := image.NewRootFS()
  386. // remove duplicate layers and check parent chain validity
  387. err = fixManifestLayers(verifiedManifest)
  388. if err != nil {
  389. return "", "", err
  390. }
  391. var descriptors []xfer.DownloadDescriptor
  392. // Image history converted to the new format
  393. var history []image.History
  394. // Note that the order of this loop is in the direction of bottom-most
  395. // to top-most, so that the downloads slice gets ordered correctly.
  396. for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
  397. blobSum := verifiedManifest.FSLayers[i].BlobSum
  398. var throwAway struct {
  399. ThrowAway bool `json:"throwaway,omitempty"`
  400. }
  401. if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
  402. return "", "", err
  403. }
  404. h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
  405. if err != nil {
  406. return "", "", err
  407. }
  408. history = append(history, h)
  409. if throwAway.ThrowAway {
  410. continue
  411. }
  412. layerDescriptor := &v2LayerDescriptor{
  413. digest: blobSum,
  414. repoInfo: p.repoInfo,
  415. repo: p.repo,
  416. V2MetadataService: p.V2MetadataService,
  417. }
  418. descriptors = append(descriptors, layerDescriptor)
  419. }
  420. resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
  421. if err != nil {
  422. return "", "", err
  423. }
  424. defer release()
  425. config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
  426. if err != nil {
  427. return "", "", err
  428. }
  429. imageID, err := p.config.ImageStore.Put(config)
  430. if err != nil {
  431. return "", "", err
  432. }
  433. manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
  434. return imageID, manifestDigest, nil
  435. }
  436. func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
  437. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  438. if err != nil {
  439. return "", "", err
  440. }
  441. target := mfst.Target()
  442. if _, err := p.config.ImageStore.Get(target.Digest); err == nil {
  443. // If the image already exists locally, no need to pull
  444. // anything.
  445. return target.Digest, manifestDigest, nil
  446. }
  447. var descriptors []xfer.DownloadDescriptor
  448. // Note that the order of this loop is in the direction of bottom-most
  449. // to top-most, so that the downloads slice gets ordered correctly.
  450. for _, d := range mfst.Layers {
  451. layerDescriptor := &v2LayerDescriptor{
  452. digest: d.Digest,
  453. repo: p.repo,
  454. repoInfo: p.repoInfo,
  455. V2MetadataService: p.V2MetadataService,
  456. src: d,
  457. }
  458. descriptors = append(descriptors, layerDescriptor)
  459. }
  460. configChan := make(chan []byte, 1)
  461. errChan := make(chan error, 1)
  462. var cancel func()
  463. ctx, cancel = context.WithCancel(ctx)
  464. // Pull the image config
  465. go func() {
  466. configJSON, err := p.pullSchema2Config(ctx, target.Digest)
  467. if err != nil {
  468. errChan <- ImageConfigPullError{Err: err}
  469. cancel()
  470. return
  471. }
  472. configChan <- configJSON
  473. }()
  474. var (
  475. configJSON []byte // raw serialized image config
  476. downloadedRootFS *image.RootFS // rootFS from registered layers
  477. configRootFS *image.RootFS // rootFS from configuration
  478. )
  479. // https://github.com/docker/docker/issues/24766 - Err on the side of caution,
  480. // explicitly blocking images intended for linux from the Windows daemon. On
  481. // Windows, we do this before the attempt to download, effectively serialising
  482. // the download slightly slowing it down. We have to do it this way, as
  483. // chances are the download of layers itself would fail due to file names
  484. // which aren't suitable for NTFS. At some point in the future, if a similar
  485. // check to block Windows images being pulled on Linux is implemented, it
  486. // may be necessary to perform the same type of serialisation.
  487. if runtime.GOOS == "windows" {
  488. configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
  489. if err != nil {
  490. return "", "", err
  491. }
  492. if configRootFS == nil {
  493. return "", "", errRootFSInvalid
  494. }
  495. }
  496. if p.config.DownloadManager != nil {
  497. downloadRootFS := *image.NewRootFS()
  498. rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
  499. if err != nil {
  500. if configJSON != nil {
  501. // Already received the config
  502. return "", "", err
  503. }
  504. select {
  505. case err = <-errChan:
  506. return "", "", err
  507. default:
  508. cancel()
  509. select {
  510. case <-configChan:
  511. case <-errChan:
  512. }
  513. return "", "", err
  514. }
  515. }
  516. if release != nil {
  517. defer release()
  518. }
  519. downloadedRootFS = &rootFS
  520. }
  521. if configJSON == nil {
  522. configJSON, configRootFS, err = receiveConfig(p.config.ImageStore, configChan, errChan)
  523. if err != nil {
  524. return "", "", err
  525. }
  526. if configRootFS == nil {
  527. return "", "", errRootFSInvalid
  528. }
  529. }
  530. if downloadedRootFS != nil {
  531. // The DiffIDs returned in rootFS MUST match those in the config.
  532. // Otherwise the image config could be referencing layers that aren't
  533. // included in the manifest.
  534. if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) {
  535. return "", "", errRootFSMismatch
  536. }
  537. for i := range downloadedRootFS.DiffIDs {
  538. if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] {
  539. return "", "", errRootFSMismatch
  540. }
  541. }
  542. }
  543. imageID, err := p.config.ImageStore.Put(configJSON)
  544. if err != nil {
  545. return "", "", err
  546. }
  547. return imageID, manifestDigest, nil
  548. }
  549. func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, error) {
  550. select {
  551. case configJSON := <-configChan:
  552. rootfs, err := s.RootFSFromConfig(configJSON)
  553. if err != nil {
  554. return nil, nil, err
  555. }
  556. return configJSON, rootfs, nil
  557. case err := <-errChan:
  558. return nil, nil, err
  559. // Don't need a case for ctx.Done in the select because cancellation
  560. // will trigger an error in p.pullSchema2ImageConfig.
  561. }
  562. }
  563. // pullManifestList handles "manifest lists" which point to various
  564. // platform-specific manifests.
  565. func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList) (id digest.Digest, manifestListDigest digest.Digest, err error) {
  566. manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
  567. if err != nil {
  568. return "", "", err
  569. }
  570. var manifestDigest digest.Digest
  571. for _, manifestDescriptor := range mfstList.Manifests {
  572. // TODO(aaronl): The manifest list spec supports optional
  573. // "features" and "variant" fields. These are not yet used.
  574. // Once they are, their values should be interpreted here.
  575. if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
  576. manifestDigest = manifestDescriptor.Digest
  577. break
  578. }
  579. }
  580. if manifestDigest == "" {
  581. return "", "", errors.New("no supported platform found in manifest list")
  582. }
  583. manSvc, err := p.repo.Manifests(ctx)
  584. if err != nil {
  585. return "", "", err
  586. }
  587. manifest, err := manSvc.Get(ctx, manifestDigest)
  588. if err != nil {
  589. return "", "", err
  590. }
  591. manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), manifestDigest)
  592. if err != nil {
  593. return "", "", err
  594. }
  595. switch v := manifest.(type) {
  596. case *schema1.SignedManifest:
  597. id, _, err = p.pullSchema1(ctx, manifestRef, v)
  598. if err != nil {
  599. return "", "", err
  600. }
  601. case *schema2.DeserializedManifest:
  602. id, _, err = p.pullSchema2(ctx, manifestRef, v)
  603. if err != nil {
  604. return "", "", err
  605. }
  606. default:
  607. return "", "", errors.New("unsupported manifest format")
  608. }
  609. return id, manifestListDigest, err
  610. }
  611. func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
  612. blobs := p.repo.Blobs(ctx)
  613. configJSON, err = blobs.Get(ctx, dgst)
  614. if err != nil {
  615. return nil, err
  616. }
  617. // Verify image config digest
  618. verifier := dgst.Verifier()
  619. if _, err := verifier.Write(configJSON); err != nil {
  620. return nil, err
  621. }
  622. if !verifier.Verified() {
  623. err := fmt.Errorf("image config verification failed for digest %s", dgst)
  624. logrus.Error(err)
  625. return nil, err
  626. }
  627. return configJSON, nil
  628. }
  629. // schema2ManifestDigest computes the manifest digest, and, if pulling by
  630. // digest, ensures that it matches the requested digest.
  631. func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
  632. _, canonical, err := mfst.Payload()
  633. if err != nil {
  634. return "", err
  635. }
  636. // If pull by digest, then verify the manifest digest.
  637. if digested, isDigested := ref.(reference.Canonical); isDigested {
  638. verifier := digested.Digest().Verifier()
  639. if _, err := verifier.Write(canonical); err != nil {
  640. return "", err
  641. }
  642. if !verifier.Verified() {
  643. err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
  644. logrus.Error(err)
  645. return "", err
  646. }
  647. return digested.Digest(), nil
  648. }
  649. return digest.FromBytes(canonical), nil
  650. }
  651. // allowV1Fallback checks if the error is a possible reason to fallback to v1
  652. // (even if confirmedV2 has been set already), and if so, wraps the error in
  653. // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
  654. // error unmodified.
  655. func allowV1Fallback(err error) error {
  656. switch v := err.(type) {
  657. case errcode.Errors:
  658. if len(v) != 0 {
  659. if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) {
  660. return fallbackError{
  661. err: err,
  662. confirmedV2: false,
  663. transportOK: true,
  664. }
  665. }
  666. }
  667. case errcode.Error:
  668. if shouldV2Fallback(v) {
  669. return fallbackError{
  670. err: err,
  671. confirmedV2: false,
  672. transportOK: true,
  673. }
  674. }
  675. case *url.Error:
  676. if v.Err == auth.ErrNoBasicAuthCredentials {
  677. return fallbackError{err: err, confirmedV2: false}
  678. }
  679. }
  680. return err
  681. }
  682. func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Named) (m *schema1.Manifest, err error) {
  683. // If pull by digest, then verify the manifest digest. NOTE: It is
  684. // important to do this first, before any other content validation. If the
  685. // digest cannot be verified, don't even bother with those other things.
  686. if digested, isCanonical := ref.(reference.Canonical); isCanonical {
  687. verifier := digested.Digest().Verifier()
  688. if _, err := verifier.Write(signedManifest.Canonical); err != nil {
  689. return nil, err
  690. }
  691. if !verifier.Verified() {
  692. err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
  693. logrus.Error(err)
  694. return nil, err
  695. }
  696. }
  697. m = &signedManifest.Manifest
  698. if m.SchemaVersion != 1 {
  699. return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
  700. }
  701. if len(m.FSLayers) != len(m.History) {
  702. return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
  703. }
  704. if len(m.FSLayers) == 0 {
  705. return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
  706. }
  707. return m, nil
  708. }
  709. // fixManifestLayers removes repeated layers from the manifest and checks the
  710. // correctness of the parent chain.
  711. func fixManifestLayers(m *schema1.Manifest) error {
  712. imgs := make([]*image.V1Image, len(m.FSLayers))
  713. for i := range m.FSLayers {
  714. img := &image.V1Image{}
  715. if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
  716. return err
  717. }
  718. imgs[i] = img
  719. if err := v1.ValidateID(img.ID); err != nil {
  720. return err
  721. }
  722. }
  723. if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
  724. // Windows base layer can point to a base layer parent that is not in manifest.
  725. return errors.New("invalid parent ID in the base layer of the image")
  726. }
  727. // check general duplicates to error instead of a deadlock
  728. idmap := make(map[string]struct{})
  729. var lastID string
  730. for _, img := range imgs {
  731. // skip IDs that appear after each other, we handle those later
  732. if _, exists := idmap[img.ID]; img.ID != lastID && exists {
  733. return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
  734. }
  735. lastID = img.ID
  736. idmap[lastID] = struct{}{}
  737. }
  738. // backwards loop so that we keep the remaining indexes after removing items
  739. for i := len(imgs) - 2; i >= 0; i-- {
  740. if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
  741. m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
  742. m.History = append(m.History[:i], m.History[i+1:]...)
  743. } else if imgs[i].Parent != imgs[i+1].ID {
  744. return fmt.Errorf("Invalid parent ID. Expected %v, got %v.", imgs[i+1].ID, imgs[i].Parent)
  745. }
  746. }
  747. return nil
  748. }
  749. func createDownloadFile() (*os.File, error) {
  750. return ioutil.TempFile("", "GetImageBlob")
  751. }