From: Victor Toso <victortoso@redhat.com>
To: Andrea Bolognani <abologna@redhat.com>
Cc: qemu-devel@nongnu.org, "Eric Blake" <eblake@redhat.com>,
"Markus Armbruster" <armbru@redhat.com>,
"John Snow" <jsnow@redhat.com>,
"Daniel P . Berrangé" <berrange@redhat.com>
Subject: Re: [RFC PATCH v2 2/8] qapi: golang: Generate qapi's alternate types in Go
Date: Mon, 22 Aug 2022 08:59:56 +0200 [thread overview]
Message-ID: <20220822065956.nmamhjgowbda6dha@tapioca> (raw)
In-Reply-To: <CABJz62PZvdem1C-m-ODVMLrFaN6kqqJm0qyvbLxqeRPXL5jDaA@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 5414 bytes --]
Hi,
On Fri, Aug 19, 2022 at 11:27:13AM -0500, Andrea Bolognani wrote:
> On Wed, Aug 17, 2022 at 04:04:19PM +0200, Victor Toso wrote:
> > On Tue, Jul 05, 2022 at 08:45:06AM -0700, Andrea Bolognani wrote:
> > > On Fri, Jun 17, 2022 at 02:19:26PM +0200, Victor Toso wrote:
> > > > func (s *BlockdevRef) UnmarshalJSON(data []byte) error {
> > > > // Check for json-null first
> > > > if string(data) == "null" {
> > > > return errors.New(`null not supported for BlockdevRef`)
> > > > }
> > > > // Check for BlockdevOptions
> > > > {
> > > > s.Definition = new(BlockdevOptions)
> > > > if err := StrictDecode(s.Definition, data); err == nil {
> > > > return nil
> > > > }
> > >
> > > The use of StrictDecode() here means that we won't be able to
> > > parse an alternate produced by a version of QEMU where
> > > BlockdevOptions has gained additional fields, doesn't it?
> >
> > That's correct. This means that with this RFCv2 proposal, qapi-go
> > based on qemu version 7.1 might not be able to decode a qmp
> > message from qemu version 7.2 if it has introduced a new field.
> >
> > This needs fixing, not sure yet the way to go.
> >
> > > Considering that we will happily parse such a BlockdevOptions
> > > outside of the context of BlockdevRef, I think we should be
> > > consistent and allow the same to happen here.
> >
> > StrictDecode is only used with alternates because, unlike unions,
> > Alternate types don't have a 'discriminator' field that would
> > allow us to know what data type to expect.
> >
> > With this in mind, theoretically speaking, we could have very
> > similar struct types as Alternate fields and we have to find on
> > runtime which type is that underlying byte stream.
> >
> > So, to reply to your suggestion, if we allow BlockdevRef without
> > StrictDecode we might find ourselves in a situation that it
> > matched a few fields of BlockdevOptions but it the byte stream
> > was actually another type.
>
> IIUC your concern is that the QAPI schema could gain a new
> type, TotallyNotBlockdevOptions, which looks exactly like
> BlockdevOptions except for one or more extra fields.
>
> If QEMU then produced a JSON like
>
> { "description": { /* a TotallyNotBlockdevOptions here */ } }
>
> and we'd try to deserialize it with Go code like
>
> ref := BlockdevRef{}
> json.Unmarsal(&ref)
>
> we'd end up mistakenly parsing the TotallyNotBlockdevOptions as a
> valid BlockdevOptions, dropping the extra fields in the process.
>
> Does that correctly describe the reason why you feel that the use of
> StrictDecode is necessary?
Not quite. The problem here is related to the Alternate types of
the QAPI specification [0], just to name a simple in-use example,
BlockdevRefOrNul [1].
[0] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/devel/qapi-code-gen.rst?plain=1#L387
[1] https://gitlab.com/qemu-project/qemu/-/blob/master/qapi/block-core.json#L4349
To exemplify the problem that I try to solve with StrictDecode,
let's say there is a DeviceRef alternate type that looks like:
{ 'alternate': 'DeviceRef',
'data': { 'memory': 'BlockdevRefInMemory',
'disk': 'BlockdevRefInDisk',
'cloud': 'BlockdevRefInCloud' } }
Just a quick recap, at runtime we don't have data's payload name
(e.g: disk). We need to check the actual data and try to find
what is the payload type.
type BlockdevRefInMemory struct {
Name *string
Size uint64
Start uint64
End uint64
}
type BlockdevRefInDisk struct {
Name *string
Size uint64
Path *string
}
type BlockdevRefInCloud struct {
Name *string
Size uint64
Uri *string
}
All types have unique fields but they all share some fields too.
Golang, without StrictDecode would happily decode a "disk"
payload into either "memory" or "cloud" fields, matching only
what the json provides and ignoring the rest. StrictDecode would
error if the payload had fields that don't belong to that Type so
we could try to find a perfect match.
While this should work reliably with current version of QEMU's
qapi-schema.json, it is not future error proof... It is just a
bit better than not checking at all.
The overall expectations is that, if the fields have too much in
common, it is likely they should have been tagged as 'union'
instead of 'alternate'. Yet, because alternate types have this
flexibility, we need to be extra careful.
I'm still thinking about this in a way that would not give too
much hassle when considering a generated code that talks with
older/newer qemu where some fields might have been added/removed.
> If so, I respectfully disagree :)
>
> If the client code is expecting a BlockdevRef as the return
> value of a command and QEMU is producing something that is
> *not* a BlockdevRef instead, that's an obvious bug in QEMU. If
> the client code is expecting a BlockdevRef as the return value
> of a command that is specified *not* to return a BlockdevRef,
> that's an obvious bug in the client code.
>
> In neither case it should be the responsibility of the SDK to
> second-guess the declared intent, especially when it's
> perfectly valid for a type to be extended in a
> backwards-compatible way by adding fields to it.
Yes, the SDK should consider valid QMP messages. This specific
case is just a bit more complex qapi type that SDK needs to
worry.
Thanks for your input!
Victor
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
next prev parent reply other threads:[~2022-08-22 7:03 UTC|newest]
Thread overview: 53+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-17 12:19 [RFC PATCH v2 0/8] qapi: add generator for Golang interface Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 1/8] qapi: golang: Generate qapi's enum types in Go Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 2/8] qapi: golang: Generate qapi's alternate " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-08-17 14:04 ` Victor Toso
2022-08-19 16:27 ` Andrea Bolognani
2022-08-22 6:59 ` Victor Toso [this message]
2022-08-29 11:27 ` Markus Armbruster
2022-08-29 13:31 ` Victor Toso
2022-09-02 14:49 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 3/8] qapi: golang: Generate qapi's struct " Victor Toso
2022-06-17 14:41 ` Daniel P. Berrangé
2022-06-17 15:23 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 4/8] qapi: golang: Generate qapi's union " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:35 ` Daniel P. Berrangé
2022-07-06 9:28 ` Andrea Bolognani
2022-07-06 9:37 ` Daniel P. Berrangé
2022-07-06 9:48 ` Daniel P. Berrangé
2022-07-06 12:20 ` Andrea Bolognani
2022-08-17 16:25 ` Victor Toso
2022-08-19 7:20 ` Victor Toso
2022-08-19 15:25 ` Andrea Bolognani
2022-08-22 6:33 ` Victor Toso
2022-08-17 16:06 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 5/8] qapi: golang: Generate qapi's event " Victor Toso
2022-07-05 15:45 ` Andrea Bolognani
2022-07-05 16:47 ` Daniel P. Berrangé
2022-07-06 14:53 ` Andrea Bolognani
2022-07-06 15:07 ` Daniel P. Berrangé
2022-07-06 16:22 ` Andrea Bolognani
2022-08-18 7:55 ` Victor Toso
2022-08-18 7:47 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 6/8] qapi: golang: Generate qapi's command " Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 7/8] qapi: golang: Add CommandResult type to Go Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-07-05 16:49 ` Daniel P. Berrangé
2022-08-17 15:00 ` Victor Toso
2022-06-17 12:19 ` [RFC PATCH v2 8/8] qapi: golang: document skip function visit_array_types Victor Toso
2022-06-27 7:15 ` [RFC PATCH v2 0/8] qapi: add generator for Golang interface Markus Armbruster
2022-06-27 12:48 ` Victor Toso
2022-06-27 15:29 ` Markus Armbruster
2022-08-18 8:04 ` Victor Toso
2022-07-05 15:46 ` Andrea Bolognani
2022-08-17 14:24 ` Victor Toso
2022-08-29 11:53 ` Markus Armbruster
2022-08-29 14:05 ` Victor Toso
2024-11-07 10:43 ` Daniel P. Berrangé
2024-11-07 12:36 ` Markus Armbruster
2024-11-07 13:06 ` Daniel P. Berrangé
2024-11-07 13:35 ` Daniel P. Berrangé
2024-11-07 14:18 ` Markus Armbruster
2024-11-08 9:43 ` Victor Toso
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220822065956.nmamhjgowbda6dha@tapioca \
--to=victortoso@redhat.com \
--cc=abologna@redhat.com \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=eblake@redhat.com \
--cc=jsnow@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).