ORC API Contracts
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.
Resource-specific conventions
After scaffolding, each resource will require 3 custom structs:
<ResourceName>Filter
<ResourceName>ResourceSpec
<ResourceName>ResourceStatus
where Custom
is the name of the specific resource.
These structs, along with any supporting resource-specific code, should be defined in api/v1alpha1/<resourcename>_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 (spec.managementPolicy == 'managed'
).
- Where relevant, the ResourceSpec should include a
name
field 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
set
ormap
where possible, butatomic
lists 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 ResourceStatus fields should not be validated: we should store any value returned by OpenStack, even invalid ones.
- This requires implementing separate
Spec
andStatus
variants of structs.
- This requires implementing separate
- Lists should be
atomic
. - All fields should be optional and have the
omitempty
tag. - 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 CRDs
The CRDs are automatically generated from the Go API. To generate them, run:
$ make generate
However, note that this will not add a new CRD to the list of CRDs to load. This must be done manually by adding the new CRD to the list in config/crd/kustomization.yaml
.
This will also generate all other artifacts, including the apply configurations in pkg/clients/applyconfiguration
.
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.
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
Every time you make a change to the API you should ensure these are up to date by running:
$ make generate
General API guidelines
- Do not define API fields which are not implemented. We will define them when we implement them.
- All strings must have a maximum length, even if we have to guess.
- All lists must have a maximum length, even if we have to guess.
- 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,
int32
orint64
, instead ofint
. - Do not use unsigned integers: use
intN
with a kubebuilder marker validating for a minimum of 0. - Optional fields should have the
omitempty
tag. - 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?