Skip to content

Commit

Permalink
Additional GEP cleanup, skeleton resource, conflict resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
robscott committed Jul 16, 2021
1 parent b0b7365 commit 553af6a
Showing 1 changed file with 124 additions and 20 deletions.
144 changes: 124 additions & 20 deletions site-src/geps/gep-713.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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

Expand All @@ -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
Expand All @@ -47,15 +57,15 @@ 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)

### Policy Attachment for Mesh
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)

Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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.
//
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 "<namespace>/<name>". 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
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit 553af6a

Please sign in to comment.