API Design
Kubernetes API conventions
In general we try to follow the Kubernetes API conventions. Note that these API conventions are written for Kubernetes itself. As ORC is not part of Kubernetes we are not 'bound' by them, and have occasionally chosen to deviate from them. However, we treat these conventions as the distilled experience of the authors of the API itself, so we don't deviate from them lightly.
ORC uses KAL, the Kube API Linter, to enforce most of the conventions.
Resource-specific conventions
Each resource requires three custom structs:
These structs, along with any supporting resource-specific code, should be defined in api/v1alpha1/<kind>_types.go.
Filter
This is located at spec.import.filter in the base object. It is used when importing a pre-existing OpenStack resource into ORC when the resource's ID is not already known.
- Filter must not contain an ID field. This is handled separately by
spec.import.id. - Where an equivalent filter exists in Cluster API Provider OpenStack, consider copying it where possible as these filters are already widely used.
- Neutron types should include FilterByNeutronTags inline.
ResourceSpec
This is located at spec.resource in the base object. It is only defined for managed objects.
- Where relevant, the
ResourceSpecshould include anamefield to allow object name to be overridden. - All fields should use pre-defined validated types where possible, e.g.
OpenStackName,NeutronDescription,IPvAny. - Lists should have type
setormapwhere possible, butatomiclists may be necessary where a struct has no merge key.
ResourceStatus
This is located at status.resource in the base object. It contains the observed state of the OpenStack object.
- ID of the resource must not be included. It is stored separately at
status.ID. - The content of
ResourceStatusfields should not be validated: we should store any value returned by OpenStack, even invalid ones.- This requires implementing separate
SpecandStatusvariants of structs.
- This requires implementing separate
- Lists should be
atomic. - All fields should be optional and have the
omitemptytag. - strings do not need to be pointers.
Note that although we don't validate the content of status fields, we must still add maximum length validation for strings and lists. This both defensively constrains the size of the object in the etcd database, and limits the maximum size of the object computed by kube-apiserver for the purposes of CEL validation complexity. Strings should ideally be constrained to the length of the field on the source service's database, or failing that some value in the order of kilobytes large enough to be improbable to occur in practice. Lists should similarly be constrained to a value large enough to be improbable to occur in practice.
Generating API artifacts
There are a number of artifacts which are automatically generated from the API. Of primary interest are:
- The CRDs
- The apply configurations in
pkg/clients/applyconfiguration - Reference documentation for the website
Every time you make a change to the API you should ensure these are up to date by running:
make generate
Kustomize references
You should update examples/components/kustomizeconfig/kustomizeconfig.yaml with any references defined by your API. This will ensure that kustomize handles them correctly during transformations.
Note
kustomizeconfig.yaml is currently used only by the examples, but we intend to eventually publish it as a release artifact.
General API guidelines
- Do not define API fields which are not implemented. We will define them when we implement them.
- As a consequence, it's perfectly fine to omit the optional fields for the initial implementation of a controller.
- All strings must have a maximum length, even if we have to guess. It should be the length of the type in the related OpenStack service database.
- All lists must have a maximum length, even if we have to guess. It should be large enough that we don't hit the limit in practice.
- Limits for fields in the
ResourceSpecshould never be higher the one for the same field in theResourceStatus. - Constants coming from OpenStack should be copied verbatim.
- All fields should have a godoc comment, beginning with the json name of the field.
- Use sized integers,
int32orint64, instead ofint. - Do not use unsigned integers: use
intNwith a kubebuilder marker validating for a minimum of 0. - Optional fields should have the
omitemptytag. - Optional fields should be pointers, unless their zero-value is also the OpenStack default, or we can be very confident that we will never need to distinguish between empty and unset values. e.g. Will we ever want to set a value explicitly to the empty string?