181 lines
4.3 KiB
Go
181 lines
4.3 KiB
Go
/*
|
|
* Copyright 2024 the KubeSphere Authors.
|
|
* Please refer to the LICENSE file in the root directory of the project.
|
|
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
|
*/
|
|
|
|
package v1alpha2
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/emicklei/go-restful/v3"
|
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/klog/v2"
|
|
"kubesphere.io/api/gateway/v1alpha2"
|
|
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"kubesphere.io/kubesphere/pkg/api"
|
|
)
|
|
|
|
const (
|
|
MasterLabel = "node-role.kubernetes.io/control-plane"
|
|
SvcNameAnnotation = "gateway.kubesphere.io/service-name"
|
|
)
|
|
|
|
type handler struct {
|
|
cache runtimeclient.Reader
|
|
}
|
|
|
|
func (h *handler) ListIngressClassScopes(req *restful.Request, resp *restful.Response) {
|
|
currentNs := req.PathParameter("namespace")
|
|
ctx := req.Request.Context()
|
|
|
|
ingressClassScopeList := v1alpha2.IngressClassScopeList{}
|
|
err := h.cache.List(ctx, &ingressClassScopeList)
|
|
if err != nil {
|
|
api.HandleError(resp, req, err)
|
|
return
|
|
}
|
|
|
|
var ret []v1alpha2.IngressClassScope
|
|
for _, item := range ingressClassScopeList.Items {
|
|
namespaces := item.Spec.Scope.Namespaces
|
|
nsSelector := item.Spec.Scope.NamespaceSelector
|
|
|
|
// Specify all namespace
|
|
if len(namespaces) == 0 && nsSelector == "" {
|
|
_ = h.setStatus(ctx, &item)
|
|
ret = append(ret, item)
|
|
continue
|
|
}
|
|
|
|
// Specify namespaces
|
|
if len(namespaces) > 0 {
|
|
for _, n := range namespaces {
|
|
if n == currentNs {
|
|
_ = h.setStatus(ctx, &item)
|
|
ret = append(ret, item)
|
|
break
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Specify namespaceSelector
|
|
if nsSelector != "" {
|
|
nsList := corev1.NamespaceList{}
|
|
_ = h.cache.List(ctx, &nsList, &runtimeclient.ListOptions{LabelSelector: Selector(nsSelector)})
|
|
for _, n := range nsList.Items {
|
|
if n.Name == currentNs {
|
|
_ = h.setStatus(ctx, &item)
|
|
ret = append(ret, item)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resp.WriteEntity(ret)
|
|
}
|
|
|
|
func Selector(s string) labels.Selector {
|
|
if selector, err := labels.Parse(s); err != nil {
|
|
return labels.Everything()
|
|
} else {
|
|
return selector
|
|
}
|
|
}
|
|
|
|
func (h *handler) getMasterNodeIp(ctx context.Context) []string {
|
|
internalIps := []string{}
|
|
masters := &corev1.NodeList{}
|
|
err := h.cache.List(ctx, masters, &runtimeclient.ListOptions{
|
|
LabelSelector: labels.SelectorFromSet(
|
|
labels.Set{
|
|
MasterLabel: "",
|
|
})})
|
|
|
|
if err != nil {
|
|
klog.Info(err)
|
|
return internalIps
|
|
}
|
|
|
|
for _, node := range masters.Items {
|
|
for _, address := range node.Status.Addresses {
|
|
if address.Type == corev1.NodeInternalIP {
|
|
internalIps = append(internalIps, address.Address)
|
|
}
|
|
}
|
|
}
|
|
return internalIps
|
|
}
|
|
|
|
func (h *handler) setStatus(ctx context.Context, ics *v1alpha2.IngressClassScope) (e error) {
|
|
svcKeyStr, exists := ics.Annotations[SvcNameAnnotation]
|
|
if !exists {
|
|
klog.Errorf("Name: %s, Annotation %s not found", ics.Name, SvcNameAnnotation)
|
|
return nil
|
|
}
|
|
|
|
svcKeyParts := strings.SplitN(svcKeyStr, "/", 2)
|
|
if len(svcKeyParts) != 2 {
|
|
klog.Errorf("Name: %s, Invalid %s annotation, should follow the namespace/name format", ics.Name, SvcNameAnnotation)
|
|
return nil
|
|
}
|
|
|
|
svc := corev1.Service{}
|
|
key := types.NamespacedName{
|
|
Namespace: svcKeyParts[0],
|
|
Name: svcKeyParts[1],
|
|
}
|
|
|
|
if err := h.cache.Get(ctx, key, &svc); err != nil {
|
|
klog.Errorf("Failed to fetch svc %s, %v", key.String(), err)
|
|
return err
|
|
}
|
|
|
|
// append selected node ip as loadBalancer ingress ip
|
|
if svc.Spec.Type != corev1.ServiceTypeLoadBalancer && len(svc.Status.LoadBalancer.Ingress) == 0 {
|
|
rips := h.getMasterNodeIp(ctx)
|
|
for _, rip := range rips {
|
|
gIngress := corev1.LoadBalancerIngress{
|
|
IP: rip,
|
|
}
|
|
svc.Status.LoadBalancer.Ingress = append(svc.Status.LoadBalancer.Ingress, gIngress)
|
|
}
|
|
}
|
|
|
|
status := unstructured.Unstructured{
|
|
Object: map[string]interface{}{
|
|
"loadBalancer": svc.Status.LoadBalancer,
|
|
"service": svc.Spec.Ports,
|
|
},
|
|
}
|
|
|
|
target, err := status.MarshalJSON()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ics.Status.Raw != nil {
|
|
//merge with origin status
|
|
patch, err := jsonpatch.CreateMergePatch([]byte(`{}`), target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
modified, err := jsonpatch.MergePatch(ics.Status.Raw, patch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ics.Status.Raw = modified
|
|
}
|
|
ics.Status.Raw = target
|
|
return nil
|
|
}
|