Custom resource schema modifications#3571
Conversation
Does the PR have any schema changes?Looking good! No breaking changes found. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3571 +/- ##
==========================================
+ Coverage 58.98% 59.83% +0.84%
==========================================
Files 68 69 +1
Lines 8658 8786 +128
==========================================
+ Hits 5107 5257 +150
+ Misses 3081 3037 -44
- Partials 470 492 +22 ☔ View full report in Codecov by Sentry. |
thomas11
left a comment
There was a problem hiding this comment.
LGTM overall, nice work!
Some thoughts:
- I'm not sure why we need to discover all referenced types upfront, since most custom resources won't even use them. Could we offer
findTypesForResourceas a utility for custom resources instead, or even get away with justTypeLookupFuncfor resources that know which types they want to access? - When two custom resources touch the same resource or type, the result depends on the order of iteration
customresources.ApplySchemaModifications. It's sufficiently unlikely so it's ok, I think, but even better would be to keep track of modifications and warn if something is touched a second time. - Do we need
MetaTypeswhenSchemaModificationscan also add metadata?
It felt like an easier API if you get passed values and you return values rather than callbacks. It avoids questions like "what happens if they call the callback lots of times" etc too. The types will only be searched if the custom resource implements the
Yes, good catch on the map iteration. Interestingly this is also the case for all the mixin functions too. I'll change them all to iterate by name for consistency at least.
Not strictly, but it's a simpler form to use if only adding and not modifying anything existing. |
Makes sense!
In isolation, it's simpler, but having two ways to do the same thing is not simpler IMO (think of debugging). There's also nothing keeping people from the footgun of using both. |
Ok, I like the move to just a single function for defining either new or edits for the whole schema as once. I'll just remove the fix from this PR for now, but also make the func(resource *ResourceDefinition) (ResourceDefinition, error) |
64411c7 to
d3a4fe2
Compare
|
Rebased and squashed reworking following review .. and implemented the dashboard changes. |
mikhailshilkov
left a comment
There was a problem hiding this comment.
The approach makes sense on the first glance. I'll review tomorrow - hopefully after you add unit and integration tests. I think we also still want the asserts about the shape of the existing resource that we discussed before.
Do we also want to override resources with explicit versions, since this is much easier with this design?
mikhailshilkov
left a comment
There was a problem hiding this comment.
This looks like a step in the right direction, thank you! Should we check for possibilities to rewrite some existing schema modifications in this style?
mjeffryes
left a comment
There was a problem hiding this comment.
I agree with the general direction here, but there are a lot of details to sort out still. I suspect, it will be easier to reason about as a pure refactor, independent of restoring the dashboard customization.
Would like to see this fully replace all the custom edits in genMixins as well.
2fed4e2 to
5e12e73
Compare
|
I've rebased, addressed the minor comments and reworked the Portal schema to be much more similar to the existing code, with the addition of asserts over each type we're replacing to flag when there's changes upstream which affect the parts we're overwriting. |
mikhailshilkov
left a comment
There was a problem hiding this comment.
The changes since my last review look good. Still missing test coverage.
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| snaps.MatchSnapshot(t, generationResult.Schema) |
There was a problem hiding this comment.
In my opinion, snapshot tests are no substitute to proper unit tests. It was okay to add them post-factum for all the existing code that we had no tests for, but I suggest we don't use them as an excuse not to write tests for net-new non-trivial functionality.
Your current PR is a nice illustration of my case: as far as I can tell, your snapshot is actually not testing anything at all. The only portal resource that the snapshot contains is azure-native:portal/v20200901preview:Dashboard, which is not processed by the custom resource you implemented, so it just passes nil to the Schema method, and the method exists immediately. Yet, the "test" provides an impression that we are testing something, and it's hard to tell otherwise without looking into the details.
@thomas11 @mjeffryes I'd love to get your opinion on this testing approach and whether it matches mine or not.
There was a problem hiding this comment.
Yes, it looks like the snapshot currently misses generating the default resource, I'll take a look at fixing that when I get opportunity.
@mikhailshilkov Perhaps if you're envisioning something specific with the unit tests that you think is more effective, you could contribute it?
There was a problem hiding this comment.
Apart from the snaps vs unit tests question, a test that passes without asserting anything is a bug. Can we add some assertions here to prevent that? Along the lines of "generationResult contains foo".
In general, I'm all for unit tests over snap-based tests, but for things the portal resource where you have a bunch of opaque JSON, snaps can still a good fit.
There was a problem hiding this comment.
How can we test anything except the happy path with snapshot tests? I guess we miss a mechanism to amend snapshots to cause errors/edge cases and assert the outcomes?
There was a problem hiding this comment.
My understanding is that snapshot tests, as we're using them, are strictly for the happy path.
| TypeSpec: schema.TypeSpec{ | ||
| Type: "string", | ||
| }, | ||
| Description: "The dashboard part metadata type.\nExpected value is 'Extension/HubsExtension/PartType/MarkdownPart'.", |
There was a problem hiding this comment.
On the second pass, I'm not sure I like the strictness of this. We will have to re-write this code in case upstream changes the description of a type that we are throwing away, right? That seems unnecessary fragile?
There was a problem hiding this comment.
This is why the original design I proposed attempted to derrive the modified schema from the original schema in order to be more flexible, but the feedback on that was that it was too complex & fragile. The frequency of updates hasn't been too high previously so we can always refine these asserts if needed later.
|
|
||
| existingMarkdownPartMetadataType := resource.Types["azure-native:portal:MarkdownPartMetadata"] | ||
| if !reflect.DeepEqual(existingMarkdownPartMetadataType, expectedMarkdownPartMetadataType("")) { | ||
| return nil, fmt.Errorf("unexpected azure-native:portal:MarkdownPartMetadata type: %#v", existingMarkdownPartMetadataType) |
There was a problem hiding this comment.
Should we provide guidance to future maintainers about what they need to do in this situation?
There was a problem hiding this comment.
Sure, can refine these message further, though having any messages is already a big improvement over the current failure mode.
- Use single function to all the modification or creation of new resources, in the schema and meta as well as creating or modifying the associated types. - Apply modifications in a consistent order. - Implement finding types via type visitor. From review feedback: - Use codegen.SortedKeys - Use early return - Rename 'ok's - Ensure original corresponding meta types exist
- Mirror existing implementation - Add asserts before replacing types - This clearly documents what we're expecting to be present before we add our replacements.
Ensure that changes to the custom portal schema and metadata are visible. - Fix default dashboard version by applying transformations. - Add not nil assertions. - Use JSON matchers instead of generic matchers for better snapshots.
- Rename Schema to LegacySchema. - Rename SchemaF to Schema. - Add deprecation notices to old properties.
Much less verbose snapshot and encodes the language sections correctly.
46f3fe1 to
10d7d04
Compare
|
This PR has been shipped in release v2.64.0. |
|
Note that the "Dashboard Fix" section of the description was not correct anymore, so I deleted it |
Add a more understandable and unified approach to modifying existing parts of the schema.
Use a function to define a resource's schema–resource & types in both the schema and metadata. This is given any existing declaration which it is allowed to modify.
This allows the removal of the custom resource
Types,SchemaandMetafields along with the associatedSchemaMixins,SchemaTypeMixinsandMetaMixinsfunctions.