-
Notifications
You must be signed in to change notification settings - Fork 46
Support discriminated unions correctly #2
Support discriminated unions correctly #2
Conversation
Attrs struct { | ||
Attrs Fattr | ||
} | ||
Attr PostOpAttr |
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.
Curious. What happens the the Follows
word? I don't see it defined in PostOpAttr
.
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.
fdawg! great to hear from you :)
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.
@fdawg4l Welcome! This is sort of like SAX vs DOM parsing for XML. Use of follows determines whether the gated data is even present in the XDR stream and that broke us - seems Linux had always been supplying the data, but when NetApp didn't we had misalignment in the rest of the structure.
To answer the specific query, Follows
has been replaced with the xdr:"union"
annotation which is in the xdr2
package specifically to support discriminated unions.
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.
OOOHH!!! ok cool. Wow. That must've sucked. Good find.
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.
tcpdump + wireshark 4TW. Wireshark has awesome XDR/NFS protocol analytics built in.
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.
It was the only way i made any progress was with wireshark. Agreed. Invaluable tool. And it lets you go up and down the stack; tcp, rpc, nfs.
@@ -194,6 +233,9 @@ func DialService(addr string, prog rpc.Mapping) (*rpc.Client, error) { | |||
} | |||
|
|||
client, err := dialService(addr, port) | |||
if err != nil { |
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.
Wow, that's was pretty big bug... :-)
}, | ||
FH: fh, | ||
Cookie: cookie, | ||
CookieVerf: cookieVerf, |
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.
So was Cookie and CookieVerf the issue with the NTAP box? I don't see how the union handling change wouldve fixed the issue.
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.
Cookie/CookieVerf were preventing us from getting more than 1 directory entry back. I'm not sure why but the first return always seems to be for .
and then you get the rest. Could be related to the FSInfo.dtpref
value, but I couldn't find any expansion on what the preferred multipliers were intended for so left it as is.
The union values were causing most of the problems - for example FSInfo was getting populated with incorrect data because we didn't have the optional FAttr structure - that led to the wtpref
size being 0 so we wrote 0 bytes at a time, got a short write and then died.
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.
Gah. Good point - I'll need to add handling to Write to deal with a wtmax being lower than the data to write.
@@ -68,11 +72,14 @@ type message struct { | |||
} | |||
|
|||
func (c *Client) Call(call interface{}) (io.ReadSeeker, error) { | |||
retries := 1 |
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.
This can get hairy. It's semantically correct, but I was afraid we'd retry atomic operations when really the unmarshaller was off by a few bytes or something nonsensical like that. Anyway, just providing color.
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 - I don't really like it but if I don't then we won't work with certain IBM storage appliances - the actual docs for that appliance contain more details, but relies on the retry.
Saying "we're not using the Linux client" gets awkward as a configuration input.
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.
Cool find. The union handling I suspect wasnt the issue and adds a lot of churn, but the cleanup looks useful. Probably good to call out the cookie handling (i suspect that's the cause) as the bug.
Yes, "woefully" incomplete. I'm planning on using this project in the future and will add some methods as they are needed. Probably good to add issues for the ones that are missing.
@fdawg4l At some point we'll need to add enough support to this to allow for |
To implement |
This makes use of the discriminated union support in the xdr2 package, and uses that to correct several protocol errors in the previous code. This client is still woefully incomplete and likely does not handle various corner cases correctly. Basic testing performed against Linux kernel NFS server and NetApp simulator.
964a7a4
to
5173af6
Compare
@fdawg4l We do when we're copying to a running container, however when copying to a stopped container we have to do it on the appliance where the same VFS issues apply as for the volume store prep. For now we've punted and said that NFS volumes are just not processed for offline containers. |
Write looks fine. Good work! |
This doesn’t work with the declared go-xdr dependency because it requires an unmerged pull request (davecgh/go-xdr#6). See #6. |
Set uint32 | ||
Mode uint32 | ||
SetIt bool `xdr:"union"` | ||
Mode uint32 `xdr:"unioncase=1"` | ||
} |
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.
Sorry for being late to the game, but all sequences of "isset bool + value" are described in the RFC as optional data type. This type supported using xdr:"optional"
and a pointer type, so basically you can replace Mode SetMode
with Mode *uint32 `xdr:"optional"`
and then check Mode != nil
to find out when Mode
is set.
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.
@rasky Thanks for the review at all! This was a very targeted PR done with the NFS RFC in one hand, wireshark trace in the other. Does the same xdr:optional
and pointer values apply for the multi-value unions as well, e.g. this struct?
@matthewavery If you know wireshark/delve can you pick this up and use it to teach those tools to @AngieCris and/or @anchal-agrawal. If not I'll run that with the three of you next time I'm in Austin.
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.
@hickeng the PR is technically correct, I'm just suggesting a better usage of the Go XDR library. If you read RFC4506, there are two different data types: discriminated unions (§4.15) and optional types (§4.19).
Optional types are defined as a special case of discriminated unions, where the discriminator is a boolean type, and the FALSE case is empty; so it does make sense that you can use a discriminated union for those structures, it's just that the optional type is more easy to reason, lets you write less code, and more readable.
NFS RFC has many optional types, but it's true that they're described as XDR unions in the spec. I'm not sure why, must be some historical reason, but the two are really equivalent.
As for applying optional values to multi-field structs, it's sufficient to use the correct level of indirection:
type WccData struct {
Before *struct {
Size uint64
MTime NFS3Time
CTime NFS3Time
} `xdr:"optional"`
After PostOpAttr
}
Notice that you need to change Before
to be a pointer, in addition to adding the optional tag.
cache all entries of a directory
This makes use of the discriminated union support in the xdr2 package,
and uses that to correct several protocol errors in the previous code.
This client is still woefully incomplete and likely does not handle
various corner cases correctly.
Basic testing performed against Linux kernel NFS server and NetApp
simulator.