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

[Feature] Support for explicit versioning of definitions #6435

Open
Kolossi opened this issue Jan 4, 2024 · 26 comments
Open

[Feature] Support for explicit versioning of definitions #6435

Kolossi opened this issue Jan 4, 2024 · 26 comments
Labels

Comments

@Kolossi
Copy link
Contributor

Kolossi commented Jan 4, 2024

Description

This feature request is to add the facility for component/trait etc definitions to explicitly specify a version as part of the definition.

The Application will then have the ability to refer to the definition version when using a definition.

Exisiting situation

Definitions currently have a version field but this is automatically assigned:

The Version Control for Definitions docs have:

When an OAM definition is updated, KubeVela will automatically generate a new revision of this definition.
[my emphasis]

This is a simple version number, being an integer. Being auto-assigned causes problems in not being able to predict what version will need to be used in the Application file, and the version number may be different in different environments such as dev,staging,prod)

Possible Solutions

My preference as the originator would be to use semantic versioning for the definition version.

This could either be by repurposing the existing version field, or by adding a new version field.

Referencing the defintion within the Application could be allowed as follows:

    # latest (no version specified)
    type: wb-service
    # specific
    type: wb-service@v3.0.1
    # non-specific
    type: >wb-service@v1.2

Alternatives

  • a current workaround is to call the definition e.g. "wbserver-v1.0.1" then "wbserver-v1.0.2" etc. This would meet requirements but has knock-on effects e.g. bloat of vela def list
  • During Kubevela Community call 4th-Jan-2024, @linaking-guidewire mentioned that the versioning could use the CRD alpha/beta/v1 format.

Additional considerations

  • is any form of old version cleanup wanted/possible? (could it automatically discover used and unused versions in the cluster)
  • what actions if any are to be taken regarding checking whether a given version is available?
  • can anything be done to easy deployment of a given defintion version to multiple clusters

References

This proposal originated from a slack thread created by Frederick Boismenu.

It was also discussed on the Kubevela Community call 4th-Jan-2024.

@Kolossi
Copy link
Contributor Author

Kolossi commented Jan 4, 2024

I'll admit I've not a great understanding of the CRD alpha/beta/v1 versioning - by which I mean how does the consumer identify breaking/non-breaking changes, patches etc?

Using it would obviously be "the kubernetes way". @linaking-guidewire, care to pitch in here with a description and case for using that over semver 🙂 ?

@FogDong
Copy link
Member

FogDong commented Jan 5, 2024

I also like the idea of using CRD version like v1alpha, which probably gives us the space to add migration func from v1 to v2.

@Kolossi
Copy link
Contributor Author

Kolossi commented Jan 8, 2024

So @FogDong / @linaking-guidewire I get how v1alpha/v1beta/v1 works when taking a new definition through concept to full "v1" implementation. But how does this work if we are adding a new feature to an already out there definition?

For example say I want to add a new feature to the cpuscaler trait to support scaleDown/scaleUp policies. In semver world this would lead to a minor version bump e.g. to v1.1.0 - or if the implementation leads to a breaking change it might be v2.0.0.

