mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-05 11:11:28 +00:00
278 lines
8.6 KiB
Go
278 lines
8.6 KiB
Go
/*
|
|
Copyright 2020-2022 The KubeVela 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 multicluster
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
corev1 "k8s.io/api/core/v1"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
apilabels "k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/selection"
|
|
apitypes "k8s.io/apimachinery/pkg/types"
|
|
clusterv1 "open-cluster-management.io/api/cluster/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
|
|
"github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
|
|
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
|
|
|
|
"github.com/oam-dev/kubevela/apis/types"
|
|
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
|
|
)
|
|
|
|
// VirtualCluster contains base info of cluster, it unifies the difference between different cluster implementations
|
|
// like cluster secret or ocm managed cluster
|
|
type VirtualCluster struct {
|
|
Name string
|
|
Alias string
|
|
Type v1alpha1.CredentialType
|
|
EndPoint string
|
|
Accepted bool
|
|
Labels map[string]string
|
|
Metrics *ClusterMetrics
|
|
Object client.Object
|
|
}
|
|
|
|
// FullName the name with alias if available
|
|
func (vc *VirtualCluster) FullName() string {
|
|
if vc.Alias != "" {
|
|
return fmt.Sprintf("%s (%s)", vc.Name, vc.Alias)
|
|
}
|
|
return vc.Name
|
|
}
|
|
|
|
func getClusterAlias(o client.Object) string {
|
|
if annots := o.GetAnnotations(); annots != nil {
|
|
return annots[types.AnnotationClusterAlias]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func setClusterAlias(o client.Object, alias string) {
|
|
annots := o.GetAnnotations()
|
|
if annots == nil {
|
|
annots = map[string]string{}
|
|
}
|
|
annots[types.AnnotationClusterAlias] = alias
|
|
o.SetAnnotations(annots)
|
|
}
|
|
|
|
// NewVirtualClusterFromLocal return virtual cluster corresponding to local cluster
|
|
func NewVirtualClusterFromLocal() *VirtualCluster {
|
|
return &VirtualCluster{
|
|
Name: ClusterLocalName,
|
|
Type: types.CredentialTypeInternal,
|
|
EndPoint: types.ClusterBlankEndpoint,
|
|
Accepted: true,
|
|
Labels: map[string]string{},
|
|
Metrics: metricsMap[ClusterLocalName],
|
|
}
|
|
}
|
|
|
|
// NewVirtualClusterFromSecret extract virtual cluster from cluster secret
|
|
func NewVirtualClusterFromSecret(secret *corev1.Secret) (*VirtualCluster, error) {
|
|
endpoint := string(secret.Data["endpoint"])
|
|
labels := secret.GetLabels()
|
|
if labels == nil {
|
|
labels = map[string]string{}
|
|
}
|
|
if _endpoint, ok := labels[clustercommon.LabelKeyClusterEndpointType]; ok {
|
|
endpoint = _endpoint
|
|
}
|
|
credType, ok := labels[clustercommon.LabelKeyClusterCredentialType]
|
|
if !ok {
|
|
return nil, errors.Errorf("secret is not a valid cluster secret, no credential type found")
|
|
}
|
|
return &VirtualCluster{
|
|
Name: secret.Name,
|
|
Alias: getClusterAlias(secret),
|
|
Type: v1alpha1.CredentialType(credType),
|
|
EndPoint: endpoint,
|
|
Accepted: true,
|
|
Labels: labels,
|
|
Metrics: metricsMap[secret.Name],
|
|
Object: secret,
|
|
}, nil
|
|
}
|
|
|
|
// NewVirtualClusterFromManagedCluster extract virtual cluster from ocm managed cluster
|
|
func NewVirtualClusterFromManagedCluster(managedCluster *clusterv1.ManagedCluster) (*VirtualCluster, error) {
|
|
if len(managedCluster.Spec.ManagedClusterClientConfigs) == 0 {
|
|
return nil, errors.Errorf("managed cluster has no client config")
|
|
}
|
|
return &VirtualCluster{
|
|
Name: managedCluster.Name,
|
|
Alias: getClusterAlias(managedCluster),
|
|
Type: types.CredentialTypeOCMManagedCluster,
|
|
EndPoint: types.ClusterBlankEndpoint,
|
|
Accepted: managedCluster.Spec.HubAcceptsClient,
|
|
Labels: managedCluster.GetLabels(),
|
|
Metrics: metricsMap[managedCluster.Name],
|
|
Object: managedCluster,
|
|
}, nil
|
|
}
|
|
|
|
// GetVirtualCluster returns virtual cluster with given clusterName
|
|
func GetVirtualCluster(ctx context.Context, c client.Client, clusterName string) (vc *VirtualCluster, err error) {
|
|
if clusterName == ClusterLocalName {
|
|
return NewVirtualClusterFromLocal(), nil
|
|
}
|
|
secret := &corev1.Secret{}
|
|
err = c.Get(ctx, apitypes.NamespacedName{
|
|
Name: clusterName,
|
|
Namespace: ClusterGatewaySecretNamespace,
|
|
}, secret)
|
|
var secretErr error
|
|
if err == nil {
|
|
vc, secretErr = NewVirtualClusterFromSecret(secret)
|
|
if secretErr == nil {
|
|
return vc, nil
|
|
}
|
|
}
|
|
if err != nil && !apierrors.IsNotFound(err) {
|
|
secretErr = err
|
|
}
|
|
|
|
managedCluster := &clusterv1.ManagedCluster{}
|
|
err = c.Get(ctx, apitypes.NamespacedName{
|
|
Name: clusterName,
|
|
Namespace: ClusterGatewaySecretNamespace,
|
|
}, managedCluster)
|
|
var managedClusterErr error
|
|
if err == nil {
|
|
vc, managedClusterErr = NewVirtualClusterFromManagedCluster(managedCluster)
|
|
if managedClusterErr == nil {
|
|
return vc, nil
|
|
}
|
|
}
|
|
|
|
if err != nil && !apierrors.IsNotFound(err) && !velaerrors.IsCRDNotExists(err) {
|
|
managedClusterErr = err
|
|
}
|
|
|
|
if secretErr == nil && managedClusterErr == nil {
|
|
return nil, ErrClusterNotExists
|
|
}
|
|
|
|
var errs velaerrors.ErrorList
|
|
if secretErr != nil {
|
|
errs = append(errs, secretErr)
|
|
}
|
|
if managedClusterErr != nil {
|
|
errs = append(errs, managedClusterErr)
|
|
}
|
|
return nil, errs
|
|
}
|
|
|
|
// MatchVirtualClusterLabels filters the list/delete operation of cluster list
|
|
type MatchVirtualClusterLabels map[string]string
|
|
|
|
// ApplyToList applies this configuration to the given list options.
|
|
func (m MatchVirtualClusterLabels) ApplyToList(opts *client.ListOptions) {
|
|
sel := apilabels.SelectorFromValidatedSet(map[string]string(m))
|
|
r, err := apilabels.NewRequirement(clustercommon.LabelKeyClusterCredentialType, selection.Exists, nil)
|
|
if err == nil {
|
|
sel = sel.Add(*r)
|
|
}
|
|
opts.LabelSelector = sel
|
|
opts.Namespace = ClusterGatewaySecretNamespace
|
|
}
|
|
|
|
// ApplyToDeleteAllOf applies this configuration to the given a List options.
|
|
func (m MatchVirtualClusterLabels) ApplyToDeleteAllOf(opts *client.DeleteAllOfOptions) {
|
|
m.ApplyToList(&opts.ListOptions)
|
|
}
|
|
|
|
// ListVirtualClusters will get all registered clusters in control plane
|
|
func ListVirtualClusters(ctx context.Context, c client.Client) ([]VirtualCluster, error) {
|
|
clusters, err := FindVirtualClustersByLabels(ctx, c, map[string]string{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append([]VirtualCluster{*NewVirtualClusterFromLocal()}, clusters...), nil
|
|
}
|
|
|
|
// FindVirtualClustersByLabels will get all virtual clusters with matched labels in control plane
|
|
func FindVirtualClustersByLabels(ctx context.Context, c client.Client, labels map[string]string) ([]VirtualCluster, error) {
|
|
var clusters []VirtualCluster
|
|
secrets := corev1.SecretList{}
|
|
if err := c.List(ctx, &secrets, MatchVirtualClusterLabels(labels)); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to get clusterSecret secrets")
|
|
}
|
|
for _, secret := range secrets.Items {
|
|
vc, err := NewVirtualClusterFromSecret(secret.DeepCopy())
|
|
if err == nil {
|
|
clusters = append(clusters, *vc)
|
|
}
|
|
}
|
|
|
|
managedClusters := clusterv1.ManagedClusterList{}
|
|
if err := c.List(context.Background(), &managedClusters, client.MatchingLabels(labels)); err != nil && !velaerrors.IsCRDNotExists(err) {
|
|
return nil, errors.Wrapf(err, "failed to get managed clusters")
|
|
}
|
|
for _, managedCluster := range managedClusters.Items {
|
|
vc, err := NewVirtualClusterFromManagedCluster(managedCluster.DeepCopy())
|
|
if err == nil {
|
|
clusters = append(clusters, *vc)
|
|
}
|
|
}
|
|
return clusters, nil
|
|
}
|
|
|
|
// ClusterNameMapper mapper for cluster names
|
|
type ClusterNameMapper interface {
|
|
GetClusterName(string) string
|
|
}
|
|
|
|
type clusterAliasMapper map[string]string
|
|
|
|
// GetClusterName .
|
|
func (cm clusterAliasMapper) GetClusterName(cluster string) string {
|
|
if alias := strings.TrimSpace(cm[cluster]); alias != "" {
|
|
return fmt.Sprintf("%s (%s)", cluster, alias)
|
|
}
|
|
return cluster
|
|
}
|
|
|
|
// NewClusterNameMapper load all clusters and return the mapper of their names
|
|
func NewClusterNameMapper(ctx context.Context, c client.Client) (ClusterNameMapper, error) {
|
|
cm := clusterAliasMapper(make(map[string]string))
|
|
clusters := &prismclusterv1alpha1.ClusterList{}
|
|
if err := c.List(ctx, clusters); err == nil {
|
|
for _, cluster := range clusters.Items {
|
|
cm[cluster.Name] = cluster.Spec.Alias
|
|
}
|
|
return cm, nil
|
|
} else if err != nil && !meta.IsNoMatchError(err) && !runtime.IsNotRegisteredError(err) {
|
|
return nil, err
|
|
}
|
|
vcs, err := ListVirtualClusters(ctx, c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, cluster := range vcs {
|
|
cm[cluster.Name] = cluster.Alias
|
|
}
|
|
return cm, nil
|
|
}
|