diff --git a/docs/devel/api-conventions.md b/docs/devel/api-conventions.md index c5fda4bb5bd..af02d3dbaf9 100644 --- a/docs/devel/api-conventions.md +++ b/docs/devel/api-conventions.md @@ -53,6 +53,7 @@ using resources with kubectl can be found in [Working with resources](../user-gu - [Lists of named subobjects preferred over maps](#lists-of-named-subobjects-preferred-over-maps) - [Primitive types](#primitive-types) - [Constants](#constants) + - [Unions](#unions) - [Lists and Simple kinds](#lists-and-simple-kinds) - [Differing Representations](#differing-representations) - [Verbs on Resources](#verbs-on-resources) @@ -263,6 +264,15 @@ This rule maintains the invariant that all JSON/YAML keys are fields in API obje Some fields will have a list of allowed values (enumerations). These values will be strings, and they will be in CamelCase, with an initial uppercase letter. Examples: "ClusterFirst", "Pending", "ClientIP". +#### Unions + +Sometimes, at most one of a set of fields can be set. For example, the [volumes] field of a PodSpec has 17 different volume type-specific +fields, such as `nfs` and `iscsi`. All fields in the set should be [Optional](#optional-vs-required). + +Sometimes, when a new type is created, the api designer may anticipate that a union will be needed in the future, even if only one field is +allowed initially. In this case, be sure to make the field [Optional](#optional-vs-required) optional. In the validation, you may +still return an error if the sole field is unset. Do not set a default value for that field. + ### Lists and Simple kinds Every list or simple kind SHOULD have the following metadata in a nested object field called "metadata": diff --git a/docs/devel/api_changes.md b/docs/devel/api_changes.md index 2fe8a5af8cd..0c039aab187 100644 --- a/docs/devel/api_changes.md +++ b/docs/devel/api_changes.md @@ -32,6 +32,38 @@ Documentation for other releases can be found at +*This document is oriented at developers who want to change existing APIs. +A set of API conventions, which applies to new APIs and to changes, can be +found at [API Conventions](api-conventions.md). + +**Table of Contents** + + +- [So you want to change the API?](#so-you-want-to-change-the-api) + - [Operational overview](#operational-overview) + - [On compatibility](#on-compatibility) + - [Incompatible API changes](#incompatible-api-changes) + - [Changing versioned APIs](#changing-versioned-apis) + - [Edit types.go](#edit-typesgo) + - [Edit defaults.go](#edit-defaultsgo) + - [Edit conversion.go](#edit-conversiongo) + - [Changing the internal structures](#changing-the-internal-structures) + - [Edit types.go](#edit-typesgo) + - [Edit validation.go](#edit-validationgo) + - [Edit version conversions](#edit-version-conversions) + - [Edit deep copy files](#edit-deep-copy-files) + - [Edit json (un)marshaling code](#edit-json-unmarshaling-code) + - [Making a new API Group](#making-a-new-api-group) + - [Update the fuzzer](#update-the-fuzzer) + - [Update the semantic comparisons](#update-the-semantic-comparisons) + - [Implement your change](#implement-your-change) + - [Write end-to-end tests](#write-end-to-end-tests) + - [Examples and docs](#examples-and-docs) + - [Alpha, Beta, and Stable Versions](#alpha-beta-and-stable-versions) + - [Adding Unstable Features to Stable Versions](#adding-unstable-features-to-stable-versions) + + + # So you want to change the API? Before attempting a change to the API, you should familiarize yourself @@ -273,6 +305,11 @@ enumerated set *can* be a compatible change, if handled properly (treat the removed value as deprecated but allowed). This is actually a special case of a new representation, discussed above. +For [Unions](api-conventions.md), sets of fields where at most one should be set, +it is acceptible to add a new option to the union if the [appropriate conventions] +were followed in the original object. Removing an option requires following +the deprecation process. + ## Incompatible API changes There are times when this might be OK, but mostly we want changes that @@ -549,10 +586,6 @@ hack/update-swagger-spec.sh The API spec changes should be in a commit separate from your other changes. -## Adding new REST objects - -TODO(smarterclayton): write this. - ## Alpha, Beta, and Stable Versions New feature development proceeds through a series of stages of increasing maturity: @@ -617,6 +650,82 @@ New feature development proceeds through a series of stages of increasing maturi - Support: API version will continue to be present for many subsequent software releases; - Recommended Use Cases: any +### Adding Unstable Features to Stable Versions + +When adding a feature to an object which is already Stable, the new fields and new behaviors +need to meet the Stable level requirements. If these cannot be met, then the new +field cannot be added to the object. + +For example, consider the following object: + +```go +// API v6. +type Frobber struct { + Height int `json:"height"` + Param string `json:"param"` +} +``` + +A developer is considering adding a new `Width` parameter, like this: + +```go +// API v6. +type Frobber struct { + Height int `json:"height"` + Width int `json:"height"` + Param string `json:"param"` +} +``` + +However, the new feature is not stable enough to be used in a stable version (`v6`). +Some reasons for this might include: + +- the final representation is undecided (e.g. should it be called `Width` or `Breadth`?) +- the implementation is not stable enough for general use (e.g. the `Area()` routine sometimes overflows.) + +The developer cannot add the new field until stability is met. However, sometimes stability +cannot be met until some users try the new feature, and some users are only able or willing +to accept a released version of Kubernetes. In that case, the developer has a few options, +both of which require staging work over several releases. + + +A preferred option is to first make a release where the new value (`Width` in this example) +is specified via an annotation, like this: + +```go +kind: frobber +version: v6 +metadata: + name: myfrobber + annotations: + frobbing.alpha.kubernetes.io/width: 2 +height: 4 +param: "green and blue" +``` + +This format allows users to specify the new field, but makes it clear +that they are using a Alpha feature when they do, since the word `alpha` +is in the annotation key. + +Another option is to introduce a new type with an new `alpha` or `beta` version +designator, like this: + +``` +// API v6alpha2 +type Frobber struct { + Height int `json:"height"` + Width int `json:"height"` + Param string `json:"param"` +} +``` + +The latter requires that all objects in the same API group as `Frobber` to be replicated in +the new version, `v6alpha2`. This also requires user to use a new client which uses the +other version. Therefore, this is not a preferred option. + +A releated issue is how a cluster manager can roll back from a new version +with a new feature, that is already being used by users. See https://github.com/kubernetes/kubernetes/issues/4855. + [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/devel/api_changes.md?pixel)]()