pull_v2.go 27 KB

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