Skip to content

Commit

Permalink
Add conditions to HTTPProxy and TLSCertificateDelegation (#2706)
Browse files Browse the repository at this point in the history
Updates #2495.

This adds a new `DetailedCondition` type to HTTPProxy, that allows for sub-conditions.

This allows Contour to have a `Valid` condition, that has further information about
how that conditions `status` has been reached, or in other words, to expose more
than one error or warning about a HTTPProxy to the user.

Also adds the Conditions block to TLSCertificateDelegation.

Signed-off-by: Nick Young <ynick@vmware.com>
  • Loading branch information
youngnick committed Jul 23, 2020
1 parent 5c79158 commit cc3076a
Show file tree
Hide file tree
Showing 9 changed files with 1,568 additions and 26 deletions.
104 changes: 104 additions & 0 deletions apis/projectcontour/v1/detailedconditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright Project Contour Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +k8s:deepcopy-gen=package

// Package v1 is the v1 version of the API.
// +groupName=projectcontour.io
package v1

// SubCondition is a Condition-like type intended for use as a subcondition inside a DetailedCondition.
//
// It contains a subset of the Condition fields.
//
// It is intended for warnings and errors, so `type` names should use abnormal-true polarity,
// that is, they should be of the form "ErrorPresent: true".
//
// The expected lifecycle for these errors is that they should only be present when the error or warning is,
// and should be removed when they are not relevant.
type SubCondition struct {
// Type of condition in `CamelCase` or in `foo.example.com/CamelCase`.
//
// This must be in abnormal-true polarity, that is, `ErrorFound` or `controller.io/ErrorFound`.
//
// The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type" protobuf:"bytes,1,opt,name=type"`
// Status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status"`
// Reason contains a programmatic identifier indicating the reason for the condition's last transition.
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
//
// The value should be a CamelCase string.
//
// This field may not be empty.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
Reason string `json:"reason" protobuf:"bytes,3,opt,name=reason"`
// Message is a human readable message indicating details about the transition.
//
// This may be an empty string.
//
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message" protobuf:"bytes,4,opt,name=message"`
}

// TODO(youngnick): Replace the inlined Condition with metav1.Condition once we have moved to a client-go
// version that includes it. Also includes deleting kubeconditions.go.

// DetailedCondition is an extension of the normal Kubernetes conditions, with two extra
// fields to hold sub-conditions, which provide more detailed reasons for the state (True or False)
// of the condition.
//
// `errors` holds information about sub-conditions which are fatal to that condition and render its state False.
//
// `warnings` holds information about sub-conditions which are not fatal to that condition and do not force the state to be False.
//
// Remember that Conditions have a type, a status, and a reason.
//
// The type is the type of the condition, the most important one in this CRD set is `Valid`.
//
// In the case of `Valid`, `status: true` means that the object is has been ingested into Contour with no errors.
// `warnings` may still be present, and will be indicated in the Reason field.
//
// `Valid`, `status: false` means that the object has had one or more fatal errors during processing into Contour.
// The details of the errors will be present under the `errors` field.
//
// There should never be subconditions under `errors` when `status` is `true`.
type DetailedCondition struct {
Condition `json:",inline"`
// Errors contains a slice of relevant error subconditions for this object.
//
// Subconditions are expected to appear when relevant (when there is a error), and disappear when not relevant.
// An empty slice here indicates no errors.
// +optional
Errors []SubCondition `json:"errors,omitempty"`
// Warnings contains a slice of relevant warning subconditions for this object.
//
// Subconditions are expected to appear when relevant (when there is a warning), and disappear when not relevant.
// An empty slice here indicates no warnings.
// +optional
Warnings []SubCondition `json:"warnings,omitempty"`
}
14 changes: 13 additions & 1 deletion apis/projectcontour/v1/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@

// +k8s:deepcopy-gen=package

// Package v1 is the v1 version of the API.
// This package holds the specification for the projectcontour.io Custom Resource Definitions (CRDs).
//
// In building this CRD, we've inadvertently overloaded the word "Condition", so we've tried to make
// this spec clear as to which types of condition are which.
//
// `MatchConditions` are used by `Routes` and `Includes` to specify rules to match requests against for either
// routing or inclusion.
//
// `DetailedConditions` are used in the `Status` of these objects to hold information about the relevant
// state of the object and the world around it.
//
// `SubConditions` are used underneath `DetailedConditions` to give more detail to errors or warnings.
//
// +groupName=projectcontour.io
package v1
30 changes: 25 additions & 5 deletions apis/projectcontour/v1/httpproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// HTTPProxySpec defines the spec of the CRD.
type HTTPProxySpec struct {
// Virtualhost appears at most once. If it is present, the object is considered
// to be a "root".
// to be a "root" HTTPProxy.
// +optional
VirtualHost *VirtualHost `json:"virtualhost,omitempty"`
// Routes are the ingress routes. If TCPProxy is present, Routes is ignored.
Expand All @@ -30,7 +30,8 @@ type HTTPProxySpec struct {
// TCPProxy holds TCP proxy information.
// +optional
TCPProxy *TCPProxy `json:"tcpproxy,omitempty"`
// Includes allow for specific routing configuration to be appended to another HTTPProxy in another namespace.
// Includes allow for specific routing configuration to be included from another HTTPProxy,
// possibly in another namespace.
// +optional
Includes []Include `json:"includes,omitempty"`
}
Expand Down Expand Up @@ -104,11 +105,11 @@ type HeaderMatchCondition struct {
// to be a "root".
type VirtualHost struct {
// The fully qualified domain name of the root of the ingress tree
// all leaves of the DAG rooted at this object relate to the fqdn
// all leaves of the DAG rooted at this object relate to the fqdn.
Fqdn string `json:"fqdn"`
// If present describes tls properties. The SNI names that will be matched on
// are described in fqdn, the tls.secretName secret must contain a
// matching certificate
// certificate that itself contains a name that matches the FQDN.
// +optional
TLS *TLS `json:"tls,omitempty"`
}
Expand Down Expand Up @@ -467,12 +468,30 @@ type HTTPProxyStatus struct {
// +optional
// LoadBalancer contains the current status of the load balancer.
LoadBalancer corev1.LoadBalancerStatus `json:"loadBalancer,omitempty"`
// +optional
// Conditions contains information about the current status of the HTTPProxy,
// in an upstream-friendly container.
//
// Contour will update a single condition, `Valid`, that is in normal-true polarity.
// That is, when `currentStatus` is `valid`, the `Valid` condition will be `status: true`,
// and vice versa.
//
// Contour will leave untouched any other Conditions set in this block,
// in case some other controller wants to add a Condition.
//
// If you are another controller owner and wish to add a condition, you *should*
// namespace your condition with a label, like `controller.domain.com/ConditionName`.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []DetailedCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// HTTPProxy is an Ingress CRD specification
// HTTPProxy is an Ingress CRD specification.
// +k8s:openapi-gen=true
// +kubebuilder:printcolumn:name="FQDN",type="string",JSONPath=".spec.virtualhost.fqdn",description="Fully qualified domain name"
// +kubebuilder:printcolumn:name="TLS Secret",type="string",JSONPath=".spec.virtualhost.tls.secretName",description="Secret with TLS credentials"
Expand All @@ -485,6 +504,7 @@ type HTTPProxy struct {
metav1.ObjectMeta `json:"metadata"`

Spec HTTPProxySpec `json:"spec"`
// Status is a container for computed information about the HTTPProxy.
// +optional
Status HTTPProxyStatus `json:"status,omitempty"`
}
Expand Down
109 changes: 109 additions & 0 deletions apis/projectcontour/v1/kubeconditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// +k8s:deepcopy-gen=package

// Package v1 is the v1 version of the API.
// +groupName=projectcontour.io
package v1

// TODO(youngnick): This whole file is a copy-and-paste from
// the current upstream master of "k8s.io/apimachinery/pkg/apis/meta/v1".
// This should be removed once we update to a version of client-go
// that includes a metav1.Condition type.
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type ConditionStatus string

// These are valid condition statuses. "ConditionTrue" means a resource is in the condition.
// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes
// can't decide if a resource is in the condition or not. In the future, we could add other
// intermediate conditions, e.g. ConditionDegraded.
const (
ConditionTrue ConditionStatus = "True"
ConditionFalse ConditionStatus = "False"
ConditionUnknown ConditionStatus = "Unknown"
)

// Condition contains details for one aspect of the current state of this API Resource.
//
// This struct is intended for direct use as an array at the field path .status.conditions. For example,
// ```go
// type FooStatus struct{
// // Represents the observations of a foo's current state.
// // Known .status.conditions.type are: "Available", "Progressing", and "Degraded"
// // +patchMergeKey=type
// // +patchStrategy=merge
// // +listType=map
// // +listMapKey=type
// Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
//
// // other fields
// }
// ```
type Condition struct {
// Type of condition in CamelCase or in foo.example.com/CamelCase.
//
// Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
// useful (see .node.status.conditions), the ability to deconflict is important.
//
// The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$`
// +kubebuilder:validation:MaxLength=316
Type string `json:"type" protobuf:"bytes,1,opt,name=type"`
// status of the condition, one of True, False, Unknown.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=True;False;Unknown
Status ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status"`
// observedGeneration represents the .metadata.generation that the condition was set based upon.
//
// For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
// with respect to the current state of the instance.
// +optional
// +kubebuilder:validation:Minimum=0
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,3,opt,name=observedGeneration"`
// lastTransitionTime is the last time the condition transitioned from one status to another.
//
// This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:Type=string
// +kubebuilder:validation:Format=date-time
LastTransitionTime metav1.Time `json:"lastTransitionTime" protobuf:"bytes,4,opt,name=lastTransitionTime"`
// Reason contains a programmatic identifier indicating the reason for the condition's last transition.
//
// Producers of specific condition types may define expected values and meanings for this field,
// and whether the values are considered a guaranteed API.
//
// The value should be a CamelCase string.
//
// This field may not be empty.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=1024
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:Pattern=`^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$`
Reason string `json:"reason" protobuf:"bytes,5,opt,name=reason"`
// message is a human readable message indicating details about the transition.
//
// This may be an empty string.
// +required
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=32768
Message string `json:"message" protobuf:"bytes,6,opt,name=message"`
}
26 changes: 26 additions & 0 deletions apis/projectcontour/v1/tlscertificatedelegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,44 @@ type CertificateDelegation struct {
TargetNamespaces []string `json:"targetNamespaces"`
}

// TLSCertificateDelegationStatus allows for the status of the delegation
// to be presented to the user.
type TLSCertificateDelegationStatus struct {
// +optional
// Conditions contains information about the current status of the HTTPProxy,
// in an upstream-friendly container.
//
// Contour will update a single condition, `Valid`, that is in normal-true polarity.
// That is, when `currentStatus` is `valid`, the `Valid` condition will be `status: true`,
// and vice versa.
//
// Contour will leave untouched any other Conditions set in this block,
// in case some other controller wants to add a Condition.
//
// If you are another controller owner and wish to add a condition, you *should*
// namespace your condition with a label, like `controller.domain.com\ConditionName`.
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []DetailedCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// TLSCertificateDelegation is an TLS Certificate Delegation CRD specificiation.
// See design/tls-certificate-delegation.md for details.
// +k8s:openapi-gen=true
// +kubebuilder:resource:scope=Namespaced,path=tlscertificatedelegations,shortName=tlscerts,singular=tlscertificatedelegation
// +kubebuilder:subresource:status
type TLSCertificateDelegation struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`

Spec TLSCertificateDelegationSpec `json:"spec"`
// +optional
Status TLSCertificateDelegationStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down

0 comments on commit cc3076a

Please sign in to comment.