Skip to content

Commit 36c6d81

Browse files
authored
Merge pull request #293 from stuggi/route_override
Allow customize route via route.Override
2 parents 13724f4 + a5afd03 commit 36c6d81

File tree

8 files changed

+687
-8
lines changed

8 files changed

+687
-8
lines changed

modules/common/endpoint/endpoint.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ type Data struct {
5252
Path string
5353
// details for metallb service generation
5454
MetalLB *MetalLBData
55+
// possible overrides for Route
56+
RouteOverride *route.OverrideSpec
5557
}
5658

5759
// MetalLBData - information specific to creating the MetalLB service
@@ -174,7 +176,7 @@ func ExposeEndpoints(
174176
if endpointType == EndpointPublic {
175177
// Create the route
176178
// TODO TLS
177-
route := route.NewRoute(
179+
route, err := route.NewRoute(
178180
route.GenericRoute(&route.GenericRouteDetails{
179181
Name: endpointName,
180182
Namespace: h.GetBeforeObject().GetNamespace(),
@@ -184,7 +186,11 @@ func ExposeEndpoints(
184186
}),
185187
exportLabels,
186188
timeout,
189+
data.RouteOverride,
187190
)
191+
if err != nil {
192+
return endpointMap, ctrl.Result{}, err
193+
}
188194

189195
ctrlResult, err = route.CreateOrPatch(ctx, h)
190196
if err != nil {

modules/common/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ require (
6969
k8s.io/component-base v0.26.6 // indirect; indirect // indirect
7070
k8s.io/klog/v2 v2.100.1 // indirect
7171
k8s.io/kube-openapi v0.0.0-20230308215209-15aac26d736a // indirect; indirect // indirect
72-
k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect; indirect // indirect
72+
k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect // indirect
7373
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect; indirect // indirect
7474
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
7575
sigs.k8s.io/yaml v1.3.0 // indirect

modules/common/route/route.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package route
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"time"
2324

@@ -29,6 +30,7 @@ import (
2930
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
3031
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3132
"k8s.io/apimachinery/pkg/util/intstr"
33+
"k8s.io/apimachinery/pkg/util/strategicpatch"
3234
ctrl "sigs.k8s.io/controller-runtime"
3335
)
3436

@@ -37,11 +39,49 @@ func NewRoute(
3739
route *routev1.Route,
3840
labels map[string]string,
3941
timeout time.Duration,
40-
) *Route {
41-
return &Route{
42+
override *OverrideSpec,
43+
) (*Route, error) {
44+
r := &Route{
4245
route: route,
4346
timeout: timeout,
4447
}
48+
49+
// patch route with possible overrides of Labels, Annotations and Spec
50+
if override != nil {
51+
if override.EmbeddedLabelsAnnotations != nil {
52+
if override.Labels != nil {
53+
r.route.Labels = util.MergeStringMaps(override.Labels, r.route.Labels)
54+
}
55+
if override.Annotations != nil {
56+
r.route.Annotations = util.MergeStringMaps(override.Annotations, r.route.Annotations)
57+
}
58+
}
59+
if override.Spec != nil {
60+
originalSpec, err := json.Marshal(r.route.Spec)
61+
if err != nil {
62+
return r, fmt.Errorf("error marshalling Route Spec: %w", err)
63+
}
64+
65+
patch, err := json.Marshal(override.Spec)
66+
if err != nil {
67+
return r, fmt.Errorf("error marshalling Route Spec override: %w", err)
68+
}
69+
70+
patchedJSON, err := strategicpatch.StrategicMergePatch(originalSpec, patch, routev1.RouteSpec{})
71+
if err != nil {
72+
return r, fmt.Errorf("error patching Route Spec: %w", err)
73+
}
74+
75+
patchedSpec := routev1.RouteSpec{}
76+
err = json.Unmarshal(patchedJSON, &patchedSpec)
77+
if err != nil {
78+
return r, fmt.Errorf("error unmarshalling patched Route Spec: %w", err)
79+
}
80+
r.route.Spec = patchedSpec
81+
}
82+
}
83+
84+
return r, nil
4585
}
4686

4787
// GetHostname - returns the hostname of the created route
@@ -89,7 +129,7 @@ func (r *Route) CreateOrPatch(
89129
}
90130

91131
op, err := controllerutil.CreateOrPatch(ctx, h.GetClient(), route, func() error {
92-
route.Labels = util.MergeStringMaps(route.Labels, r.route.Labels)
132+
route.Labels = r.route.Labels
93133
route.Annotations = r.route.Annotations
94134
route.Spec = r.route.Spec
95135
if len(route.Spec.Host) == 0 && len(route.Status.Ingress) > 0 {

modules/common/route/types.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
// +kubebuilder:object:generate:=true
18+
1719
package route
1820

1921
import (
@@ -38,3 +40,146 @@ type GenericRouteDetails struct {
3840
TargetPortName string
3941
FQDN string
4042
}
43+
44+
// OverrideSpec configuration for the Route created to serve traffic to the cluster.
45+
type OverrideSpec struct {
46+
// +optional
47+
*EmbeddedLabelsAnnotations `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
48+
// Spec defines the behavior of a Route.
49+
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
50+
//
51+
// The spec will be merged using StrategicMergePatch
52+
// - Provided parameters will override the ones from the original spec.
53+
// - Required parameters of sub structs have to be named.
54+
// - For parameters which are list of struct it depends on the patchStrategy defined on the list
55+
// https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#notes-on-the-strategic-merge-patch
56+
// If `patchStrategy:"merge"` is set, src and dst list gets merged, otherwise they get replaced.
57+
// +optional
58+
Spec *Spec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
59+
}
60+
61+
// EmbeddedLabelsAnnotations is an embedded subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta.
62+
// Only labels and annotations are included.
63+
// New labels/annotations get merged with the ones created by the operator. If a privided
64+
// annotation/label is the same as one created by the service operator, the ones provided
65+
// via this override will replace the one from the operator.
66+
type EmbeddedLabelsAnnotations struct {
67+
// Map of string keys and values that can be used to organize and categorize
68+
// (scope and select) objects. May match selectors of replication controllers
69+
// and services.
70+
// More info: http://kubernetes.io/docs/user-guide/labels
71+
// +optional
72+
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
73+
74+
// Annotations is an unstructured key value map stored with a resource that may be
75+
// set by external tools to store and retrieve arbitrary metadata. They are not
76+
// queryable and should be preserved when modifying objects.
77+
// More info: http://kubernetes.io/docs/user-guide/annotations
78+
// +optional
79+
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
80+
}
81+
82+
// Spec describes the hostname or path the route exposes, any security information,
83+
// and one to four backends (services) the route points to. Requests are distributed
84+
// among the backends depending on the weights assigned to each backend. When using
85+
// roundrobin scheduling the portion of requests that go to each backend is the backend
86+
// weight divided by the sum of all of the backend weights. When the backend has more than
87+
// one endpoint the requests that end up on the backend are roundrobin distributed among
88+
// the endpoints. Weights are between 0 and 256 with default 100. Weight 0 causes no requests
89+
// to the backend. If all weights are zero the route will be considered to have no backends
90+
// and return a standard 503 response.
91+
//
92+
// The `tls` field is optional and allows specific certificates or behavior for the
93+
// route. Routers typically configure a default certificate on a wildcard domain to
94+
// terminate routes without explicit certificates, but custom hostnames usually must
95+
// choose passthrough (send traffic directly to the backend via the TLS Server-Name-
96+
// Indication field) or provide a certificate.
97+
//
98+
// Copy of RouteSpec in https://github.com/openshift/api/blob/master/route/v1/types.go,
99+
// parameters set to be optional, have omitempty, and no default.
100+
type Spec struct {
101+
// host is an alias/DNS that points to the service. Optional.
102+
// If not specified a route name will typically be automatically
103+
// chosen.
104+
// Must follow DNS952 subdomain conventions.
105+
//
106+
// +optional
107+
// +kubebuilder:validation:MaxLength=253
108+
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
109+
Host string `json:"host,omitempty" protobuf:"bytes,1,opt,name=host"`
110+
// subdomain is a DNS subdomain that is requested within the ingress controller's
111+
// domain (as a subdomain). If host is set this field is ignored. An ingress
112+
// controller may choose to ignore this suggested name, in which case the controller
113+
// will report the assigned name in the status.ingress array or refuse to admit the
114+
// route. If this value is set and the server does not support this field host will
115+
// be populated automatically. Otherwise host is left empty. The field may have
116+
// multiple parts separated by a dot, but not all ingress controllers may honor
117+
// the request. This field may not be changed after creation except by a user with
118+
// the update routes/custom-host permission.
119+
//
120+
// Example: subdomain `frontend` automatically receives the router subdomain
121+
// `apps.mycluster.com` to have a full hostname `frontend.apps.mycluster.com`.
122+
//
123+
// +optional
124+
// +kubebuilder:validation:MaxLength=253
125+
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
126+
Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,8,opt,name=subdomain"`
127+
128+
// path that the router watches for, to route traffic for to the service. Optional
129+
//
130+
// +optional
131+
// +kubebuilder:validation:Pattern=`^/`
132+
Path string `json:"path,omitempty" protobuf:"bytes,2,opt,name=path"`
133+
134+
// to is an object the route should use as the primary backend. Only the Service kind
135+
// is allowed, and it will be defaulted to Service. If the weight field (0-256 default 100)
136+
// is set to zero, no traffic will be sent to this backend.
137+
To TargetReference `json:"to,omitempty" protobuf:"bytes,3,opt,name=to"`
138+
139+
// alternateBackends allows up to 3 additional backends to be assigned to the route.
140+
// Only the Service kind is allowed, and it will be defaulted to Service.
141+
// Use the weight field in RouteTargetReference object to specify relative preference.
142+
//
143+
// +kubebuilder:validation:MaxItems=3
144+
AlternateBackends []TargetReference `json:"alternateBackends,omitempty" protobuf:"bytes,4,rep,name=alternateBackends"`
145+
146+
// If specified, the port to be used by the router. Most routers will use all
147+
// endpoints exposed by the service by default - set this value to instruct routers
148+
// which port to use.
149+
// +optional
150+
Port *routev1.RoutePort `json:"port,omitempty" protobuf:"bytes,5,opt,name=port"`
151+
152+
// The tls field provides the ability to configure certificates and termination for the route.
153+
TLS *routev1.TLSConfig `json:"tls,omitempty" protobuf:"bytes,6,opt,name=tls"`
154+
155+
// Wildcard policy if any for the route.
156+
// Currently only 'Subdomain' or 'None' is allowed.
157+
//
158+
// +kubebuilder:validation:Enum=None;Subdomain;""
159+
WildcardPolicy routev1.WildcardPolicyType `json:"wildcardPolicy,omitempty" protobuf:"bytes,7,opt,name=wildcardPolicy"`
160+
}
161+
162+
// TargetReference specifies the target that resolve into endpoints. Only the 'Service'
163+
// kind is allowed. Use 'weight' field to emphasize one over others.
164+
// Copy of RouteTargetReference in https://github.com/openshift/api/blob/master/route/v1/types.go,
165+
// parameters set to be optional, have omitempty, and no default.
166+
type TargetReference struct {
167+
// The kind of target that the route is referring to. Currently, only 'Service' is allowed
168+
//
169+
// +optional
170+
// +kubebuilder:validation:Enum=Service;""
171+
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
172+
173+
// name of the service/target that is being referred to. e.g. name of the service
174+
//
175+
// +optional
176+
Name string `json:"name,omitempty" protobuf:"bytes,2,opt,name=name"`
177+
178+
// weight as an integer between 0 and 256, default 100, that specifies the target's relative weight
179+
// against other target reference objects. 0 suppresses requests to this backend.
180+
//
181+
// +optional
182+
// +kubebuilder:validation:Minimum=0
183+
// +kubebuilder:validation:Maximum=256
184+
Weight *int32 `json:"weight,omitempty" protobuf:"varint,3,opt,name=weight"`
185+
}

0 commit comments

Comments
 (0)