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 }