pull_v2.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861
  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 (
  33. errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
  34. errMediaTypePlugin = errors.New("target is a plugin")
  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(ref.String(), 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, err = digest.NewDigestVerifier(ld.digest)
  206. if err != nil {
  207. return nil, 0, xfer.DoNotRetry{Err: err}
  208. }
  209. }
  210. _, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
  211. if err != nil {
  212. if err == transport.ErrWrongCodeForByteRange {
  213. if err := ld.truncateDownloadFile(); err != nil {
  214. return nil, 0, xfer.DoNotRetry{Err: err}
  215. }
  216. return nil, 0, err
  217. }
  218. return nil, 0, retryOnError(err)
  219. }
  220. progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
  221. if !ld.verifier.Verified() {
  222. err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
  223. logrus.Error(err)
  224. // Allow a retry if this digest verification error happened
  225. // after a resumed download.
  226. if offset != 0 {
  227. if err := ld.truncateDownloadFile(); err != nil {
  228. return nil, 0, xfer.DoNotRetry{Err: err}
  229. }
  230. return nil, 0, err
  231. }
  232. return nil, 0, xfer.DoNotRetry{Err: err}
  233. }
  234. progress.Update(progressOutput, ld.ID(), "Download complete")
  235. logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
  236. _, err = tmpFile.Seek(0, os.SEEK_SET)
  237. if err != nil {
  238. tmpFile.Close()
  239. if err := os.Remove(tmpFile.Name()); err != nil {
  240. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  241. }
  242. ld.tmpFile = nil
  243. ld.verifier = nil
  244. return nil, 0, xfer.DoNotRetry{Err: err}
  245. }
  246. // hand off the temporary file to the download manager, so it will only
  247. // be closed once
  248. ld.tmpFile = nil
  249. return ioutils.NewReadCloserWrapper(tmpFile, func() error {
  250. tmpFile.Close()
  251. err := os.RemoveAll(tmpFile.Name())
  252. if err != nil {
  253. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  254. }
  255. return err
  256. }), size, nil
  257. }
  258. func (ld *v2LayerDescriptor) Close() {
  259. if ld.tmpFile != nil {
  260. ld.tmpFile.Close()
  261. if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
  262. logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  263. }
  264. }
  265. }
  266. func (ld *v2LayerDescriptor) truncateDownloadFile() error {
  267. // Need a new hash context since we will be redoing the download
  268. ld.verifier = nil
  269. if _, err := ld.tmpFile.Seek(0, os.SEEK_SET); err != nil {
  270. logrus.Errorf("error seeking to beginning of download file: %v", err)
  271. return err
  272. }
  273. if err := ld.tmpFile.Truncate(0); err != nil {
  274. logrus.Errorf("error truncating download file: %v", err)
  275. return err
  276. }
  277. return nil
  278. }
  279. func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
  280. // Cache mapping from this layer's DiffID to the blobsum
  281. ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.FullName()})
  282. }
  283. func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) {
  284. manSvc, err := p.repo.Manifests(ctx)
  285. if err != nil {
  286. return false, err
  287. }
  288. var (
  289. manifest distribution.Manifest
  290. tagOrDigest string // Used for logging/progress only
  291. )
  292. if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
  293. manifest, err = manSvc.Get(ctx, "", distribution.WithTag(tagged.Tag()))
  294. if err != nil {
  295. return false, allowV1Fallback(err)
  296. }
  297. tagOrDigest = tagged.Tag()
  298. } else if digested, isDigested := ref.(reference.Canonical); isDigested {
  299. manifest, err = manSvc.Get(ctx, digested.Digest())
  300. if err != nil {
  301. return false, err
  302. }
  303. tagOrDigest = digested.Digest().String()
  304. } else {
  305. return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", ref.String())
  306. }
  307. if manifest == nil {
  308. return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
  309. }
  310. if m, ok := manifest.(*schema2.DeserializedManifest); ok {
  311. if m.Manifest.Config.MediaType == schema2.MediaTypePluginConfig ||
  312. m.Manifest.Config.MediaType == "application/vnd.docker.plugin.image.v0+json" { //TODO: remove this v0 before 1.13 GA
  313. return false, errMediaTypePlugin
  314. }
  315. }
  316. // If manSvc.Get succeeded, we can be confident that the registry on
  317. // the other side speaks the v2 protocol.
  318. p.confirmedV2 = true
  319. logrus.Debugf("Pulling ref from V2 registry: %s", ref.String())
  320. progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+p.repo.Named().Name())
  321. var (
  322. id digest.Digest
  323. manifestDigest digest.Digest
  324. )
  325. switch v := manifest.(type) {
  326. case *schema1.SignedManifest:
  327. id, manifestDigest, err = p.pullSchema1(ctx, ref, v)
  328. if err != nil {
  329. return false, err
  330. }
  331. case *schema2.DeserializedManifest:
  332. id, manifestDigest, err = p.pullSchema2(ctx, ref, v)
  333. if err != nil {
  334. return false, err
  335. }
  336. case *manifestlist.DeserializedManifestList:
  337. id, manifestDigest, err = p.pullManifestList(ctx, ref, v)
  338. if err != nil {
  339. return false, err
  340. }
  341. default:
  342. return false, errors.New("unsupported manifest format")
  343. }
  344. progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
  345. oldTagID, err := p.config.ReferenceStore.Get(ref)
  346. if err == nil {
  347. if oldTagID == id {
  348. return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
  349. }
  350. } else if err != reference.ErrDoesNotExist {
  351. return false, err
  352. }
  353. if canonical, ok := ref.(reference.Canonical); ok {
  354. if err = p.config.ReferenceStore.AddDigest(canonical, id, true); err != nil {
  355. return false, err
  356. }
  357. } else {
  358. if err = addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil {
  359. return false, err
  360. }
  361. if err = p.config.ReferenceStore.AddTag(ref, id, true); err != nil {
  362. return false, err
  363. }
  364. }
  365. return true, nil
  366. }
  367. func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Named, unverifiedManifest *schema1.SignedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
  368. var verifiedManifest *schema1.Manifest
  369. verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
  370. if err != nil {
  371. return "", "", err
  372. }
  373. rootFS := image.NewRootFS()
  374. // remove duplicate layers and check parent chain validity
  375. err = fixManifestLayers(verifiedManifest)
  376. if err != nil {
  377. return "", "", err
  378. }
  379. var descriptors []xfer.DownloadDescriptor
  380. // Image history converted to the new format
  381. var history []image.History
  382. // Note that the order of this loop is in the direction of bottom-most
  383. // to top-most, so that the downloads slice gets ordered correctly.
  384. for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
  385. blobSum := verifiedManifest.FSLayers[i].BlobSum
  386. var throwAway struct {
  387. ThrowAway bool `json:"throwaway,omitempty"`
  388. }
  389. if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
  390. return "", "", err
  391. }
  392. h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
  393. if err != nil {
  394. return "", "", err
  395. }
  396. history = append(history, h)
  397. if throwAway.ThrowAway {
  398. continue
  399. }
  400. layerDescriptor := &v2LayerDescriptor{
  401. digest: blobSum,
  402. repoInfo: p.repoInfo,
  403. repo: p.repo,
  404. V2MetadataService: p.V2MetadataService,
  405. }
  406. descriptors = append(descriptors, layerDescriptor)
  407. }
  408. resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, descriptors, p.config.ProgressOutput)
  409. if err != nil {
  410. return "", "", err
  411. }
  412. defer release()
  413. config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
  414. if err != nil {
  415. return "", "", err
  416. }
  417. imageID, err := p.config.ImageStore.Create(config)
  418. if err != nil {
  419. return "", "", err
  420. }
  421. manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
  422. return imageID.Digest(), manifestDigest, nil
  423. }
  424. func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) {
  425. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  426. if err != nil {
  427. return "", "", err
  428. }
  429. target := mfst.Target()
  430. if _, err := p.config.ImageStore.Get(image.IDFromDigest(target.Digest)); err == nil {
  431. // If the image already exists locally, no need to pull
  432. // anything.
  433. return target.Digest, 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.pullSchema2Config(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. // https://github.com/docker/docker/issues/24766 - Err on the side of caution,
  468. // explicitly blocking images intended for linux from the Windows daemon. On
  469. // Windows, we do this before the attempt to download, effectively serialising
  470. // the download slightly slowing it down. We have to do it this way, as
  471. // chances are the download of layers itself would fail due to file names
  472. // which aren't suitable for NTFS. At some point in the future, if a similar
  473. // check to block Windows images being pulled on Linux is implemented, it
  474. // may be necessary to perform the same type of serialisation.
  475. if runtime.GOOS == "windows" {
  476. configJSON, unmarshalledConfig, err = receiveConfig(configChan, errChan)
  477. if err != nil {
  478. return "", "", err
  479. }
  480. if unmarshalledConfig.RootFS == nil {
  481. return "", "", errRootFSInvalid
  482. }
  483. if unmarshalledConfig.OS == "linux" {
  484. return "", "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
  485. }
  486. }
  487. downloadRootFS = *image.NewRootFS()
  488. rootFS, release, err := p.config.DownloadManager.Download(ctx, downloadRootFS, descriptors, p.config.ProgressOutput)
  489. if err != nil {
  490. if configJSON != nil {
  491. // Already received the config
  492. return "", "", err
  493. }
  494. select {
  495. case err = <-errChan:
  496. return "", "", err
  497. default:
  498. cancel()
  499. select {
  500. case <-configChan:
  501. case <-errChan:
  502. }
  503. return "", "", err
  504. }
  505. }
  506. defer release()
  507. if configJSON == nil {
  508. configJSON, unmarshalledConfig, err = receiveConfig(configChan, errChan)
  509. if err != nil {
  510. return "", "", err
  511. }
  512. if unmarshalledConfig.RootFS == nil {
  513. return "", "", errRootFSInvalid
  514. }
  515. }
  516. // The DiffIDs returned in rootFS MUST match those in the config.
  517. // Otherwise the image config could be referencing layers that aren't
  518. // included in the manifest.
  519. if len(rootFS.DiffIDs) != len(unmarshalledConfig.RootFS.DiffIDs) {
  520. return "", "", errRootFSMismatch
  521. }
  522. for i := range rootFS.DiffIDs {
  523. if rootFS.DiffIDs[i] != unmarshalledConfig.RootFS.DiffIDs[i] {
  524. return "", "", errRootFSMismatch
  525. }
  526. }
  527. imageID, err := p.config.ImageStore.Create(configJSON)
  528. if err != nil {
  529. return "", "", err
  530. }
  531. return imageID.Digest(), manifestDigest, nil
  532. }
  533. func receiveConfig(configChan <-chan []byte, errChan <-chan error) ([]byte, image.Image, error) {
  534. select {
  535. case configJSON := <-configChan:
  536. var unmarshalledConfig image.Image
  537. if err := json.Unmarshal(configJSON, &unmarshalledConfig); err != nil {
  538. return nil, image.Image{}, err
  539. }
  540. return configJSON, unmarshalledConfig, nil
  541. case err := <-errChan:
  542. return nil, image.Image{}, err
  543. // Don't need a case for ctx.Done in the select because cancellation
  544. // will trigger an error in p.pullSchema2ImageConfig.
  545. }
  546. }
  547. // pullManifestList handles "manifest lists" which point to various
  548. // platform-specifc manifests.
  549. func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList) (id digest.Digest, manifestListDigest digest.Digest, err error) {
  550. manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
  551. if err != nil {
  552. return "", "", err
  553. }
  554. var manifestDigest digest.Digest
  555. for _, manifestDescriptor := range mfstList.Manifests {
  556. // TODO(aaronl): The manifest list spec supports optional
  557. // "features" and "variant" fields. These are not yet used.
  558. // Once they are, their values should be interpreted here.
  559. if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
  560. manifestDigest = manifestDescriptor.Digest
  561. break
  562. }
  563. }
  564. if manifestDigest == "" {
  565. return "", "", errors.New("no supported platform found in manifest list")
  566. }
  567. manSvc, err := p.repo.Manifests(ctx)
  568. if err != nil {
  569. return "", "", err
  570. }
  571. manifest, err := manSvc.Get(ctx, manifestDigest)
  572. if err != nil {
  573. return "", "", err
  574. }
  575. manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), manifestDigest)
  576. if err != nil {
  577. return "", "", err
  578. }
  579. switch v := manifest.(type) {
  580. case *schema1.SignedManifest:
  581. id, _, err = p.pullSchema1(ctx, manifestRef, v)
  582. if err != nil {
  583. return "", "", err
  584. }
  585. case *schema2.DeserializedManifest:
  586. id, _, err = p.pullSchema2(ctx, manifestRef, v)
  587. if err != nil {
  588. return "", "", err
  589. }
  590. default:
  591. return "", "", errors.New("unsupported manifest format")
  592. }
  593. return id, manifestListDigest, err
  594. }
  595. func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
  596. blobs := p.repo.Blobs(ctx)
  597. configJSON, err = blobs.Get(ctx, dgst)
  598. if err != nil {
  599. return nil, err
  600. }
  601. // Verify image config digest
  602. verifier, err := digest.NewDigestVerifier(dgst)
  603. if err != nil {
  604. return nil, err
  605. }
  606. if _, err := verifier.Write(configJSON); err != nil {
  607. return nil, err
  608. }
  609. if !verifier.Verified() {
  610. err := fmt.Errorf("image config verification failed for digest %s", dgst)
  611. logrus.Error(err)
  612. return nil, err
  613. }
  614. return configJSON, nil
  615. }
  616. // schema2ManifestDigest computes the manifest digest, and, if pulling by
  617. // digest, ensures that it matches the requested digest.
  618. func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
  619. _, canonical, err := mfst.Payload()
  620. if err != nil {
  621. return "", err
  622. }
  623. // If pull by digest, then verify the manifest digest.
  624. if digested, isDigested := ref.(reference.Canonical); isDigested {
  625. verifier, err := digest.NewDigestVerifier(digested.Digest())
  626. if err != nil {
  627. return "", err
  628. }
  629. if _, err := verifier.Write(canonical); err != nil {
  630. return "", err
  631. }
  632. if !verifier.Verified() {
  633. err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
  634. logrus.Error(err)
  635. return "", err
  636. }
  637. return digested.Digest(), nil
  638. }
  639. return digest.FromBytes(canonical), nil
  640. }
  641. // allowV1Fallback checks if the error is a possible reason to fallback to v1
  642. // (even if confirmedV2 has been set already), and if so, wraps the error in
  643. // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
  644. // error unmodified.
  645. func allowV1Fallback(err error) error {
  646. switch v := err.(type) {
  647. case errcode.Errors:
  648. if len(v) != 0 {
  649. if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) {
  650. return fallbackError{
  651. err: err,
  652. confirmedV2: false,
  653. transportOK: true,
  654. }
  655. }
  656. }
  657. case errcode.Error:
  658. if shouldV2Fallback(v) {
  659. return fallbackError{
  660. err: err,
  661. confirmedV2: false,
  662. transportOK: true,
  663. }
  664. }
  665. case *url.Error:
  666. if v.Err == auth.ErrNoBasicAuthCredentials {
  667. return fallbackError{err: err, confirmedV2: false}
  668. }
  669. }
  670. return err
  671. }
  672. func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Named) (m *schema1.Manifest, err error) {
  673. // If pull by digest, then verify the manifest digest. NOTE: It is
  674. // important to do this first, before any other content validation. If the
  675. // digest cannot be verified, don't even bother with those other things.
  676. if digested, isCanonical := ref.(reference.Canonical); isCanonical {
  677. verifier, err := digest.NewDigestVerifier(digested.Digest())
  678. if err != nil {
  679. return nil, err
  680. }
  681. if _, err := verifier.Write(signedManifest.Canonical); err != nil {
  682. return nil, err
  683. }
  684. if !verifier.Verified() {
  685. err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
  686. logrus.Error(err)
  687. return nil, err
  688. }
  689. }
  690. m = &signedManifest.Manifest
  691. if m.SchemaVersion != 1 {
  692. return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, ref.String())
  693. }
  694. if len(m.FSLayers) != len(m.History) {
  695. return nil, fmt.Errorf("length of history not equal to number of layers for %q", ref.String())
  696. }
  697. if len(m.FSLayers) == 0 {
  698. return nil, fmt.Errorf("no FSLayers in manifest for %q", ref.String())
  699. }
  700. return m, nil
  701. }
  702. // fixManifestLayers removes repeated layers from the manifest and checks the
  703. // correctness of the parent chain.
  704. func fixManifestLayers(m *schema1.Manifest) error {
  705. imgs := make([]*image.V1Image, len(m.FSLayers))
  706. for i := range m.FSLayers {
  707. img := &image.V1Image{}
  708. if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
  709. return err
  710. }
  711. imgs[i] = img
  712. if err := v1.ValidateID(img.ID); err != nil {
  713. return err
  714. }
  715. }
  716. if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
  717. // Windows base layer can point to a base layer parent that is not in manifest.
  718. return errors.New("invalid parent ID in the base layer of the image")
  719. }
  720. // check general duplicates to error instead of a deadlock
  721. idmap := make(map[string]struct{})
  722. var lastID string
  723. for _, img := range imgs {
  724. // skip IDs that appear after each other, we handle those later
  725. if _, exists := idmap[img.ID]; img.ID != lastID && exists {
  726. return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
  727. }
  728. lastID = img.ID
  729. idmap[lastID] = struct{}{}
  730. }
  731. // backwards loop so that we keep the remaining indexes after removing items
  732. for i := len(imgs) - 2; i >= 0; i-- {
  733. if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
  734. m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
  735. m.History = append(m.History[:i], m.History[i+1:]...)
  736. } else if imgs[i].Parent != imgs[i+1].ID {
  737. return fmt.Errorf("Invalid parent ID. Expected %v, got %v.", imgs[i+1].ID, imgs[i].Parent)
  738. }
  739. }
  740. return nil
  741. }
  742. func createDownloadFile() (*os.File, error) {
  743. return ioutil.TempFile("", "GetImageBlob")
  744. }