mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-30 00:27:04 +09:00
The TenantGateway controller withheld the HTTPS-terminate listener for a name a passthrough listener answers only once a TLSRoute was attached there and resolved a backend, mirroring how Cilium builds its filter chains. That kept a served endpoint up on Cilium 1.19.5, but it tied every route's status to one implementation's translator and needed Service and ReferenceGrant watches to land the deferred decision. The shipped operator accepts a wildcard terminate listener beside a narrower passthrough listener on 443 and keeps conflicting every other overlap. A terminate listener for the passthrough name itself is not narrower, so on 443 the rule now protects the dataplane outright; on a native port Gateway API would serve the pair, and the rule holds as a policy that keeps a platform name with its passthrough listener. Reading it off the spec makes the outcome the same on every implementation. Under http01 an HTTPRoute claiming such a name gets no listener and no certificate whether or not a TLSRoute is attached. A route whose every hostname is reserved reads Accepted=False with NoMatchingListenerHostname; one that also claims a free hostname stays Accepted and is served there, as Gateway API accepts a route whose hostnames intersect at least one listener. dns01 and existingSecret are unchanged: their wildcard listener still covers such a name, and the passthrough listener answers it by its exact server name. The chart writes the passthrough services on the publishing tenant alone, so the names reserved by default are the ones the platform publishes there through its own TLSRoutes. The shape an upgrade changes is an HTTPRoute on such a name whose platform TLSRoute forwards nowhere, because it never attached (the package owning the service is not installed) or because none of its backends resolve: that route kept a terminate listener and now gets none. The backend resolution, its watches and the RBAC rules that served them go away. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <f@lex.la>
107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
/*
|
|
Copyright 2026 The Cozystack 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.
|
|
*/
|
|
|
|
package tenantgateway
|
|
|
|
import (
|
|
"context"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
|
|
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
|
|
|
gatewayv1alpha1 "github.com/cozystack/cozystack/api/gateway/v1alpha1"
|
|
)
|
|
|
|
// routeToTenantGateway returns an EventHandler that maps an HTTPRoute
|
|
// or TLSRoute change back to the TenantGateway resources whose Gateway
|
|
// the route attaches to. controller-runtime requeues the parent so
|
|
// listener / cert lifecycle stays in sync with route additions and
|
|
// removals.
|
|
func (r *Reconciler) routeToTenantGateway() handler.EventHandler {
|
|
return handler.EnqueueRequestsFromMapFunc(r.mapRouteToTenantGateways)
|
|
}
|
|
|
|
// mapRouteToTenantGateways is the underlying MapFunc, exposed as a
|
|
// method so tests can drive it directly without going through
|
|
// controller-runtime's EventHandler.Generic().
|
|
func (r *Reconciler) mapRouteToTenantGateways(ctx context.Context, obj client.Object) []reconcile.Request {
|
|
var (
|
|
parentRefs []gatewayv1.ParentReference
|
|
routeNs string
|
|
)
|
|
switch route := obj.(type) {
|
|
case *gatewayv1.HTTPRoute:
|
|
parentRefs = route.Spec.ParentRefs
|
|
routeNs = route.Namespace
|
|
case *gatewayv1alpha2.TLSRoute:
|
|
parentRefs = route.Spec.ParentRefs
|
|
routeNs = route.Namespace
|
|
default:
|
|
return nil
|
|
}
|
|
if len(parentRefs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
list := &gatewayv1alpha1.TenantGatewayList{}
|
|
if err := r.List(ctx, list); err != nil {
|
|
log.FromContext(ctx).Error(err, "list TenantGateways for route mapper")
|
|
return nil
|
|
}
|
|
|
|
var out []reconcile.Request
|
|
for i := range list.Items {
|
|
tgw := &list.Items[i]
|
|
if _, ok := pickAttachingParentRef(parentRefs, routeNs, tgw); !ok {
|
|
continue
|
|
}
|
|
out = append(out, reconcile.Request{
|
|
NamespacedName: types.NamespacedName{
|
|
Namespace: tgw.Namespace,
|
|
Name: tgw.Name,
|
|
},
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// mapGatewayClassToTenantGateways requeues the TenantGateways on a
|
|
// class, because GatewayClassUnsupported is judged from the class's
|
|
// status.supportedFeatures, which a class writes after it is accepted
|
|
// and may change on an upgrade of its controller, with nothing on the
|
|
// TenantGateway changing.
|
|
func (r *Reconciler) mapGatewayClassToTenantGateways(ctx context.Context, obj client.Object) []reconcile.Request {
|
|
list := &gatewayv1alpha1.TenantGatewayList{}
|
|
if err := r.List(ctx, list); err != nil {
|
|
log.FromContext(ctx).Error(err, "list TenantGateways for GatewayClass mapper")
|
|
return nil
|
|
}
|
|
var out []reconcile.Request
|
|
for i := range list.Items {
|
|
tgw := &list.Items[i]
|
|
if gatewayClassName(tgw) != obj.GetName() {
|
|
continue
|
|
}
|
|
out = append(out, reconcile.Request{NamespacedName: types.NamespacedName{Namespace: tgw.Namespace, Name: tgw.Name}})
|
|
}
|
|
return out
|
|
}
|