How would this scenario be handled under v1alpha/v1beta/v1 approach? (genuine question, this is where I don't understand this approach).

@fboismenu
Copy link

I see components as being managed products by a platform engineering team with the promise that they can abstract away infrastructure details, encapsulate some policies, etc.

Kubernetes standard interface allows to nicely decouple Platform Eng and users workflow. For example doing a rolling restart/upgrade of Kubernetes nodes should not impact user workload availability and does not require synchronisation between teams.

With KubeVela we would like to retain this separation of concerns, and I feel components are kind in the way without this feature.

IMHO it would be ideal if Platform Eng could progressively rollout new component versions, for example by doing a progressive patching of all Application using the component(s) to update,
without requiring users synchronization or just waiting for them to reploy (some stable apps may redeploy only each month for example).

@Kolossi
Copy link
Contributor Author

Kolossi commented Jan 11, 2024

Thanks @fboismenu . I'm mostly in agreement on your points.

One thing I'd like to note though is the proposal here to allow rollout of new definition versions (my emphasis). I think this is a good feature but would need to be optional, I wouldn't favour it being done automatically. I get it might be a good idea in dev, but would not want to do a bulk upgrade in prod, it would be akin to using :latest imho (not a good thing!).

If it is possible and if we choose semver, it should not allow bulk upgrade to a new major version number - the point of this in semver is that a major version number indicates a breaking (or at least very significant) change, and istm this should require review by teams.

Whilst a minor or patch version update is intended to be non-breaking there's no guarantees so personally I wouldn't see even just a patch version increment as a green light to bulk auto-upgrade a definition, I'd want to choose to do that, but I don't see a problem with it being an optional approach for those that want it.

Thoughts anyone?

@FogDong
Copy link
Member

FogDong commented Jan 12, 2024

I agree that versioning is an optional setting for definitions.

In semantic versioning, bumping the major version usually means there's a big change that could break things. Our current setup automatically increases the version with each update, and we're only tracking the major version at this point. To keep things smooth for KubeVela users, we're saying that a major version increase won't be a breaking change that requires manual intervention.

In practice, updating definitions often just means adding a few new fields, which doesn't cause any disruption. This suggests that the typical approach should be to update the minor version—it's not as critical as changes to CRDs. Given this, I think semantic versioning could be a better fit for versioning our definitions.

@linaking-guidewire
Copy link

Semver is fine as long as we support specifying ranges, but I do think the Kubernetes API versioning would be a more standard approach for this case - "the Kubernetes way" as you mentioned @Kolossi.
From my perspective, although they are not CRDs, KubeVela definitions still behave as extensions to the Kubernetes API, so I would expect (as a user) for them to be versioned similarly to the Kubernetes API standards and deprecated/removed similarly to the Kubernetes deprecation policy.
This pattern does put more responsibility on the developer of the definition to make backwards-compatible changes, perform thorough regression tests, and possibly build in automated conversions, but then the user rarely needs to take any action and could easily uptake new features of a definition at their own pace. Just going off of the use cases I have seen at Guidewire so far, I can't think of a situation where we would want a user to explicitly specify minor or patch versions of a definition. I would fear that offering that as an option at all could result in us supporting many different versions of a definition at once in a cluster with many tenants. That is more concerning to me than bulk upgrade.

@dhiguero
Copy link
Collaborator

I like the proposal, but have some doubts on the options that are being proposed. In terms of calling component versions I think adding the version as proposed by @Kolossi will provide a user friendly approach for users deploying applications. To add an example of what I am understanding that would be:

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: app-with-comp-versioning
spec:
  components:
    - name: backend
      type: my-component-type@1.0.2

However, from the component definition side of things, and linking with @FogDong comment, we should also define where the version will be specified. Should we add an optional annotation such as definition.oam.dev/version which could be compatible with the existing v1beta1 component specification, or should we modify the ComponentDefinition to include a spec.version field which could require a new v1beta2 entity?

apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  name: <ComponentDefinition name>
  annotations:
    definition.oam.dev/description: <Function description>
    # Option 1: using annotations
    definition.oam.dev/version: <semver>
spec:
  #Option 2: modifying the spec
  version: <semver>
  workload: # Workload Capability Indicator
    definition:
      apiVersion: <Kubernetes Workload resource group>
      kind: <Kubernetes Workload types>
  schematic:  # Component description
    cue: # Details of components defined by CUE language
      template: <CUE format template>

@dhiguero
Copy link
Collaborator

I have been giving this a second tough, and I have a doubt regarding the expected use case. Are we considering the possibility of having to versions of the same component available in the cluster? or is this just to enforce the fact that an application could only be deployed with a specific subset of component versions? @Kolossi which is the use you have in mind for this feature?

The main problem I see if we need to store two component definitions in the cluster at the same time is the naming of the CRDs, as it may require having a name for the CRD entity (e.g., metadata.name: my-component-vX.Y.Z), and another canonical name for the component definition matched by KubeVela (e.g., spec.name: my-component) since it is not possible to have two crd entities with the same name as one will be overwritten by the other.

@Kolossi
Copy link
Contributor Author

Kolossi commented Jan 30, 2024

Thanks for reminding me about this @dhiguero, I've got sidetracked lately.

Yes the scenario you have raised - of having multiple versions of the same definition (might be component, trait etc) - is what I'm after. When using custom traits there's the usual same SDLC scenarios of issuing patches, enhancements, breaking changes but whilst not wanting to break existing items.

I'm still very curious as to how the alpha/beta/v1 proposal addresses this and no-one's posted how this could work?

@wonderflow
Copy link
Collaborator

Thanks for this proposal!

IMHO it would be ideal if Platform Eng could progressively rollout new component versions, for example by doing a progressive patching of all Application using the component(s) to update, without requiring users synchronization or just waiting for them to redeploy (some stable apps may redeploy only each month for example).

I personally agree with @fboismenu most. And I don't really care in which way we name the version, whatever it's semantic versioning or CRD alpha/beta/v1 versioning, they're actually tags which shouldn't be aware by app developers, they should be handled by platform builders internally.

The question here is how we can make it, a rolling upgrade for definition version. My proposal should be adding an annotation on App automatically and let the annotation to specify the real version.

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: app-with-comp-versioning
  annotations:
    oam.definition.versioning: "my-component-type@1.0.2, my-foo-def@v0.1.0"
spec:
  components:
    - name: backend
      type: my-component-type
    - name: frontend
      type: my-foo-def

Kubevela webhook can check extra rollout policy to add this kind of annotations automatically, other systems or users can also have the flexibility to specify it.

I personally think we'd better avoid deign the versioning feature to be too complex, it should be the platform builders capability to maintain the compatibility, or they can invent a new definition name. That means we shouldn't support fancy feature like "type: >wb-service@v1.2".

@Kolossi
Copy link
Contributor Author

Kolossi commented Feb 1, 2024

IMHO auto update should never be the default, particularly in live. If others want it then fine let them as an option but I wouldn't want to see auto update being the default behaviour - and definitely not the only behaviour!

Aside from that I'd still like to see the worked-through scenario of how anyone thinks name, version number and update process will work in alternate proposed approaches in the case of

  • a minor patch
  • a new feature
  • a breaking change

How will the numbering/naming work?

My proposal is:

  • minor patch
    • current version is {x}.{y}.{z}, new version is {x}.{y}.{z+1}
    • component/trait name unchanged
    • new version specified explicitly in Application definition when developer chooses, and application redeployed
  • a new feature
    • current version is {x}.{y}.{z}, new version is {x}.[y+1}.{z}
    • component/trait name unchanged
    • new version specified explicitly in Application definition when developer chooses, and application redeployed
  • a breaking change
    • current version is {x}.{y}.{z}, new version is {x+1}.{y}.{z}
    • component/trait name unchanged
    • new version specified explicitly in Application definition when developer chooses (and has consulted CHANGELOG for details on how to resolve breaking changes), and application redeployed

Using the above I don't personally see the need for the > syntax, the exact version should be specified otherwise it's just a version of the docker ":latest" tag problem.

It seems to me that from what you've said @wonderflow your proposal would be the following, but please repost exactly what you mean not my words :-)

  • minor patch
    • current version is core.oam.dev/v{x}, new version is unchanged as core.oam.dev/v{x}
    • component/trait name unchanged
    • workload auto-updated with new definition
  • a new feature
    • current version is core.oam.dev/v{x}, new version is core.oam.dev/v{x+1}
    • component/trait name unchanged
    • workload auto-updated with new definition
  • a breaking change
    • component name updated e.g. from "mytraitv1" to "mytraitv2"
    • current version is core.oam.dev/v{x}, new version is core.oam.dev/v1beta1
    • new name and version specified explicitly in Application definition when developer chooses (and has consulted CHANGELOG for details on how to resolve breaking changes), and application redeployed

Thoughts @dhiguero @FogDong @wonderflow @linaking-guidewire ?

@wonderflow
Copy link
Collaborator

Yes, thanks @Kolossi for explanation, you fully get what I mean, while the app developer still has a chance to define the version through annotation by their own if they prefer.

@Kolossi
Copy link
Contributor Author

Kolossi commented Feb 1, 2024

Ah ok, so if I understand, your proposal to auto-update only applies if the developer has not specified a version number. In this case they would get patch and new feature changes automatically, but not breaking changes (because component name has to be changed in application definition)?

If so, there's logic in that but that's essentially what we have at the moment, and having to have seperate component names with major versions in them was one thing this proposal was trying to address.

However another key one was the ability of the definition author to determine the applicable version number as opposed to the auto-increment numbering provided at present. Whilst I'd prefer the semver approach (you got that, right? ;-) ) if we only added the ability for definition author to explicitly set the feature/patch version that would be of great benefit IMHO.

@wonderflow
Copy link
Collaborator

@Kolossi , yes, you have fully got my point. Just go ahead with semver.

@RohanMishra315
Copy link

Hey @Kolossi @wonderflow I would love to look into this issue. Can you tell me some prerequisites to tackles this.And where, we can discuss about it. Thanks !

@Beastharsh970
Copy link

Dear @FogDong I am looking forward to work on this issue during LFX mentorship program in your guidance and Support. Thank You.

@FogDong
Copy link
Member

FogDong commented Feb 4, 2024

@Kolossi Thanks for the detail explanation, I like the idea of using semver to distinguish the minor patch and break change.

For anyone who's interested in this issue for LFX, feel free to discuss with the community in Slack Kubevela channel and DM me in Slack, my id is FogDong.

Cc @RohanMishra315 @Beastharsh970

@oanasc
Copy link

oanasc commented Feb 8, 2024

Good proposal and the fact we can specify the versions as annotations in the application is very much needed.

In addition to this, I propose that we include a vela core configuration at the cluster level, which would specify the preferred versions for that specific cluster.

And versions would be used in this order of precedence:

  1. user specified
  2. cluster config
  3. latest

@Kolossi
Copy link
Contributor Author

Kolossi commented Feb 8, 2024

Thanks @oanasc, a good thing to raise.

My only thought is what behaviour would we get if the cluster config version is changed?

For user-changed version the update would only happen on redeploy, until then the previous Application, with the previous version number will be current.

For "latest", as at present I would expect it wouldn't auto-update as soon as a latest version is available (how to poll etc?), so again only on redeploy.

So that leaves the cluster config - if this is updated do we expect the cluster to redeploy everything? I would guess not, but then we end up with a situation where e.g. the workloads don't have a version configured, but when they were deployed it was v1.0.1, but since then the cluster config has been updated to v1.0.2 and then v1.0.3. So the only version specification in the cluster says v1.0.3 but the only deployed version is v1.0.1 which leads to confusion.

But I can't really think of an easy way around that with cluster config that doesn't auto-update. And as I said I'm not keen on auto-update! 🤔

Anyone got any ideas? 🙂

@oanasc
Copy link

oanasc commented Feb 8, 2024

Currently when you publish a new definition version it isn't automatically applied. A redeploy is needed in order to apply.
Now as part of this proposal you'd like to auto-upgrade minor & patch versions, should that be a flag which you can have on/off very much like cloud providers have their minor auto upgrade options, and the behaviour should then be consistent if is on is on regardless where the version is coming from?
I agree otherwise will be very confusing

@Kolossi
Copy link
Contributor Author

Kolossi commented Feb 8, 2024

Actually, I'm not proposing the auto-upgrade, but others have 🙂. I do see that others may want it, particularly in dev environments, so happy to go along provided it's configurable as you say.

But in the particular case of an update to the cluster config which you have proposed, how would you see that working?

@dhiguero dhiguero added type/enhancement New feature or request area/app-render labels Feb 8, 2024
@oanasc
Copy link

oanasc commented Feb 8, 2024

To elaborate and make sure there's clarity, I share some of the views that users shouldn't have to worry about the definition versions.
It could lead to a situation where teams have to coordinate and make updates across all repositories every time there's a definition update.

Additionally, I noticed we haven't considered how this approach will function in a multicluster setup; specifically, one in which a single definition is installed in the main cluster and subsequently used across all connected clusters. Under this scenario, if we upgrade a definition,it will instantaneously take effect across all clusters. The aim behind the definition upgrades is to initially test them in a separate environment; however, these annotation methods aren't topology sensitive.

Therefore, I proposed setting up a hierarchy of configuration precedence as follows:

  1. Start with the version defined by the user in the application.yaml annotations.
  2. If the first option isn't set, and you have a cluster-level configuration for versions, use these versions instead. This is optional
  3. Lastly, if neither the first nor the second options are set, then default to using the latest version.

And have the behaviour of auto-upgrade the same between for 1 and 2, and treat 2 as a central another way to set versions and choose to configure it or not on a cluster basis.

[Edit] I see your point @Kolossi now (realised as soon as I stepped away) auto upgrade from a cluster config could upgrade things that originally had a user version. Then what you suggested having no auto upgrade on cluster config could work.

@dhiguero
Copy link
Collaborator

dhiguero commented Feb 9, 2024

Following on yesterday's community meeting comments on this, one of the issues that needs to be designed is how the entities are going to be named in Kubernetes to avoid overwritting previous versions. One approach, as discussed, could be to assume that entities are going to be named in kubernetes with a version suffix, they will have versioning labels to facilitate retrieving them from the KubeVela operator, and the application will be abstracted from the versioning if not required. With this approach we will have the following scenario:

Listing components:

$ kubectl get componentdefinitions.core.oam.dev
NAME            WORKLOAD-KIND   DESCRIPTION
...
task              Job             Describes jobs that run code or a script to completion.
webservice        Deployment      Describes long-running, scalable, containerized services that have a stable network endpoint to receive external network traffic from customers.
worker            Deployment      Describes long-running, scalable, containerized services that running at backend. They do NOT have network endpoint to receive external network traffic.
my-comp-v1.0.0    Deployment      My custom component (v1.0.0)
my-comp-v1.3.0    Deployment      My custom component (v1.3.0)
my-comp-v2.1.0    Deployment      My custom component (v2.1.0)

Getting a component:

$ kubectl get componentdefinitions.core.oam.dev my-comp-v2.1.0
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
  annotations:
    definition.oam.dev/description: My custom component (v2.1)
  labels:
    custom.definition.oam.dev/ui-hidden: "true"
    definition.oam.dev/name: my-comp
    definition.oam.dev/major: 2
    definition.oam.dev/minor: 1
    definition.oam.dev/patch: 0
    definition.oam.dev/version: 2.1.0
  name: my-comp-v2.1.0
  namespace: vela-system
spec:
  schematic:
    cue:
    ...

To facilitate the adoption, we could modify also the vela def command so that labels are injected if not present:

$ vela def apply my-stateful.cue --version 1.2.3
ComponentDefinition my-stateful-v1.2.3 created in namespace vela-system with version 1.2.3.

@fboismenu
Copy link

fboismenu commented Feb 28, 2024

I like the component labels with semver 👍

Sorry I could not make for the community call, does the kubernetes naming convention changes the way it would be referenced from in the Application (meaning without version)?

From a user perspective in the Application, we have following scenarios, I would say:

  1. I want the latest version of the component (not sure if its very sensitive choice in prod, but I think it makes sense while developping) - so no version explicitly specified by end user.
  2. I want to specify constrains: pin the major or major.minor

As a platform eng, I would to be able to redeploy the users application, satisfying users contrains, but doing it as a progressive rollout, not a blunt auto update all and pray strategy, so it means being able to manipulate the patch and or minor version in accordance to 2.
The usual use case is I want to rollout a security update across the firm.

I personally think we'd better avoid deign the versioning feature to be too complex, it should be the platform builders capability to maintain the compatibility, or they can invent a new definition name. That means we shouldn't support fancy feature like "type: >wb-service@v1.2".

Absolutely 👍

@rootxrishabh
Copy link

Hey folks,I previously discussed the proposal on slack with @Kolossi and @FogDonga. After reading in more depth I have the following observations -

  1. I agree with semantic versioning(As CRD versioning could have issues like with version updates), also we could be with adding the version as a suffix in the end. (This could be discussed further as some people may not want version suffixing).
  2. Auto version updates make sense as @Kolossi said, we could have it as an optional feature.
  3. I agree with @dhiguero, OAM definitions would have versioning labels which the vela Operator can pick up.
  4. Keeping a vela core config at global level without auto-update without auto-update could get confusing. I think we should prioritize handling auto-updates more carefully as it has operational effects.
  5. Vela def can be modified to support labeling the Defs.

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

No branches or pull requests

10 participants