Skip to content
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

Updates to Custom kind for new schema format #9

Merged
merged 11 commits into from
Apr 20, 2023
Merged

Conversation

IfSentient
Copy link
Contributor

@IfSentient IfSentient commented Apr 13, 2023

Initial work on the Custom kind to enforce the new schema format, specifically when the kind as a crd trait.

If a Custom kind has the crd trait, then the lineage's schema enforces a specific format using joinSchema:

{
    metadata: CommonMetadata & {...string}
    spec: {...}
    status: CommonStatus & {...}
}

As part of adding the optional crd trait, the appID field was added to allow for generating the group (see TODO note about this).

Also as a part of this PR, a codegen trait was added, allowing app/plugin development tooling to know what kind of code to generate from this kind.

…e a schema format when being used to generate a CRD.
// TODO: should this just be pluginID? Is there a world where an app and plugin may not share the same ID?
// The practical reason to have it different is to allow the appID to be shorter than the pluginID as the group
// is generated from the appID, and <kind name>+<group> cannot exceed 63 characters in kubernetes.
appID: =~"^([a-z][a-z0-9-]{0,61}[a-z0-9])$"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with @IfSentient a nice compromise might be to default to use the plugin id, but in case that results in a longer group than 63 characters we could allow an override (appId or group). This will allow plugin authors to easily determine the rules, if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR so that now there is a plugin trait with an id field (there is a TODO on whether this should be a trait, or if pluginID should just be a top-level field in the kind). The crd trait has an optional groupOverride field which can be used to override using plugin.id as the group name.

Copy link
Member

@marefr marefr Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Yeah let's discuss plugin trait vs part of kind. Since common and composable kinds refer to plugins maybe make most sense to include plugin id as a field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done a weird temporary compromise where pluginID is a top-level field now, but the plugin trait still exists, and plugin.id is set to the pluginID field. Not sure if we want the trait to continue to exist, or if we want to revert that and have the ID live solely in the trait. I'm somewhat unopinionated on this, because I'm not sure if we're going to ever need more plugin info in the kind--if we do, we'll want to keep the trait. If there's both the trait and pluginID fields, it seems a bit confusing to use.

