pull_v2.go 24 KB

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