mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-08 21:14:56 +09:00
Add the serviceexposure controller to the cozystack-controller binary. It resolves a ServiceExposure's ExposureClass to a backend and renders the backend-specific pool and announcer CRs, then reports the assigned IPs in status. Backends are pluggable behind a small interface, one per file: - externalIPs: renders nothing (the node owns the address) — preserves the historical default behaviour. - metallb: IPAddressPool + L2Advertisement in cozy-metallb, scoped to the target Service's namespace. - cilium: CiliumLoadBalancerIPPool (cilium.io/v2) + CiliumL2AnnouncementPolicy (cilium.io/v2alpha1), scoped to the Service via serviceSelector. - robotlb: renders nothing (the cloud LB owns allocation and announcement). The rendered CRs belong to foreign API groups not in the Go scheme, so they are emitted as unstructured objects. They carry no OwnerReference (the Cilium CRs are cluster-scoped and the MetalLB CRs live in a different namespace than the owning ServiceExposure), so ownership is tracked via a management label plus owner annotations, cleanup is driven by a finalizer, and the controller refuses to take over an identically-named resource it does not manage. The controller never mutates the target Service, leaving its shape to the chart that renders it. Grant the controller write access to metallb.io and cilium.io pool / announcer resources. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
54 lines
1.8 KiB
Go
54 lines
1.8 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 backend
|
|
|
|
import (
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
networkv1alpha1 "github.com/cozystack/cozystack/api/network/v1alpha1"
|
|
)
|
|
|
|
// externalIPsBackend pins the Service to operator-supplied addresses via
|
|
// Service.spec.externalIPs. The node already owns the address, so there
|
|
// is no pool to allocate and no announcer to render — this backend
|
|
// produces zero cluster resources and exists to preserve the historical
|
|
// default behaviour while keeping the API uniform across backends. The
|
|
// Service's own spec.externalIPs is set by the chart that renders it, not
|
|
// by this controller.
|
|
type externalIPsBackend struct{}
|
|
|
|
func (externalIPsBackend) Name() networkv1alpha1.ExposureBackend {
|
|
return networkv1alpha1.BackendExternalIPs
|
|
}
|
|
|
|
func (externalIPsBackend) Desired(
|
|
_ *networkv1alpha1.ExposureClass,
|
|
_ []string,
|
|
) ([]*unstructured.Unstructured, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (externalIPsBackend) Observe(svc *corev1.Service) (assignedIPs []string, ready bool, reason string) {
|
|
if len(svc.Spec.ExternalIPs) > 0 {
|
|
return append([]string(nil), svc.Spec.ExternalIPs...), true, ""
|
|
}
|
|
return nil, false, "NoExternalIPs"
|
|
}
|
|
|
|
func init() { register(externalIPsBackend{}) }
|