From 7d4e17a36561239893ad9617b611e894ddf49ea5 Mon Sep 17 00:00:00 2001 From: Rob Scott Date: Thu, 15 Jul 2021 18:10:58 -0700 Subject: [PATCH] Additional GEP cleanup, skeleton resource, conflict resolution --- site-src/geps/gep-713.md | 144 +++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 20 deletions(-) diff --git a/site-src/geps/gep-713.md b/site-src/geps/gep-713.md index ee739e3cc1..81eb31c6ed 100644 --- a/site-src/geps/gep-713.md +++ b/site-src/geps/gep-713.md @@ -1,12 +1,16 @@ # GEP-713: Policy Attachment -* Issue URL: https://github.com/kubernetes-sigs/gateway-api/issues/713 +* Issue: [#713](https://github.com/kubernetes-sigs/gateway-api/issues/713) * Status: Implementable ## TLDR This GEP aims to standardize policy attachment to resources associated with -Gateway API. This will be important for providing a consistent experience across +Gateway API by establishing a pattern which defines how `Policy` API types can +have their relevant effects applied to network traffic. Individual policy APIs +(e.g. `TimeoutPolicy`, `RetryPolicy`, etc) will include a common `TargetRef` +field in their specification to identify how and where to apply that policy. +This will be important for providing a consistent experience across implementations of the API, even for configuration details that may not be fully portable. @@ -16,13 +20,18 @@ portable. included in the Gateway API spec * Establish a pattern for policy attachment which should be used for any implementation specific policies used with Gateway API resources -* Provide a way to distinguish between required and default values +* Provide a way to distinguish between required and default values for all + policy API implementations * Enable policy attachment at all relevant scopes, including Gateways, Routes, - Backends, and maybe Namespaces -* Provide a means of attachment that works for both ingress and mesh uses of - this API -* Provide a consistent way that both included and implementation-specific - policies can be interpreted + Backends +* Ensure the policy attachment specification is generic and forward thinking + enough that it could be easily adapted to other grouping mechanisms like + Namespaces in the future +* Provide a means of attachment that works for both ingress and mesh + implementations of this API +* Provide a consistent specification that will ensure familiarity between both + included and implementation-specific policies so they can both be interpreted + the same way. ## Out of scope @@ -33,7 +42,8 @@ portable. This approach is building on concepts from all of the alternatives discussed below. This is very similar to the existing BackendPolicy resource in the API, -but also borrows some concepts from the ServicePolicy proposal. +but also borrows some concepts from the [ServicePolicy +proposal](https://github.com/kubernetes-sigs/gateway-api/issues/611). ### Policy Attachment for Ingress Attaching policy to Gateway resources for ingress use cases is relatively @@ -47,7 +57,7 @@ To build on that example, it’s possible to attach policies to more resources. Each policy applies to the referenced resource and everything below it in terms of hierarchy. Although this example is likely more complex than many real world use cases, it helps demonstrate how policy attachment can work across -namespaces. +namespaces. ![Complex Ingress Example](images/713-ingress-complex.png) @@ -55,7 +65,7 @@ namespaces. Although there is a great deal of overlap between ingress and mesh use cases, mesh enables more complex policy attachment scenarios. For example, you may want to apply policy to requests from a specific namespace to a backend in another -namespace. +namespace. ![Simple Mesh Example](images/713-mesh-simple.png) @@ -73,9 +83,19 @@ this case). ### Policy TargetRef API Each Policy resource should include a single `targetRef` field. It must not -target more than one resource at a time, but it can be used target larger +target more than one resource at a time, but it can be used to target larger resources such as Gateways or Namespaces that may apply to multiple child -resources. This field should have the following structure: +resources. + +As with most APIs, there are countless ways we could choose to expand this in +the future. This includes supporting multiple targetRefs and/or label selectors. +Although this would enable compelling functionality, it would increase the +complexity of an already complex API and potentially result in more conflicts +between policies. Although we may choose to expand the targeting capabilities +in the future, at this point it is strongly preferred to start with a simpler +pattern that still leaves room for future expansion. + +The `targetRef` field should have the following structure: ```go // PolicyTargetReference identifies an API object to apply policy to. @@ -104,7 +124,7 @@ type PolicyTargetReference struct { // +kubebuilder:validation:MaxLength=253 Name string `json:"name"` - // SectionName is the name of a section within the target resource. When + // SectionName is the name of a section within the target resource. When // unspecified, this targets the entire resource. In the following // resources, SectionName is interpreted as the following: // * Gateway: Listener Name @@ -120,7 +140,7 @@ type PolicyTargetReference struct { // Namespace is the namespace of the referent. When unspecified, the local // namespace is inferred. Even when policy targets a resource in a different - // namespace, it may only apply to traffic originating from the same + // namespace, it may only apply to traffic originating from the same // namespace as the policy. // // Support: Extended @@ -129,7 +149,7 @@ type PolicyTargetReference struct { // +kubebuilder:validation:MaxLength=253 // +optional Namespace string `json:"namespace,omitempty"` - + // ClassName is the name of the class this policy should apply to. When // unspecified, the policy will apply to all classes that support it. // @@ -142,6 +162,64 @@ type PolicyTargetReference struct { } ``` +### Sample Policy API +The following structure can be used as a starting point for any Policy resource +using this API pattern. Note that the PolicyTargetReference struct defined above +will be distributed as part of the Gateway API. + +```go +// ACMEServicePolicy provides a way to apply Service policy configuration with +// the ACME implementation of the Gateway API. +type ACMEServicePolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of ACMEServicePolicy. + Spec ACMEServicePolicySpec `json:"spec"` + + // Status defines the current state of ACMEServicePolicy. + // +kubebuilder:default={conditions: {{type: "Applied", status: "False", reason:"NotReconciled", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}} + Status ACMEServicePolicyStatus `json:"status,omitempty"` +} + +// ACMEServicePolicySpec defines the desired state of ACMEServicePolicy. +type ACMEServicePolicySpec struct { + // TargetRef identifies an API object to apply policy to. + TargetRef gatewayv1a2.PolicyTargetReference `json:"targetRef"` + + // Override defines policy configuration that should override policy + // configuration attached below the targeted resource in the hierarchy. + // +optional + Override *ACMEPolicyConfig `json:"override,omitempty"` + + // Default defines default policy configuration for the targeted resource. + // +optional + Default *ACMEPolicyConfig `json:"default,omitempty"` +} + +// ACMEPolicyConfig contains ACME policy configuration. +type ACMEPolicyConfig struct { + // Add configurable policy here +} + +// ACMEServicePolicyStatus defines the observed state of ACMEServicePolicy. +type ACMEServicePolicyStatus struct { + // Conditions describe the current conditions of the ACMEServicePolicy. + // + // Known condition types are: + // + // * "Applied" + // * "Conflict" + // + // +optional + // +listType=map + // +listMapKey=type + // +kubebuilder:validation:MaxItems=8 + // +kubebuilder:default={{type: "Applied", status: "False", reason:"NotReconciled", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}} + Conditions []metav1.Condition `json:"conditions,omitempty"` +} +``` + ### Hierarchy Each policy may include default or override values. Default values are given precedence from the bottom up, while override values are top down. That means @@ -157,9 +235,13 @@ hierarchy would be expanded to this: A override > B override > C override > C default > B default > A default. -Given the hierarchy above, that means that policies attached to GatewayClass -would have the highest precedence for overrides and lowest precedence for -defaults. +Note that the hierarchy is reversed for defaults. The rationale here is that +overrides usually need to be enforced top down while defaults should apply to +the lowest resource first. For example, if an admin needs to attach required +policy, they can attach it as an override to a Gateway. That would have +precedence over Routes and Services below it. On the other hand, an app owner +may want to set a default timeout for their Service. That would have precedence +over defaults attached at higher levels such as Route or Gateway. Each policy resource would include 2 structs within the spec. One with override values and the other with default values. In the following example, the policy @@ -315,6 +397,28 @@ type RouteRule struct { } ``` +### Conflict Resolution +It is possible for multiple policies to target the same resource. When this +happens, merging is the preferred outcome. If multiple policy resources target +the same resource _and_ have an identical field specified with different values, +precedence MUST be determined in order of the following criteria, continuing on +ties: + +* The most specific targetRef. If one targetRef includes a sectionName, that + should be given precedence for the specified section over a targetRef that + does not include one. +* The oldest Policy based on creation timestamp. For example, a Policy with a + creation timestamp of "2021-07-15 01:02:03" is given precedence over a Policy + with a creation timestamp of "2021-07-15 01:02:04". +* The Policy appearing first in alphabetical order by "/". For + example, foo/bar is given precedence over foo/baz. + +When a conflict occurs, implementations MUST set a "Conflict" condition in the +`status.conditions` list for all affected policy resources. + +For a better user experience, a validating webhook can be implemented to prevent +these kinds of conflicts all together. + ### Kubectl Plugin To help improve UX and standardization, a kubectl plugin will be developed that will be capable of describing the computed sum of policy that applies to a given @@ -381,7 +485,7 @@ controller implementation: pattern * Easy to attach policy to resources we don’t control (Service, ServiceImport, etc) -* Minimal API changes required (just expansion of status) +* Minimal API changes required * Simplifies packaging an application for deployment as policy references do not need to be part of the templating