github.com/oam-dev/cluster-gateway@v1.9.0/pkg/apis/cluster/v1alpha1/virtualcluster_client.go (about)

     1  /*
     2  Copyright 2023 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package v1alpha1
    18  
    19  import (
    20  	"context"
    21  	"sort"
    22  
    23  	corev1 "k8s.io/api/core/v1"
    24  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    25  	"k8s.io/apimachinery/pkg/api/meta"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apimachinery/pkg/selection"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    32  	"k8s.io/utils/strings/slices"
    33  	ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
    34  	"sigs.k8s.io/controller-runtime/pkg/client"
    35  
    36  	"github.com/oam-dev/cluster-gateway/pkg/common"
    37  	"github.com/oam-dev/cluster-gateway/pkg/config"
    38  )
    39  
    40  // VirtualClusterClient client for reading cluster information
    41  // +kubebuilder:object:generate=false
    42  type VirtualClusterClient interface {
    43  	Get(ctx context.Context, name string) (*VirtualCluster, error)
    44  	List(ctx context.Context, options ...client.ListOption) (*VirtualClusterList, error)
    45  }
    46  
    47  type virtualClusterClient struct {
    48  	client.Client
    49  	namespace        string
    50  	withControlPlane bool
    51  }
    52  
    53  // NewVirtualClusterClient create a client for accessing cluster
    54  func NewVirtualClusterClient(cli client.Client, namespace string, withControlPlane bool) VirtualClusterClient {
    55  	return &virtualClusterClient{Client: cli, namespace: namespace, withControlPlane: withControlPlane}
    56  }
    57  
    58  func (c *virtualClusterClient) Get(ctx context.Context, name string) (*VirtualCluster, error) {
    59  	if name == ClusterLocalName {
    60  		return NewLocalCluster(), nil
    61  	}
    62  	key := types.NamespacedName{Name: name, Namespace: c.namespace}
    63  	var cluster *VirtualCluster
    64  	secret := &corev1.Secret{}
    65  	err := c.Client.Get(ctx, key, secret)
    66  	var secretErr error
    67  	if err == nil {
    68  		if cluster, secretErr = NewClusterFromSecret(secret); secretErr == nil {
    69  			return cluster, nil
    70  		}
    71  	}
    72  	if err != nil && !apierrors.IsNotFound(err) {
    73  		secretErr = err
    74  	}
    75  
    76  	managedCluster := &ocmclusterv1.ManagedCluster{}
    77  	err = c.Client.Get(ctx, key, managedCluster)
    78  	var managedClusterErr error
    79  	if err == nil {
    80  		if cluster, managedClusterErr = NewClusterFromManagedCluster(managedCluster); managedClusterErr == nil {
    81  			return cluster, nil
    82  		}
    83  	}
    84  
    85  	if err != nil && !apierrors.IsNotFound(err) && !meta.IsNoMatchError(err) && !runtime.IsNotRegisteredError(err) {
    86  		managedClusterErr = err
    87  	}
    88  
    89  	errs := utilerrors.NewAggregate([]error{secretErr, managedClusterErr})
    90  	if errs == nil {
    91  		return nil, apierrors.NewNotFound(schema.GroupResource{
    92  			Group:    config.MetaApiGroupName,
    93  			Resource: "virtualclusters",
    94  		}, name)
    95  	} else if len(errs.Errors()) == 1 {
    96  		return nil, errs.Errors()[0]
    97  	} else {
    98  		return nil, errs
    99  	}
   100  }
   101  
   102  func (c *virtualClusterClient) List(ctx context.Context, options ...client.ListOption) (*VirtualClusterList, error) {
   103  	opts := &client.ListOptions{}
   104  	for _, opt := range options {
   105  		opt.ApplyToList(opts)
   106  	}
   107  	local := NewLocalCluster()
   108  	clusters := &VirtualClusterList{Items: []VirtualCluster{*local}}
   109  
   110  	secrets := &corev1.SecretList{}
   111  	err := c.Client.List(ctx, secrets, virtualClusterSelector{selector: opts.LabelSelector, requireCredentialType: true, namespace: c.namespace})
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	for _, secret := range secrets.Items {
   116  		if cluster, err := NewClusterFromSecret(secret.DeepCopy()); err == nil {
   117  			clusters.Items = append(clusters.Items, *cluster)
   118  		}
   119  	}
   120  
   121  	managedClusters := &ocmclusterv1.ManagedClusterList{}
   122  	err = c.Client.List(ctx, managedClusters, virtualClusterSelector{selector: opts.LabelSelector, requireCredentialType: false})
   123  	if err != nil && !meta.IsNoMatchError(err) && !runtime.IsNotRegisteredError(err) {
   124  		return nil, err
   125  	}
   126  	for _, managedCluster := range managedClusters.Items {
   127  		if !clusters.HasCluster(managedCluster.Name) {
   128  			if cluster, err := NewClusterFromManagedCluster(managedCluster.DeepCopy()); err == nil {
   129  				clusters.Items = append(clusters.Items, *cluster)
   130  			}
   131  		}
   132  	}
   133  
   134  	// filter clusters
   135  	var items []VirtualCluster
   136  	for _, cluster := range clusters.Items {
   137  		if opts.LabelSelector == nil || opts.LabelSelector.Matches(labels.Set(cluster.GetLabels())) {
   138  			items = append(items, cluster)
   139  		}
   140  	}
   141  	clusters.Items = items
   142  
   143  	// sort clusters
   144  	sort.Slice(clusters.Items, func(i, j int) bool {
   145  		if clusters.Items[i].Name == ClusterLocalName {
   146  			return true
   147  		} else if clusters.Items[j].Name == ClusterLocalName {
   148  			return false
   149  		} else {
   150  			return clusters.Items[i].CreationTimestamp.After(clusters.Items[j].CreationTimestamp.Time)
   151  		}
   152  	})
   153  	return clusters, nil
   154  }
   155  
   156  // virtualClusterSelector filters the list/delete operation of cluster list
   157  type virtualClusterSelector struct {
   158  	selector              labels.Selector
   159  	requireCredentialType bool
   160  	namespace             string
   161  }
   162  
   163  // ApplyToList applies this configuration to the given list options.
   164  func (m virtualClusterSelector) ApplyToList(opts *client.ListOptions) {
   165  	opts.LabelSelector = labels.NewSelector()
   166  	if m.selector != nil {
   167  		requirements, _ := m.selector.Requirements()
   168  		for _, r := range requirements {
   169  			if !slices.Contains([]string{LabelClusterControlPlane}, r.Key()) {
   170  				opts.LabelSelector = opts.LabelSelector.Add(r)
   171  			}
   172  		}
   173  	}
   174  	if m.requireCredentialType {
   175  		r, _ := labels.NewRequirement(common.LabelKeyClusterCredentialType, selection.Exists, nil)
   176  		opts.LabelSelector = opts.LabelSelector.Add(*r)
   177  	}
   178  	opts.Namespace = m.namespace
   179  }