 Hello everyone, my name is Harsh and today I will be talking about CID versioning. A little bit about me, I am a developer Civo Cloud which is a blazingly fast-minus QNIT provider. I am also a maintainer at OpenEBS which is a CNCF Sandbox storage project. I am also a Hashacope ambassador and a real deal of playing chess. So getting right into it, CID versioning is a lot like any other API versioning conventions. So you typically do not want to add a required field in the same version or change the name and types of the existing fields because that would break the end user specs. And the reasons for wanting to change names and types of the fields are to simplify user inputs for better validations or simply because you want to evolve to the new API conventions. For example, Kubernetes sub-resource status has evolved from using phases to using conditions. There are times when you do not really need to operate as well and that is when you want to add optional fields as that would not break the end user specs and change validations. And by that I mean validations which do not break the existing defaults of the specs. So when it comes to CID versioning there are two views for it. As an end user we always export to the server version and server versions can be more than one whereas the stored version is what is really stored in HCD and it can only be one. So the API can read the stored version from HCD and represent it in multiple versions to us and we look at how it does that but essentially all you need to know is that stored version can always only be one. So when it comes to conversion strategy there are two of them. The first one is really a last mile change where you want to just bump up the API version and there are no schema changes in your CRDs. The second one is what we really think about when we think about conversion strategy and we look at how it works. So I have a little demo and in this demo I will have two versions V1 and V2 and in the beginning V1 would be the stored version and when user requests V2 view of the object what happens is the API reads the object from HCD and V1 it has a conversion web book which we have written and I will show that in a moment but the conversion web book would then throw back the object to API in V2 as it has some conversion functions and the API then returns back the output to the user. Now what I have done is I have scaffolded this project using cube builder, I even played along with the basic example of guest book all I have done is I have added this field full name in the V1 of the spec and in V2 of the spec I have added, I have spread the field into full name and last name, first name and last name. Other than that what I have done is I have added this cube builder marker which I realized I am on the wrong branch so let me switch up to V1 and I have added this cube builder marker which says that V1 is supposed to be the stored version. Other than that I have the conversion web book in place and we look at what this conversion package does in a moment but all I really want to show at this moment is the conversion functions where I read the fields, make it into one field and then assign the object metadata and that's typically how conversion would look like. And in case you have functions which lose data between versions you want to store them into the annotations of the field of the CR and that's because the versioning follows the concept of lossless conversion where you want the users to roll back to the older version as well because when we introduce a new version we set the old version as a stored version as that would give time to the users to upgrade to the new version and that's the reason we have assigned the V1 as a stored version whilst V2 is not and we look at that in CR and CRD and hope it will make a lot more sense than me just saying words. So if you look at this we have the V1 and it has served and storage as true and this is how you typically introduce a new version you set the older version to be the stored version and the newer version is just introduced and without adding any markers it by default it's set as the server version. So now I'm going to create a simple example. So as an end user I can create right now objects in V1 and V2 and we'll do that. So in the V1 I have a simple example, a dashboard sample and I'm giving it the older field of full name as it is the V1 spec and case alias for kubectl. So we've created this example now let's try to view it. So we can see that the object is written in V2 instead of V1 even though the stored version is V1 and that's because clients like kubectl always prefer the newer version, higher version. So that's the reason we got back the object in V2 but if we really want to look at how the object looks in V1 we can still do that. So now you can see the object in V1 and this really encourages clients to start using V2 as they see the object in V2 always and now we look at what I meant previously by what the conversion package previously did. So in the beginning when the conversion package wasn't there we had to write conversion functions between each of the API versions and what that meant is suppose we had three CRD versions V1, V2 and V3, we had to write conversion function for converting V1 to V2 and V3, V2 to V1 and V3 and V3 to V1 and V2 and that's Lexix conversion functions but with this hub and spoke model what we can do is we can have a centrally placed hub version and any conversion that needs to happen it needs to happen via this hub version. So in that previous example I spoke about if V2 wants to convert itself to V3 it will first have to convert itself to V1 and then to V3. So it only means that you have to write four conversion functions in this case because V1 needs to convert itself to V2 and V3 and V2 and V3 convert themselves to V1. So for the next part what we've just seen was the conversion happening from V1 to V2 but there are existing objects like in the one I've just created it was created in V1. So we need to bump that up to V2 as well as that's a CR not a CRD and it's analogous to classes and objects in object-oriented programming. So in order to do that there's this component known as storage version migrator which does that for us it probes the API discovery document every 10 minutes and looks at the new desired story version and bumps up the existing objects to that version or you can manually just patch all the objects and the specs or something and do it or have a custom program to do it. So I'm just going to do that right now I'm going to switch up to V2 and I'm going to install and in V2 what I've done is I've just set the new V2 as the stored version and I'll show you that. So as we can see V1 is still being served but now the story is set to false and V2 is storage version now and clients can now create objects and still they can create objects in V1 and V2 but in the end of the day objects inside HAD would be stored in V2 and to check that storage version migrator has the component known as CR known as storage version migration and we can take a look at that storage version migrations and if I can find this book in here or we can see that the status has succeeded. So this means that all the objects in that CD are now migrated to V2. So now that we have done that the next part what we'll be doing is since all the objects have been migrated to V2 and all the new objects will also be in V2 since that's the new stored version we can now deprecate the V1 and in order to do that what I've done is I've added this marker and so version which basically said served equals false and we'll look at the CRD as well and before I jump ahead I did forget to mention that this file is also scaffolded using QBuilder and in order to declare that V1 is the hub version I just had this one liner where I mentioned the type which is guestbook which is the V1 type of V1 of the object and hub is just a interface which we'll be using in order for the conversion package to work so that we can do something like this and now that we have installed let's look at our CRD now. Did I not installed it? Oh yeah, I have not installed it. Now let's view that. Yep, we can see that served is false and stored is false and served and storage are true. Now what this means is that we can no longer view our objects in V1 and I'll show that. So by default we again get V2 as always a higher version is preferred but when we try to view it in V1 it just doesn't work which makes sense because now we have replicated the version. So we have followed this whole process of deprecating a version where we introduced the newer version to be served first the older version is still the storage version and then we set the new version to be the storage version and in the end we stop serving the old version now we can also proceed and stop serving in the next release we can stop so we can remove it from the CRD itself and also drop the conversion with book support or if you choose to you can still keep it and clients wouldn't have to have this intermediate conversion when you try to do releases that's because of the hub and spoke model so they can directly convert from V1 to V3 instead of having to first upgrade to V2 and then to V3. I've provided all the references and a lot of the scaffolding I've I'll recommend that inside the GitHub repo and it's basically what I've followed from KubeBuilder docs other than that there's official documentation about CRD versioning in KubeBundit's docs and there's the GitHub repo for the demo and I've also linked the storage version migrator component and for non-KubeBuilder or pretty SDK projects which may not even use a controller runtime I have provided a sample or someone has already made of about how the conversion will work in this older projects you can reach out to me at Twitter, LinkedIn, Medium or my email and I'll be glad to answer any questions or discuss anything more. Thank you.