Unstructured OpenstackProviderSpec#2196
Conversation
|
/assign @lleshchi |
|
/hold Trying to find out if we can do pre-merge testing. |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #2196 +/- ##
==========================================
+ Coverage 57.80% 57.88% +0.08%
==========================================
Files 188 188
Lines 26134 26133 -1
==========================================
+ Hits 15106 15127 +21
+ Misses 9756 9733 -23
- Partials 1272 1273 +1
|
| var u *unstructured.Unstructured | ||
| if err := json.Unmarshal(masterMachine.Spec.ProviderSpec.Value.Raw, u); err != nil { |
There was a problem hiding this comment.
I believe the second argument passed to Unmarshal cannot be nil.
| var u *unstructured.Unstructured | |
| if err := json.Unmarshal(masterMachine.Spec.ProviderSpec.Value.Raw, u); err != nil { | |
| var u unstructured.Unstructured | |
| if err := json.Unmarshal(masterMachine.Spec.ProviderSpec.Value.Raw, &u); err != nil { |
There was a problem hiding this comment.
@fxierh to the rescue again!
Also had to change the line below.
Rebased, revised, rebuilt, and re-pushed quay.io/2uasimojo/hive:openstack-providerspec-unstructured
Thanks!
There was a problem hiding this comment.
I PTO-ed yesterday and I'll test it today.
25794ae to
6911ed3
Compare
|
/hold cancel @dtthuynh has verified this fix manually, seems to work 👍 |
| providerSpec, err := decodeOpenStackMachineProviderSpec(masterMachine.Spec.ProviderSpec.Value, scheme) | ||
| if err != nil { | ||
| func getOpenStackOSImage(masterMachine *machinev1beta1.Machine, logger log.FieldLogger) (string, error) { | ||
| var u unstructured.Unstructured |
There was a problem hiding this comment.
The OpenStack provider spec go type is in o/api, so you could use the structured type here rather than unstructured
There was a problem hiding this comment.
Well, that's the whole issue, isn't it? How would I unmarshal it into a structured type if I don't know which apiVersion it came from? I had considered trying one and then the other... but this seemed more generic.
There was a problem hiding this comment.
When the providerspecs were migrated from their original locations to the new locations, they remained backward compatible. The api version changed, but from a yaml unmarshalling and re-marshalling, it's absolutely compatible. You should be able to just work with the newer types from o/api
There was a problem hiding this comment.
I'm still missing something though: How do we unmarshal the old-apiVersion Raw into the new-apiVersion go object? That's what the code was attempting to do before, and it was erroring. Or do we do it by casting the Object instead? Or is it because we were using a decoder that had some internal validation for the apiVersion?
There was a problem hiding this comment.
Don't use the decoder. The old raw in a standard json.Unmarshal will unmarshal without error into the new struct. The decoder validates internally by trying to find the correct go type for a registered and known API group and version, but we weren't registering those because they are raw extensions
There was a problem hiding this comment.
Okay, I think I get it. Using json.Unmarshal doesn't try to validate that the go object was defined in the same library that the json blob's .apiVersion field represents. (How would it? That's the point of registering the schema.) So all it would be doing is translating fields using the standard json-to-golang rules and blatting in the values.
Side note: It's legal for the schema to not follow those standard field mapping rules. So in theory we could end up with blank fields in the go object that would have been populated if we were using the schema-based decoder. Right?
Side note: Presumably we would want to use non-strict unmarshaling. If (additive, backward-compatible) changes are made to the APIs and there's a lag before we revendor to incorporate those changes, we don't want the unmarshal to puke because it can't match a field in the struct. (In theory the aforementioned nonstandard field name mappings could also trigger same.)
Side note: Um, if I unmarshaled an old-apiVersion object into a new-apiVersion struct, I would end up with the apiVersion in the struct being "wrong" -- i.e. not matching the library I imported the struct from -- true? As long as I'm careful about how I'm using the thing, it wouldn't be a problem, but I can see it being dangerous to start sending it around the ecosystem in that form...
All that said, pathing the raw json as we ended up doing here seems like not a significantly different degree of evil, so I'm thinking to keep it as is. Are you okay with that?
There was a problem hiding this comment.
Side note: It's legal for the schema to not follow those standard field mapping rules. So in theory we could end up with blank fields in the go object that would have been populated if we were using the schema-based decoder. Right?
Is it? I've not seen that. Can you provide an example?
Side note: Presumably we would want to use non-strict unmarshaling. If (additive, backward-compatible) changes are made to the APIs and there's a lag before we revendor to incorporate those changes, we don't want the unmarshal to puke because it can't match a field in the struct. (In theory the aforementioned nonstandard field name mappings could also trigger same.)
API changes should only be additive, to my knowledge there has been no breaking change in MAPI providerSpecs so you should be able to use unmarshal strict without issue
Side note: Um, if I unmarshaled an old-apiVersion object into a new-apiVersion struct, I would end up with the apiVersion in the struct being "wrong" -- i.e. not matching the library I imported the struct from -- true? As long as I'm careful about how I'm using the thing, it wouldn't be a problem, but I can see it being dangerous to start sending it around the ecosystem in that form...
The string isn't checked anywhere in MAPI, and the marshal/unmarshal shouldn't be affected by the change in go type, as mentioned, you may start seeing additional fields marshalled to their empty values maybe, but, this won't affect MAPI since we always unmarshal directly into the new go types
All that said, pathing the raw json as we ended up doing here seems like not a significantly different degree of evil, so I'm thinking to keep it as is. Are you okay with that?
If you feel more comfortable with that then sure. IMO aligning with what we do in MAPI is probably better but this works as well
| } | ||
| } | ||
| return spec, nil | ||
| return u.Object["image"].(string) |
There was a problem hiding this comment.
Would it be good practice to add some error handling here, in case of an unexpected object error (i.e. future api changes such as this one)?
There was a problem hiding this comment.
Totally. I'll work something up.
There was a problem hiding this comment.
I'm wondering if it is possible to use utilities defined in the unstructured package like unstructured.NestedString which returns a triplet of type (string, bool, error) ?
e.g.
hive/vendor/k8s.io/kubectl/pkg/cmd/wait/wait.go
Lines 536 to 564 in 25794ae
There was a problem hiding this comment.
I also considered using gjson. Figured the paths were small/simple enough that it wasn't worth it... but now looking at it I'm not so sure. Let me play...
There was a problem hiding this comment.
Yeah, I like that a lot better @fxierh, changed. Thanks for the suggestion!
6911ed3 to
a5cf25d
Compare
Another attempt to address the move of OpenstackProviderSpec from cluster-api-provider-openstack to openstack/api/machine. See openshift#2117 for background. In that PR, we simply replaced our imports. The result is that we could only decode the _new_ thing. The problem is that we may still get the _old_ thing if we're talking to an old cluster. We only need the decoding to steal the osImage from the master machines so we can use that value for new workers. Rather than importing both schemata, trying to figure out which version we're talking to, and decoding against the correct one, this commit unmarshals the providerSpec as raw JSON into an Unstructured object and explicitly paths into it map-wise. On the creation side, we're told that the spoke cluster itself will understand the new thing even if it's an old version (MAPI also unmarshals the JSON raw). So it should be fine to continue using recent vendored installer code to generate MachineSets (which will contain new-apiVersion OpenstackProviderSpec). HIVE-2308
a5cf25d to
28dac77
Compare
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: 2uasimojo, lleshchi The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@2uasimojo: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
|
/cherry-pick mce-2.5 |
Sure, just ignore me completely, bot. I'll do it manually: #2200 |
This is a manual backport of openshift#2196 to address the move of OpenstackProviderSpec from cluster-api-provider-openstack to openstack/api/machine. We need to decode OpenstackProviderSpec to steal the osImage from the master machines so we can use that value for new workers. Rather than importing both schemata, trying to figure out which version we're talking to, and decoding against the correct one, this commit unmarshals the providerSpec as raw JSON into an Unstructured object and explicitly paths into it map-wise. On the creation side, we're told that the spoke cluster itself will understand the new thing even if it's an old version (MAPI also unmarshals the JSON raw). So it should be fine to continue using the existing version of vendored installer code to generate MachineSets. HIVE-2308
This is a manual backport of openshift#2196 to address the move of OpenstackProviderSpec from cluster-api-provider-openstack to openstack/api/machine. We need to decode OpenstackProviderSpec to steal the osImage from the master machines so we can use that value for new workers. Rather than importing both schemata, trying to figure out which version we're talking to, and decoding against the correct one, this commit unmarshals the providerSpec as raw JSON into an Unstructured object and explicitly paths into it map-wise. On the creation side, we're told that the spoke cluster itself will understand the new thing even if it's an old version (MAPI also unmarshals the JSON raw). So it should be fine to continue using the existing version of vendored installer code to generate MachineSets. HIVE-2308
Another attempt to address the move of OpenstackProviderSpec from cluster-api-provider-openstack to openstack/api/machine. See #2117 for background. In that PR, we simply replaced our imports. The result is that we could only decode the new thing. The problem is that we may still get the old thing if we're talking to an old cluster.
We only need the decoding to steal the osImage from the master machines so we can use that value for new workers. Rather than importing both schemata, trying to figure out which version we're talking to, and decoding against the correct one, this commit unmarshals the providerSpec as raw JSON into an Unstructured object and explicitly paths into it map-wise.
On the creation side, we're told that the spoke cluster itself will understand the new thing even if it's an old version (MAPI also unmarshals the JSON raw). So it should be fine to continue using recent vendored installer code to generate MachineSets (which will contain new-apiVersion OpenstackProviderSpec).
HIVE-2308