Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix image size calculation in importer #9115

Merged
merged 3 commits into from
Jul 13, 2016

Conversation

legionus
Copy link
Contributor

@legionus legionus commented Jun 1, 2016

After upgrading docker/distibution V1Compatibility no longer contains Size. For
this reason, the image size calculation is wrong. A new way to calculate the
size repeats the way that dockerregistry calculates the size.

Fix #7706

@smarterclayton @pweil- @mfojtik @miminar @Kargakis

@@ -397,6 +418,7 @@ func importRepositoryFromDocker(ctx gocontext.Context, retriever RepositoryRetri
importDigest.Err = err
continue
}
importDigest.Err = calculateImageSize(ctx, repo, importDigest.Image)
Copy link
Contributor

@soltysh soltysh Jun 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather we calculcate the size only when it's not there. Not all registries we use are hub.docker.com, for that cases (where v1 schema is supported) it's unnecessarily calculates the size.

This could be done as a followup to this issue, we need to unblock the queue, atm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the pattern we are using above (if err := ...; err != nil { importDigest.Err = err; continue }) for readability.

This definitely will be broken on v1, which does not have layer sizes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a more natural fit inside of schema1ToImage to build a complete Image object. The additional fanout is less than beautiful, but it would keep the API looking more consistent. schema1ToImage would return a complete and valid look.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make sure the integration test is properly testing v1 sizes against registry.access.redhat.com?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema v1 supported by hub.docker.com. Before that we used is not documented structure to calculate the size.

https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md#manifest-field-descriptions

v1Compatibility string

V1Compatibility is the raw V1 compatibility information. This will contain the JSON
object describing the V1 of this image.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not using the descriptor on the layers reference to calculate
the size? We already have that data in schema on layers - in
schema2toManifest (which this PR should create) we would be able to
calculate the fake size.

On Wed, Jun 1, 2016 at 1:53 PM, Clayton Coleman ccoleman@redhat.com wrote:

It doesn't matter that it's not documented - it's what is deployed in
production in many places still.

On Wed, Jun 1, 2016 at 1:52 PM, Alexey Gladkov notifications@github.com
wrote:

In pkg/image/importer/importer.go
#9115 (comment):

@@ -397,6 +418,7 @@ func importRepositoryFromDocker(ctx gocontext.Context, retriever RepositoryRetri
importDigest.Err = err
continue
}

  •  importDigest.Err = calculateImageSize(ctx, repo, importDigest.Image)
    

Schema v1 supported by hub.docker.com. Before that we used is not
documented structure to calculate the size.

https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md#manifest-field-descriptions

v1Compatibility string

V1Compatibility is the raw V1 compatibility information. This will contain the JSON
object describing the V1 of this image.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/openshift/origin/pull/9115/files/492baa03f23cf45ca229c3501c29ca4bccf546f3#r65409920,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p0YnP3kHQqbqW47WCSl_-TNMETGtks5qHcbcgaJpZM4IrzC2
.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the need to call stat

On Wed, Jun 1, 2016 at 1:56 PM, Clayton Coleman ccoleman@redhat.com wrote:

Why are we not using the descriptor on the layers reference to calculate
the size? We already have that data in schema on layers - in
schema2toManifest (which this PR should create) we would be able to
calculate the fake size.

On Wed, Jun 1, 2016 at 1:53 PM, Clayton Coleman ccoleman@redhat.com
wrote:

It doesn't matter that it's not documented - it's what is deployed in
production in many places still.

On Wed, Jun 1, 2016 at 1:52 PM, Alexey Gladkov notifications@github.com
wrote:

In pkg/image/importer/importer.go
#9115 (comment):

@@ -397,6 +418,7 @@ func importRepositoryFromDocker(ctx gocontext.Context, retriever RepositoryRetri
importDigest.Err = err
continue
}

  • importDigest.Err = calculateImageSize(ctx, repo, importDigest.Image)
    

Schema v1 supported by hub.docker.com. Before that we used is not
documented structure to calculate the size.

https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-1.md#manifest-field-descriptions

v1Compatibility string

V1Compatibility is the raw V1 compatibility information. This will contain the JSON
object describing the V1 of this image.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/openshift/origin/pull/9115/files/492baa03f23cf45ca229c3501c29ca4bccf546f3#r65409920,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p0YnP3kHQqbqW47WCSl_-TNMETGtks5qHcbcgaJpZM4IrzC2
.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's unnecessarily calculates the size.

@soltysh @smarterclayton I added the check to calculate the size only when it is equal to zero. This would eliminate unnecessary requests if we have all the information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still concerned because each of those stats could fail, resulting in
other failures. We probably don't want to fail import if size can't be
calculated. We probably don't want to add a fan-out of 20 requests on
setup.

I'd prefer to not fix this until we have schema2 support. I thought we
were getting a schema2 response without size.