…ded plugin trait for specifing plugin ID which nominally determines CRD group unless crd.groupOverride is provided.
…ata in Custom.lineage.joinSchema, as thema codegen fails for externally-defined types. Update CustomProperties in props.go to reflect new traits added to the Custom kind. Add the method ToDef in load.go, which works similarly to ToKindProps, except it returns a def that includes the props _and_ the unified CUE value, allowing changes to the lineage to be part of the CUE value when passed to Bind<X>.
@IfSentient IfSentient marked this pull request as ready for review April 17, 2023 00:28
@IfSentient IfSentient changed the title [WIP] Updates to Custom kind for new schema format Updates to Custom kind for new schema format Apr 17, 2023
load.go Show resolved Hide resolved
joinSchema: {
// metadata contains embedded CommonMetadata and can be extended with custom string fields
// TODO: use CommonMetadata instead of redfining here; currently needs to be defined here
// without extenal reference as using the CommonMetadata reference breaks thema codegen.
Copy link
Contributor

@sdboyer sdboyer Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What breaks, exactly?

We should be able to specifically force dereferencing of such a type with a NameFunc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got a nasty nil pointer panic from inside of cue's openapi package, I didn't look too far into it, but it appears that it's a problem when there's an external ref. For now, it was simpler to not dig into that and unify the CommonMetadata model into the joinSchema once that's been worked out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried adding a NameFunc, and it panics before I see any calls to the NameFunc. I'll open an issue for this in thema and look into it after dealing with this PR (as I have a tighter timeline with getting this into kindsys).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM on timing - and huh! well, i'm poking at that code right now as part of lineage flattening, i'll keep an eye out for panics

Comment on lines 49 to 55
// If the crd trait is defined, the schemas in the lineage must follow the format:
// {
// "metadata": CommonMetadata & {...string}
// "spec": {...}
// "status": {...}
// }
if S.crd != _|_ {
Copy link
Contributor

@sdboyer sdboyer Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a couple ways we might structure the CUE, depending on whether we want to make using the CRD trait a decision that's made in kindsys for an entire kind category, or left up to individual kind authors.

Regardless, i'd suggest that for introspectability purposes, it's worth turning this comparison to bottom into a simple boolean in a top-level field within the kind, such as isCRD: crd != _|_, then referencing that field here. The fewer comparisons to bottom the better, and having an explicit field will make a kind trivially introspectable either on Go structs or when exported to JSON.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've now addressed all this:

  • created isCRD, which is equal to S.crd == _|_
  • moved the definition of metadata/spec/status out of the lineage and into a kindsys top-level _crdSchema type (tested--codegen still works fine with this)
  • moved the isCRD check out of the lineage to the root of the kind, if true, it applies lineage: joinSchema: _crdSchema

Let me know if I'm missing something here, or if I should do anything in an alternate way.

@@ -10,6 +98,79 @@ package kindsys
Custom: S={
_sharedKind

lineage: { name: S.machineName }
// pluginID is the unique identifier of the plugin which owns this Custom kind
pluginID: =~"^([a-z][a-z0-9-]*[a-z0-9])$"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on whether this should be a top-level field, or live only in the plugin trait (or even if the plugin trait should disappear, and only the pluginID reference should remain)?

Copy link
Contributor

@sdboyer sdboyer Apr 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we need to firm up the notion of a trait to really decide.

For the CUE part of it, i've been imagining traits as discrete sets of fields that can be composed into kind category definitions. Those sets - which in this PR i would imagine include both the top-level props and the joinschema - would then be imported into the github.com/grafana/kindsys CUE package and composed into the kind category definitions in as simple a manner possible. i'm thinking of that as valuable because as a reader/maintainer of the kind cat definitions, i want to be able to easily see and reason about how the trait is composed across kind categories, basically independent of what's in the trait itself.

Given the above, and because we actually need plugins to be a container/wrapper around kind definitions, they fall at least partly outside my current operating concept of "trait". Happy to change my mind here if we find something that feels right, though!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me, I think we can ditch the plugin trait in favor of just having the pluginID field as the top-level prop for Custom.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IfSentient this is nit-picking but I would prefer if the kind system weren't aware of specific entities owning kinds. IMHO this is in agreement to sam's point about plugins (or apps) being containers of kind definitions.

To that end I suggest renaming pluginID to something more general, like a kindGroup or simply group (similar to CRD groups basically).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on discussions/input yesterday and the need for grouping all kinds (core, custom, composable) to fit in a schema registry I think group or similar makes more sense as well. Currently working on a doc that hopefully should propose how all of this are supposed to fit together in regards to a schema registry and plugins.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mind leaving it as this either for now. We can change it later if needed given it happens soon (before too many depends on this)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated it to be group in the top-level, just so we don't need to change as much later, and it doesn't explicitly reference plugins within the kind itself.

@sdboyer
Copy link
Contributor

sdboyer commented Apr 17, 2023

cc @AgnesToulet

kindcat_custom.cue Outdated Show resolved Hide resolved
Copy link

@radiohead radiohead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from my side. I left a nitpicky comment about the naming of the pluginID field but I don't mind leaving it as it is.

if S.crd.groupOverride != _|_ {
strings.ToLower(S.crd.groupOverride) + ".apps.grafana.com",
}
strings.ToLower(strings.Replace(S.pluginID, "-","_",-1)) + ".apps.grafana.com"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an open comment here as well: <kind>.<pluginID/custom override>.apps.grafana.com--do we want this to be apps.grafana.com, ext.grafana.com, plugins.grafana.com? I think it makes sense to group all custom kinds under one <x>.grafana.com, as that way they can't overlap with core kinds (which are <kind>.grafana.com), but what should be?

CC: @bcotton @radiohead @mildwonkey

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ext.grafana.com works for me.

Side note – IMHO the core kinds should also be grouped under a subdomain, e.g. core.grafana.com.

// This field could be inlined into `group`, but is separate for clarity.
_computedGroups: [
if S.crd.groupOverride != _|_ {
strings.ToLower(S.crd.groupOverride) + ".apps.grafana.com",
Copy link

@bcotton bcotton Apr 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "apps" too specific?

Will there be cases where Kinds are defined in the scope of other "extensions". Datasources, Apps, Plugins generally?

Perhaps "ext.grafana.com"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm in favor of ext.grafana.com. Doesn't take up too much of the allotted 63 characters for the full GroupKind, and is clear enough about what it is (an extension vs something in core).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ext.grafana.com works for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now uses <group>.ext.grafana.com (where group is typically the plugin ID). groupOverride can be set, which overrides the entire crd.group property, so there is a way to make the group something else if the need arises.

…ana.com', remove openness (...) wherever possible.
…efore 'ext.grafana.com', as 'group' is no longer explicitly tied to plugin ID.
Copy link
Contributor

@sdboyer sdboyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Since this is scoped to only custom kinds, it doesn't force our hand on integrating with core kinds yet. That'll be a key follow-up once this is merged

@@ -130,19 +140,19 @@ Custom: S={
// This field could be inlined into `group`, but is separate for clarity.
_computedGroups: [
if S.crd.groupOverride != _|_ {
strings.ToLower(S.crd.groupOverride) + ".apps.grafana.com",
strings.ToLower(S.crd.groupOverride) + ".ext.grafana.com",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IfSentient as per our discussion earlier this week, let's make sure there's an escape hatch of overriding also the group suffix, in case there's no viable migration path for SLO / CLE apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, most recent commit should have groupOverride overriding the whole group string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants