From b1f8f5975cd7eb7084ef9109632ed68e9d247c94 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Wed, 28 Apr 2021 11:11:11 -0700 Subject: clarifying schema for multi-kind object references There are a few types of patterns for multi-kind object references that are currently included in the Kubernetes core resources (EnvVarSource, ObjectReference). Adding a section to clarify the preferred pattern for future resources. --- .../devel/sig-architecture/api-conventions.md | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index 26a456dd..013c0c66 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -900,8 +900,34 @@ clearly described and the permissions issues should be resolved. This could be done with a double opt-in (an opt-in from both the referrer and the refer-ee) or with secondary permissions checks performed in admission. -TODO: Plugins, extensions, nested kinds, headers +TODO: Plugins, extensions, headers +### Handling references to multiple kinds + +References which can refer to multiple kinds should use a single field, and select the target kind via `apiVersion` and `kind` fields. + +For example, if one can retrieving a referenced value from a `ConfigMap` or a `Secret` kind, the schema for the reference should be of the form: + +```yaml +# preferred pattern +valueFrom: + kind: Secret # alternatively ConfigMap + name: resource-name + optional: true +``` + +Rather than: + +```yaml +# discouraged pattern +valueFrom: + configMapRef: + name: resource-name + optional: true + secretRef: + name: resource-name + optional: true +``` ## HTTP Status codes -- cgit v1.2.3 From e5fb87eafac262bd2b081786c5c4092cca02e74d Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Tue, 11 May 2021 22:23:51 -0700 Subject: addressing comments - switched from kind to resource. - extended examples to four kinds of object references. - clarified use of JSON Pointer to determine the path to the field. --- .../devel/sig-architecture/api-conventions.md | 92 ++++++++++++++++------ 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index 013c0c66..859b4e2d 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -874,11 +874,6 @@ Examples: ## Object references -Object references should either be called `fooName` if referring to an object of -kind `Foo` by just the name (within the current namespace, if a namespaced -resource), or should be called `fooRef`, and should contain a subset of the -fields of the `ObjectReference` type. - Object references on a namespaced type should usually refer only to objects in the same namespace. Because namespaces are a security boundary, cross namespace references can have unexpected impacts, including: @@ -900,35 +895,86 @@ clearly described and the permissions issues should be resolved. This could be done with a double opt-in (an opt-in from both the referrer and the refer-ee) or with secondary permissions checks performed in admission. -TODO: Plugins, extensions, headers +### References to a single resource type + +A single kind object reference is straightforward in that the controller can hard-code most qualifiers needed to identify the object. As such as the only value needed to be provided is the name (and namespace, although cross-namespace references are discouraged): + +```yaml +# for a single resource, the suffix should be Ref, with the field name +# providing an indication as to the resource type referenced. +secretRef: + name: foo + # namespace would generally not be needed and is discouraged, + # as explained above. + namespace: foo-namespace +``` + +#### Controller behavior + +The operator is expected to know the version, group, and resource name of the object it needs to retrieve the value from, and can use the discovery client or construct the API path directly. + +### References which can point to multiple resource types + +Multi-kind object references are used when there is a bounded set of valid resource types that a reference can point to. + +As with a single-kind object reference, the operator can supply missing fields, provided that the fields that are present are sufficient to uniquely identify the object resource type among the set of supported types. + +```yaml +# guidance for the field name is the same as a single resource. +fooRef: + group: sns.services.k8s.aws + resource: topics + name: foo + namespace: foo-namespace +``` + +Although not always necessary to help a controller identify a resource type, “group” is included to avoid ambiguity when the resource exists in multiple groups. It also provides clarity to end users and enables copy-pasting of a reference without the referenced type changing due to a different controller handling the reference. + +#### Controller behavior + +The operator can store a map of (group,resource) to the version of that resource it desires. From there, it can construct the full path to the resource, and retrieve the object. -### Handling references to multiple kinds +### Generic object reference -References which can refer to multiple kinds should use a single field, and select the target kind via `apiVersion` and `kind` fields. +A generic object reference is used when the desire is to provide a pointer to some object to simplify discovery for the user. For example, this could be used to reference a target object for a `core.v1.Event` that occurred. -For example, if one can retrieving a referenced value from a `ConfigMap` or a `Secret` kind, the schema for the reference should be of the form: +With a generic object reference, it is not possible to extract any information about the referenced object aside from what is standard (e.g. ObjectMeta). Since any standard fields exist in any version of a resource, it is possible to not include version in this case: ```yaml -# preferred pattern -valueFrom: - kind: Secret # alternatively ConfigMap - name: resource-name - optional: true +fooObjectRef: + group: operator.openshift.io + resource: OpenShiftAPIServer + name: cluster + # namespace is unset if the resource is cluster-scoped, or lives in the + # same namespace as the referrer. ``` -Rather than: +#### Controller behavior + +The operator would be expected to find the resource via the discovery client (as the version is not supplied). + +### Field reference + +A field reference is used when the desire is to extract a value from a specific field in a referenced object. + +Field references differ from other reference types, as the operator has no knowledge of the object prior to the reference. Since the schema of an object can differ for different versions of a resource, this means that a “version” is required for this type of reference. ```yaml -# discouraged pattern -valueFrom: - configMapRef: - name: resource-name - optional: true - secretRef: - name: resource-name - optional: true +fooFieldRef: + version: v1 # version of the resource + # group is elided in the ConfigMap example, since it has a blank group in the OpenAPI spec. + resource: configmaps + fieldPath: data/foo ``` +The fieldPath should point to a single value, and use JSON Pointer syntax to specify the desired field, from the root of the object. + +#### Controller behavior + +In this scenario, all required attributes are required, so the operator may query the API directly. + +TODO: Plugins, extensions, headers + ## HTTP Status codes The server will respond with HTTP status codes that match the HTTP spec. See the -- cgit v1.2.3 From fed5c1e6a61e65ceabc7a9ffa0829eceeb51f275 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Wed, 19 May 2021 10:31:56 -0700 Subject: Addressing feedback - switching fieldpath syntax to reference best practices already in the spec - clarifying naming of the object reference field - fixing a poor example with a kind used where a resource value should have been. --- .../devel/sig-architecture/api-conventions.md | 43 ++++++++++++++++------ 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index 859b4e2d..143ccd61 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -895,7 +895,28 @@ clearly described and the permissions issues should be resolved. This could be done with a double opt-in (an opt-in from both the referrer and the refer-ee) or with secondary permissions checks performed in admission. -### References to a single resource type +### Naming of the reference field + +The name of the reference field should be of the format "{field}Ref", with "Ref" always included in the suffix. + +The "{field}" component should be named to indicate the purpose of the reference. For example, "targetRef" in an +endpoint indicates that the object reference specifies the target. + +It is okay to have the "{field}" component indicate the resource type. For example, "secretRef" when referencing +a secret. However, this comes with the risk off the field being a misnomer in the case that the field is expanded to +reference more than one type. + +### Object References Examples + +The following sections illustrate recommended schemas for various object references scenarios. + +The schemas outlined below are designed to enable purely additive fields as the types of referencable +objects expand, and therefore are backwards compatible. + +For example, it is possible to go from a single resource type to multiple resource types without +a breaking change in the schema. + +#### References to a single resource type A single kind object reference is straightforward in that the controller can hard-code most qualifiers needed to identify the object. As such as the only value needed to be provided is the name (and namespace, although cross-namespace references are discouraged): @@ -909,11 +930,11 @@ secretRef: namespace: foo-namespace ``` -#### Controller behavior +##### Controller behavior The operator is expected to know the version, group, and resource name of the object it needs to retrieve the value from, and can use the discovery client or construct the API path directly. -### References which can point to multiple resource types +#### References which can point to multiple resource types Multi-kind object references are used when there is a bounded set of valid resource types that a reference can point to. @@ -930,11 +951,11 @@ fooRef: Although not always necessary to help a controller identify a resource type, “group” is included to avoid ambiguity when the resource exists in multiple groups. It also provides clarity to end users and enables copy-pasting of a reference without the referenced type changing due to a different controller handling the reference. -#### Controller behavior +##### Controller behavior The operator can store a map of (group,resource) to the version of that resource it desires. From there, it can construct the full path to the resource, and retrieve the object. -### Generic object reference +#### Generic object reference A generic object reference is used when the desire is to provide a pointer to some object to simplify discovery for the user. For example, this could be used to reference a target object for a `core.v1.Event` that occurred. @@ -943,17 +964,17 @@ With a generic object reference, it is not possible to extract any information a ```yaml fooObjectRef: group: operator.openshift.io - resource: OpenShiftAPIServer + resource: openshiftapiservers name: cluster # namespace is unset if the resource is cluster-scoped, or lives in the # same namespace as the referrer. ``` -#### Controller behavior +##### Controller behavior The operator would be expected to find the resource via the discovery client (as the version is not supplied). -### Field reference +#### Field reference A field reference is used when the desire is to extract a value from a specific field in a referenced object. @@ -964,12 +985,12 @@ fooFieldRef: version: v1 # version of the resource # group is elided in the ConfigMap example, since it has a blank group in the OpenAPI spec. resource: configmaps - fieldPath: data/foo + fieldPath: data.foo ``` -The fieldPath should point to a single value, and use JSON Pointer syntax to specify the desired field, from the root of the object. +The fieldPath should point to a single value, and use [the recommended field selector notation](#selecting-fields) to denote the field path. -#### Controller behavior +##### Controller behavior In this scenario, all required attributes are required, so the operator may query the API directly. -- cgit v1.2.3 From 9b694b1b7908489d1064d2740d583bdac4d491c7 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Fri, 21 May 2021 21:05:39 -0700 Subject: Update contributors/devel/sig-architecture/api-conventions.md Co-authored-by: Daniel Smith --- contributors/devel/sig-architecture/api-conventions.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index 143ccd61..4c02eeff 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -903,7 +903,7 @@ The "{field}" component should be named to indicate the purpose of the reference endpoint indicates that the object reference specifies the target. It is okay to have the "{field}" component indicate the resource type. For example, "secretRef" when referencing -a secret. However, this comes with the risk off the field being a misnomer in the case that the field is expanded to +a secret. However, this comes with the risk of the field being a misnomer in the case that the field is expanded to reference more than one type. ### Object References Examples @@ -1549,4 +1549,3 @@ Example: "must be greater than \`request\`". be less than 256", "must be greater than or equal to 0". Do not use words like "larger than", "bigger than", "more than", "higher than", etc. * When specifying numeric ranges, use inclusive ranges when possible. - -- cgit v1.2.3 From bda0f5062ea22eac28e53efc4d76723597c3ce02 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Mon, 24 May 2021 21:49:51 -0700 Subject: Addressing feeedback - new section around resources with different versions - called out edge cases around resource not found - removed very old todo --- .../devel/sig-architecture/api-conventions.md | 30 +++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index 4c02eeff..a319c237 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -906,6 +906,24 @@ It is okay to have the "{field}" component indicate the resource type. For examp a secret. However, this comes with the risk of the field being a misnomer in the case that the field is expanded to reference more than one type. +### Referencing resources with multiple versions + +Most resources will have multiple versions. For example, core resources +will undergo version changes as it transitions from alpha to GA. + +Controllers should assume that a version of a resource may change, and include appropriate error handling. + +### Handling of resources that do not exist + +There are multiple scenarios where a desired resource may not exist. Examples include: + +- the desired version of the resource does not exist. +- race condition in the bootstrapping of a cluster resulting a resource not yet added. +- user error. + +Controllers should be authored with the assumption that the referenced resource may not exist, and include +error handling to make the issue clear to the user. + ### Object References Examples The following sections illustrate recommended schemas for various object references scenarios. @@ -955,6 +973,9 @@ Although not always necessary to help a controller identify a resource type, “ The operator can store a map of (group,resource) to the version of that resource it desires. From there, it can construct the full path to the resource, and retrieve the object. +It is also possible to have the controller choose a version that it finds via the discovery client. However, as schemas can vary across different versions +of a resource, the controller must also handle these differences. + #### Generic object reference A generic object reference is used when the desire is to provide a pointer to some object to simplify discovery for the user. For example, this could be used to reference a target object for a `core.v1.Event` that occurred. @@ -972,7 +993,7 @@ fooObjectRef: ##### Controller behavior -The operator would be expected to find the resource via the discovery client (as the version is not supplied). +The operator would be expected to find the resource via the discovery client (as the version is not supplied). As any retrievable field would be common to all objects, any version of the resource should do. #### Field reference @@ -992,9 +1013,12 @@ The fieldPath should point to a single value, and use [the recommended field sel ##### Controller behavior -In this scenario, all required attributes are required, so the operator may query the API directly. +In this scenario, the user will supply all of the required path elements: group, version, resource, name, and possibly namespace. +As such, the controller can construct the API prefix and query it without the use of the discovery client: -TODO: Plugins, extensions, headers +``` +/apis/{group}/{version}/{resource}/ +``` ## HTTP Status codes -- cgit v1.2.3 From ac7999cb51bd93f9af1f21e85291336a2ae33bed Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Tue, 25 May 2021 22:32:35 -0700 Subject: Addressing comments including blurb recommending using multi resource object references if there is a chance it will be used that way. --- contributors/devel/sig-architecture/api-conventions.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contributors/devel/sig-architecture/api-conventions.md b/contributors/devel/sig-architecture/api-conventions.md index a319c237..5c4c4a21 100644 --- a/contributors/devel/sig-architecture/api-conventions.md +++ b/contributors/devel/sig-architecture/api-conventions.md @@ -934,7 +934,7 @@ objects expand, and therefore are backwards compatible. For example, it is possible to go from a single resource type to multiple resource types without a breaking change in the schema. -#### References to a single resource type +#### Single resource reference A single kind object reference is straightforward in that the controller can hard-code most qualifiers needed to identify the object. As such as the only value needed to be provided is the name (and namespace, although cross-namespace references are discouraged): @@ -948,11 +948,14 @@ secretRef: namespace: foo-namespace ``` +This schema should only be used when the intention is to always have the reference only be to a single resource. +If extending to multiple resource types is possible, use the [multiple resource reference](#multiple-resource-reference). + ##### Controller behavior The operator is expected to know the version, group, and resource name of the object it needs to retrieve the value from, and can use the discovery client or construct the API path directly. -#### References which can point to multiple resource types +#### Multiple resource reference Multi-kind object references are used when there is a bounded set of valid resource types that a reference can point to. -- cgit v1.2.3