On Wed, Jun 1, 2016 at 2:22 PM, Alexey Gladkov notifications@github.com
wrote:

In pkg/image/importer/importer.go
#9115 (comment):

@@ -397,6 +418,7 @@ func importRepositoryFromDocker(ctx gocontext.Context, retriever RepositoryRetri
importDigest.Err = err
continue
}

  •   importDigest.Err = calculateImageSize(ctx, repo, importDigest.Image)
    

it's unnecessarily calculates the size.

@soltysh https://github.com/soltysh @smarterclayton
https://github.com/smarterclayton I added the check to calculate the
size only when it is equal to zero. This would eliminate unnecessary
requests if we have all the information.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/openshift/origin/pull/9115/files/492baa03f23cf45ca229c3501c29ca4bccf546f3#r65415556,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p3qVdLRkR2IkXDwpJ7kJ7xgChqOKks5qHc3zgaJpZM4IrzC2
.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still concerned because each of those stats could fail, resulting in other failures. We probably don't want to fail import if size can't be calculated. We probably don't want to add a fan-out of 20 requests on setup.

Yes, this is possible. We use NewRetryRepository to make few retries. But fix is necessary for the scheme1.

size := int64(0)
for i := range image.DockerImageLayers {
layer := &image.DockerImageLayers[i]
desc, err := bs.Stat(ctx, digest.Digest(layer.Name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fanout is unfortunate. TODO for a better solution or maybe a cache?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment but I don't think we need the stat - we should already have desc.Size when we get the image metadata in schema1ToImage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema1ToImage is only about image creation. It's not getting digests and do not have anything to calculate the size.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the v2 spec size must be filled out in layers. Is that not
the case?

On Wed, Jun 1, 2016 at 2:09 PM, Alexey Gladkov notifications@github.com
wrote:

In pkg/image/importer/importer.go
#9115 (comment):

@@ -282,6 +282,27 @@ func applyErrorToRepository(repository *importRepository, err error) {
}
}

+func calculateImageSize(ctx gocontext.Context, repo distribution.Repository, image *api.Image) error {

  • bs := repo.Blobs(ctx)
  • layerSet := sets.NewString()
  • size := int64(0)
  • for i := range image.DockerImageLayers {
  •   layer := &image.DockerImageLayers[i]
    
  •   desc, err := bs.Stat(ctx, digest.Digest(layer.Name))
    

schema1ToImage is only about image creation. It's not getting digests and
do not have anything to calculate the size.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/openshift/origin/pull/9115/files/492baa03f23cf45ca229c3501c29ca4bccf546f3#r65413168,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p8C7_D83OxQ7joXFIweSMM4eSYOUks5qHcrygaJpZM4IrzC2
.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's acceptable.

Our tests show that we had a regression after upgrading the hub.docker.com. But OK, you know better.

this is going to be less of a problem soon.

It will be less of a problem only then when there will be no schema1 on the servers.

we need to fix right now is the tests

I know that now is broken: TestImageStreamImport, TestImageStreamImportDockerHub, but there could be more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how you can help upgrading the dockerregisrty. My PR is also affected by this issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is acceptable if in the master branch of Origin we explicitly call out that importing schema1 from the DockerHub (that have been down converted from schema2) will not have size set, because we anticipate this problem resolving itself by a) more images on the hub being schema2, b) we want to support schema2 import on the rebase.

I'll open a PR to disable the tests now and we can lower the priority.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll open a PR to disable the tests now and we can lower the priority.

OK. Then I close this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened test change in #9118

On Wed, Jun 1, 2016 at 3:41 PM, Alexey Gladkov notifications@github.com
wrote:

In pkg/image/importer/importer.go
#9115 (comment):

@@ -282,6 +282,27 @@ func applyErrorToRepository(repository *importRepository, err error) {
}
}

+func calculateImageSize(ctx gocontext.Context, repo distribution.Repository, image *api.Image) error {

  • bs := repo.Blobs(ctx)
  • layerSet := sets.NewString()
  • size := int64(0)
  • for i := range image.DockerImageLayers {
  •   layer := &image.DockerImageLayers[i]
    
  •   desc, err := bs.Stat(ctx, digest.Digest(layer.Name))
    

I'll open a PR to disable the tests now and we can lower the priority.

OK. Then I close this PR.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/openshift/origin/pull/9115/files/492baa03f23cf45ca229c3501c29ca4bccf546f3#r65428383,
or mute the thread
https://github.com/notifications/unsubscribe/ABG_p_Cre1dOt6e_KNPE4GhqBex9VKe5ks5qHeB4gaJpZM4IrzC2
.

@legionus legionus force-pushed the fix-image-metadata-size branch 2 times, most recently from 4ebd85b to 4bcc443 Compare June 1, 2016 18:18
@legionus legionus closed this Jun 1, 2016
@legionus legionus reopened this Jul 5, 2016
@legionus
Copy link
Contributor Author

legionus commented Jul 5, 2016

[test]

@legionus
Copy link
Contributor Author

legionus commented Jul 5, 2016

@smarterclayton @soltysh I added caching for layer size.
What do you think about this solution ?

@@ -474,6 +474,8 @@ func ImageWithMetadata(image *Image) error {
image.DockerImageLayers[i].Name = layer.DockerBlobSum
}
if len(manifest.History) == len(image.DockerImageLayers) {
// This code does not work since hub.docker.com has been updated.
// The V1Compatibility no longer contains Size.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be true for docker hub, but for other repos supporting V1, it's not. I'd drop this comment entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is valid for all v2 -> v1 conversions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rephrase it in following way:
"This code does not work for images imported from v2 schema registries or converted from v2 to v1, since schema v2 does not contain size information."
Or something more appropriate, please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@soltysh Fixed

@soltysh
Copy link
Contributor

soltysh commented Jul 6, 2016

The changes itself looks good, but I won't let this in without tests. We're having too many problems with images to let any code in without at least simple unit test.

@@ -474,6 +474,9 @@ func ImageWithMetadata(image *Image) error {
image.DockerImageLayers[i].Name = layer.DockerBlobSum
}
if len(manifest.History) == len(image.DockerImageLayers) {
// This code does not work for images imported from v2 schema
// registries or converted from v2 to v1, since schema v2 does not
// contain size information.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since schema v2 does not contain size information.

It does, it's just hidden in layer descriptors. This information is lost during conversion to v1 though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So current comment is accurate.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So current comment is accurate.

It's not. Because schema v2 contains size information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the last part is wrong.

@@ -78,17 +82,28 @@ func (i *ImageStreamImporter) contextImageCache(ctx gocontext.Context) map[manif
return cache
}

type imageStreamImporterCache struct {
ImageCache map[manifestKey]*api.Image
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have digestToRepositoryCache, why do we need a second cache?

@@ -50,6 +50,9 @@ type ImageStreamImporter struct {
limiter flowcontrol.RateLimiter

digestToRepositoryCache map[gocontext.Context]map[manifestKey]*api.Image

// digestToLayerSizeCache maps layer digests to size.
digestToLayerSizeCache map[string]int64
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make it thread-safe. And let it be passed to NewImageStreamImporter so it can be shared among multiple importers invoked by image import controller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but I think it should not be part of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to share it, then it needs to be an LRU.

}
}
t.Fatalf("unexpected request %s: %#v", r.URL.Path, r)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a high potential for sharing most of this handler's code with the other tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would make sense if there is another such test.

legionus and others added 2 commits July 8, 2016 18:41
After upgrading docker/distibution V1Compatibility no longer contains Size. For
this reason, the image size calculation is wrong. A new way to calculate the
size repeats the way that dockerregistry calculates the size.
@soltysh
Copy link
Contributor

soltysh commented Jul 11, 2016

Flake from #9766.

@@ -50,6 +50,9 @@ type ImageStreamImporter struct {
limiter flowcontrol.RateLimiter

digestToRepositoryCache map[gocontext.Context]map[manifestKey]*api.Image
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smarterclayton Can you explain, why is it necessary to group the entries by context? Does the same scenario apply to the digestToLayerSizeCache below?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because access to an image may only be allowed given the credentials associated with your context. So you cannot retry it. It does not apply to digest to layer size.

@soltysh
Copy link
Contributor

soltysh commented Jul 11, 2016

LGTM, need @smarterclayton approval and answer @miminar question. Also please link an issue/PR addressing multithreaded access to the cache.

@miminar
Copy link

miminar commented Jul 11, 2016

Created #9779 for a proper caching if this PR neglects it.

Signed-off-by: Gladkov Alexey <agladkov@redhat.com>
@openshift-bot
Copy link
Contributor

Evaluated for origin test up to 26dad6d

@legionus
Copy link
Contributor Author

@smarterclayton It's ready for merge ?

@openshift-bot
Copy link
Contributor

continuous-integration/openshift-jenkins/test SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/6110/)

@miminar
Copy link

miminar commented Jul 12, 2016

LGTM

@soltysh
Copy link
Contributor

soltysh commented Jul 13, 2016

I don't see any objections so merging.

[merge]

@openshift-bot
Copy link
Contributor

Evaluated for origin merge up to 26dad6d

@openshift-bot
Copy link
Contributor

openshift-bot commented Jul 13, 2016

continuous-integration/openshift-jenkins/merge SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pr_origin/6110/) (Image: devenv-rhel7_4580)

@openshift-bot openshift-bot merged commit 0f5afdc into openshift:master Jul 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants