Skip to content

Commit

Permalink
Store cluster metadata in kube-public/ace-info configmap (#587)
Browse files Browse the repository at this point in the history
Signed-off-by: Tamal Saha <tamal@appscode.com>
  • Loading branch information
tamalsaha authored Jun 11, 2024
1 parent 1c5d441 commit 3c1fd4e
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 38 deletions.
6 changes: 6 additions & 0 deletions api/v1/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const (
)

const (
AceInfoConfigMapName = "ace-info"

ClusterNameKey string = "cluster.appscode.com/name"
ClusterDisplayNameKey string = "cluster.appscode.com/display-name"
ClusterProviderNameKey string = "cluster.appscode.com/provider"
Expand All @@ -47,6 +49,10 @@ type ClusterMetadata struct {
Name string `json:"name,omitempty" protobuf:"bytes,2,opt,name=name"`
DisplayName string `json:"displayName,omitempty" protobuf:"bytes,3,opt,name=displayName"`
Provider HostingProvider `json:"provider,omitempty" protobuf:"bytes,4,opt,name=provider,casttype=HostingProvider"`
OwnerID string `json:"ownerID,omitempty"`
OwnerType string `json:"ownerType,omitempty"`
APIEndpoint string `json:"apiEndpoint,omitempty"`
CABundle string `json:"caBundle,omitempty"`
}

/*
Expand Down
37 changes: 37 additions & 0 deletions cluster/flag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright AppsCode Inc. and Contributors
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 cluster

import (
"flag"

"github.com/spf13/pflag"
)

var clusterName = ""

func AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&clusterName, "cluster-name", clusterName, "Name of cluster used in a multi-cluster setup")
}

func AddGoFlags(fs *flag.FlagSet) {
fs.StringVar(&clusterName, "cluster-name", clusterName, "Name of cluster used in a multi-cluster setup")
}

func ClusterName() string {
return clusterName
}
110 changes: 107 additions & 3 deletions cluster/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ package cluster

import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"

kmapi "kmodules.xyz/client-go/api/v1"
"kmodules.xyz/client-go/tools/clusterid"
cu "kmodules.xyz/client-go/client"

core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
Expand All @@ -42,12 +46,112 @@ func ClusterUID(c client.Reader) (string, error) {
}

func ClusterMetadata(c client.Reader) (*kmapi.ClusterMetadata, error) {
var cm core.ConfigMap
err := c.Get(context.TODO(), client.ObjectKey{Name: kmapi.AceInfoConfigMapName, Namespace: metav1.NamespacePublic}, &cm)
if err != nil {
return nil, err
}
result, err := ClusterMetadataForConfigMap(&cm)
if err == nil {
return result, nil
}

var ns core.Namespace
err := c.Get(context.TODO(), client.ObjectKey{Name: metav1.NamespaceSystem}, &ns)
err = c.Get(context.TODO(), client.ObjectKey{Name: metav1.NamespaceSystem}, &ns)
if err != nil {
return nil, err
}
return clusterid.ClusterMetadataForNamespace(&ns)
return LegacyClusterMetadataForNamespace(&ns)
}

func LegacyClusterMetadataForNamespace(ns *core.Namespace) (*kmapi.ClusterMetadata, error) {
if ns.Name != metav1.NamespaceSystem {
return nil, fmt.Errorf("expected namespace %s, found namespace %s", metav1.NamespaceSystem, ns.Name)
}
name := ns.Annotations[kmapi.ClusterNameKey]
if name == "" {
name = ClusterName()
}
md := &kmapi.ClusterMetadata{
UID: string(ns.UID),
Name: name,
DisplayName: ns.Annotations[kmapi.ClusterDisplayNameKey],
Provider: kmapi.HostingProvider(ns.Annotations[kmapi.ClusterProviderNameKey]),
}
return md, nil
}

func ClusterMetadataForConfigMap(cm *core.ConfigMap) (*kmapi.ClusterMetadata, error) {
if cm.Name != kmapi.AceInfoConfigMapName || cm.Namespace != metav1.NamespacePublic {
return nil, fmt.Errorf("expected configmap %s/%s, found %s/%s", metav1.NamespacePublic, kmapi.AceInfoConfigMapName, cm.Namespace, cm.Name)
}

md := &kmapi.ClusterMetadata{
UID: cm.Data["uid"],
Name: cm.Data["name"],
DisplayName: cm.Data["displayName"],
Provider: kmapi.HostingProvider(cm.Data["provider"]),
OwnerID: cm.Data["ownerID"],
OwnerType: cm.Data["ownerType"],
APIEndpoint: cm.Data["apiEndpoint"],
CABundle: cm.Data["ca.crt"],
}

data, err := json.Marshal(md)
if err != nil {
return nil, err
}
hasher := hmac.New(sha256.New, []byte(md.UID))
hasher.Write(data)
messageMAC := hasher.Sum(nil)
expectedMAC := cm.BinaryData["mac"]
if hmac.Equal(messageMAC, expectedMAC) {
return nil, fmt.Errorf("configmap %s/%s fails validation", cm.Namespace, cm.Name)
}

if md.Name == "" {
md.Name = ClusterName()
}
return md, nil
}

func UpsertClusterMetadata(kc client.Client, md *kmapi.ClusterMetadata) error {
obj := core.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: kmapi.AceInfoConfigMapName,
Namespace: metav1.NamespacePublic,
},
}

data, err := json.Marshal(md)
if err != nil {
return err
}
hasher := hmac.New(sha256.New, []byte(md.UID))
hasher.Write(data)
messageMAC := hasher.Sum(nil)

_, err = cu.CreateOrPatch(context.TODO(), kc, &obj, func(o client.Object, createOp bool) client.Object {
cm := o.(*core.ConfigMap)
if cm.Data == nil {
cm.Data = make(map[string]string)
}

cm.Data["uid"] = md.UID
cm.Data["name"] = md.Name
cm.Data["displayName"] = md.DisplayName
cm.Data["provider"] = string(md.Provider)
cm.Data["ownerID"] = md.OwnerID
cm.Data["ownerType"] = md.OwnerType
cm.Data["apiEndpoint"] = md.APIEndpoint
cm.Data["ca.crt"] = md.CABundle

cm.BinaryData = map[string][]byte{
"mac": messageMAC,
}
return cm
})
return err
}

func DetectCAPICluster(kc client.Client) (*kmapi.CAPIClusterInfo, error) {
Expand Down
46 changes: 11 additions & 35 deletions tools/clusterid/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,15 @@ package clusterid

import (
"context"
"flag"
"fmt"

kmapi "kmodules.xyz/client-go/api/v1"
clustermeta "kmodules.xyz/client-go/cluster"

"github.com/spf13/pflag"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)

var clusterName = ""

func AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&clusterName, "cluster-name", clusterName, "Name of cluster used in a multi-cluster setup")
}

func AddGoFlags(fs *flag.FlagSet) {
fs.StringVar(&clusterName, "cluster-name", clusterName, "Name of cluster used in a multi-cluster setup")
}

func ClusterName() string {
return clusterName
}

func ClusterUID(client corev1.NamespaceInterface) (string, error) {
ns, err := client.Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{})
if err != nil {
Expand All @@ -51,27 +35,19 @@ func ClusterUID(client corev1.NamespaceInterface) (string, error) {
return string(ns.UID), nil
}

func ClusterMetadataForNamespace(ns *core.Namespace) (*kmapi.ClusterMetadata, error) {
if ns.Name != metav1.NamespaceSystem {
return nil, fmt.Errorf("expected namespace %s, found namespace %s", metav1.NamespaceSystem, ns.Name)
}
name := ns.Annotations[kmapi.ClusterNameKey]
if name == "" {
name = ClusterName()
func ClusterMetadata(client kubernetes.Interface) (*kmapi.ClusterMetadata, error) {
cm, err := client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(context.TODO(), kmapi.AceInfoConfigMapName, metav1.GetOptions{})
if err != nil {
return nil, err
}
obj := &kmapi.ClusterMetadata{
UID: string(ns.UID),
Name: name,
DisplayName: ns.Annotations[kmapi.ClusterDisplayNameKey],
Provider: kmapi.HostingProvider(ns.Annotations[kmapi.ClusterProviderNameKey]),
result, err := clustermeta.ClusterMetadataForConfigMap(cm)
if err == nil {
return result, nil
}
return obj, nil
}

func ClusterMetadata(client corev1.NamespaceInterface) (*kmapi.ClusterMetadata, error) {
ns, err := client.Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{})
ns, err := client.CoreV1().Namespaces().Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{})
if err != nil {
return nil, err
}
return ClusterMetadataForNamespace(ns)
return clustermeta.LegacyClusterMetadataForNamespace(ns)
}

0 comments on commit 3c1fd4e

Please sign in to comment.