-
Notifications
You must be signed in to change notification settings - Fork 248
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
Streaming qcow2->raw conversion and resource limits on qemu-img subprocess #317
Conversation
should we also use fedors28 in hack/build/docker/builder/Dockerfile? |
pkg/image/qemu.go
Outdated
) | ||
|
||
// ConvertQcow2ToRaw converts a local qcou2 image to raw format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: typo
pkg/image/qemu.go
Outdated
return nil | ||
} | ||
|
||
// ConvertQcow2ToRawStream converts an http accessible qcow2 image to raf format without locally caching the qcow2 image |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: typo: assume "raw"?
pkg/importer/dataStream.go
Outdated
@@ -467,8 +483,18 @@ func closeReaders(readers []Reader) (rtnerr error) { | |||
return rtnerr | |||
} | |||
|
|||
func (d dataStream) isHTTPQcow2() bool { | |||
// assuming len(d.Readers) == 3 is [http, mr, mr] | |||
// should prob get rid of the redundant MultiReader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree. Where is the extra mr coming from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the loop in constructReaders() always adds a MultiReader to the stack, when matchHeader() returns a match. This is a problem in the qcow2 case because unlike the other possible matches, no qcow2 reader is added to the stack.
I think there is a pretty straightforward fix for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, my bad and thanks for finding this. If you want to fix this in a separate pr let me know asap, else I'll fix it. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jeffvance I spent a bit of time looking into this earlier. Turns out a fix is not as simple as I thought. I think a separate PR would be appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just added pr #339. Thanks for mentioning this as it turned out to be a bug in constructReaders
and the unit test too.
pkg/importer/dataStream.go
Outdated
func (d dataStream) isHTTPQcow2() bool { | ||
// assuming len(d.Readers) == 3 is [http, mr, mr] | ||
// should prob get rid of the redundant MultiReader | ||
return (d.Url.Scheme == "http" || d.Url.Scheme == "https") && d.qemu && len(d.Readers) == 3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
== 3
? vs. >=3
(or 2 if mr fix). It looks like we only invoke streaming if the endpoint has not been compressed and/or archived, correct? I thought http(s) supports some form of inline decompression. If this is true, is true, can we use this so that additional endpoint formats based on qemu can be imported via qemu streaming?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, qemu-img can't work directly on compressed/tarred files.
I'm not aware of any http features that can help us here. An http client can request that the server compress data for transmission via the Accept-Encoding
header. But I'm not aware of any protocol support for decompressing a request for a gzipped file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, qemu-img convert
doesn't support reading from stdin so we can't pipe data into it from a qzip reader. If the file is compressed/tarred, I think we have to copy it locally,
pkg/importer/dataStream.go
Outdated
// Copy endpoint to dest based on passed-in reader. | ||
func (d dataStream) copy(dest string) error { | ||
if d.isHTTPQcow2() { | ||
glog.V(Vuser).Infoln("Doing streaming qcow2 to raw conversion") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps logging level here should be Vadmin? Just thinking that the end user doesn't really care if we use qemu streaming, but the admin might due to SLA or perf guarantees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense
866dd05
to
3d765d6
Compare
pkg/image/qemu.go
Outdated
return nil | ||
} | ||
|
||
// ConvertQcow2ToRawStream converts an http accessible qcow2 image to raf format without locally caching the qcow2 image | ||
// ConvertQcow2ToRawStream converts an http accessible qcow2 image to raw format without locally caching the qcow2 image |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you think of having a single ConvertQcow2ToRaw
func with an "optional" json arg? This fits the DRY principle...
pkg/image/qemu.go
Outdated
BackingFile string `json:"backing-filename"` | ||
} | ||
|
||
output, err := execWithLimits("qemu-img", "info", "--output=json", image) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not know how qemu-img
does reads on qcow2 files. But, does this func cause the entire (or a lot of?) the qcow2 file to be read?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure it doesn't read the whole file. Would be surprised if it reads more than the header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, it's just reading the header.
pkg/image/qemu.go
Outdated
|
||
func validate(image, format string) error { | ||
type imageInfo struct { | ||
Format string `json:"format"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do these fields need to be exported? Maybe for unmarshaliing? (but the struct type is not exported)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the scope of that struct is limited to the function I didn't think much about public/private. I guess I can make members private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get this warning if I make private: struct field format has json tag but is not exported
so will keep exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm, thanks for checking.
pkg/image/qemu.go
Outdated
} | ||
|
||
if info.Format != format { | ||
return errors.Wrapf(err, "Invalid format %s for image %s", info.Format, image) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but err
is nil here so no need to wrap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch!
pkg/image/qemu.go
Outdated
} | ||
|
||
if len(info.BackingFile) > 0 { | ||
return errors.Wrapf(err, "Image %s is invalid because it has backing file %s", image, info.BackingFile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto re nil err
20ffd3e
to
a4e7105
Compare
pkg/image/prlimit_test.go
Outdated
|
||
const killedByTestError = "Had to kill process" | ||
|
||
func TestLimits(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if these tests are really necessary and if so should they be functional tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure we've ever found a real image in the wild that can cause problems. Maybe we can run using a fake qemu binary that spinloops or something?
pkg/image/qemu.go
Outdated
return nil | ||
} | ||
|
||
// ConvertQcow2ToRawStream converts an http accessible qcow2 image to raw format without locally caching the qcow2 image | ||
func ConvertQcow2ToRawStream(url *url.URL, dest string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a functional test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say so. Will depend on @copejon 's server container in the environment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aglitke this test actually starts an embedded http server that serves up image files. but yeah may be redundant in functional test environment
2a9a74e
to
422a42a
Compare
test/scripts/memory-consumer.sh
Outdated
|
||
arr=() | ||
for ((i=1; i<=$1; i++)); do | ||
random="$(head -c 4096 /dev/urandom | base64)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why pipe to base64
from the perspective of consuming memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bash didn't like certain characters (definitely nulls and maybe other stuff) so just base64 encoded everything.
ec230db
to
6246025
Compare
0912a2a
to
add4373
Compare
@mhenriks Can you rebase this? Do the existing functional tests cover this feature satisfactorily? |
add4373
to
22051a2
Compare
22051a2
to
d9bbc4e
Compare
@mhenriks Can we remove the WIP label? |
@aglitke I honestly have no idea how to remove the WIP label! Tried to do it awhile ago. Maybe because I still have restricted permissions to this repo? |
@mhenriks Actually the PROW integration will really help with this. |
lgtm |
Two goals here:
Support streaming qcow2 to raw conversion. Don't want to have to create temporary file. Currently only for http or https source images. But maybe we want to avoid the extra copy for non gzipped/tarred as well?
Put resource limits on qemu-img. Basically, it is possible to craft a qcow2 file in such a way that it'll DOS your system. Initial checkin uses prlimit() to set the same cpu/memory limits used in Nova. Subsequent update will include call to
qemu-img info
to get details on source image to detect bad images before attempting conversion.See this thread for more details about streaming conversion and DOS prevention: https://groups.google.com/forum/#!msg/kubevirt-dev/4h7R8fPq0eU/fyL2IaiSBAAJ
qemu-img info
Fixes #254