diff --git a/pkg/agent/run.go b/pkg/agent/run.go index bef72cbe..6995c548 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -32,6 +32,7 @@ import ( "github.com/jetstack/preflight/api" "github.com/jetstack/preflight/pkg/client" + "github.com/jetstack/preflight/pkg/clusteruid" "github.com/jetstack/preflight/pkg/datagatherer" "github.com/jetstack/preflight/pkg/datagatherer/k8s" "github.com/jetstack/preflight/pkg/kubeconfig" @@ -78,6 +79,28 @@ func Run(cmd *cobra.Command, args []string) (returnErr error) { return fmt.Errorf("While evaluating configuration: %v", err) } + // We need the cluster UID before we progress further so it can be sent along with other data readings + + { + restCfg, err := kubeconfig.LoadRESTConfig("") + if err != nil { + return err + } + + clientset, err := kubernetes.NewForConfig(restCfg) + if err != nil { + return err + } + + ctx, err = clusteruid.GetClusterUID(ctx, clientset) + if err != nil { + return fmt.Errorf("failed to get cluster UID: %v", err) + } + + clusterUID := clusteruid.ClusterUIDFromContext(ctx) + log.V(logs.Debug).Info("Retrieved cluster UID", "clusterUID", clusterUID) + } + group, gctx := errgroup.WithContext(ctx) defer func() { cancel() diff --git a/pkg/clusteruid/clusteruid.go b/pkg/clusteruid/clusteruid.go new file mode 100644 index 00000000..2a5327f2 --- /dev/null +++ b/pkg/clusteruid/clusteruid.go @@ -0,0 +1,45 @@ +package clusteruid + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +// clusterUIDKey is the context key for storing the cluster UID +type clusterUIDKey struct{} + +// GetClusterUID retrieves the UID of the kube-system namespace using the given Kubernetes clientset. +// This UID can be used as a unique identifier for the Kubernetes cluster. +// The UID is stored in the given context for later retrieval; use ClusterUIDFromContext to get it. +func GetClusterUID(ctx context.Context, clientset kubernetes.Interface) (context.Context, error) { + namespace, err := clientset.CoreV1().Namespaces().Get(ctx, "kube-system", metav1.GetOptions{}) + if err != nil { + return ctx, err + } + + ctx = withClusterUID(ctx, string(namespace.ObjectMeta.UID)) + return ctx, nil +} + +// ClusterUIDFromContext retrieves the cluster UID from the context. +// Panics if the value is not found or if the value is not a string. +func ClusterUIDFromContext(ctx context.Context) string { + value := ctx.Value(clusterUIDKey{}) + if value == nil { + panic("cluster UID not found in context") + } + + uid, ok := value.(string) + if !ok { + panic("cluster UID in context is not a string") + } + + return uid +} + +// withClusterUID adds the given cluster UID to the context +func withClusterUID(ctx context.Context, clusterUID string) context.Context { + return context.WithValue(ctx, clusterUIDKey{}, clusterUID) +} diff --git a/pkg/clusteruid/clusteruid_test.go b/pkg/clusteruid/clusteruid_test.go new file mode 100644 index 00000000..15fe6be5 --- /dev/null +++ b/pkg/clusteruid/clusteruid_test.go @@ -0,0 +1,40 @@ +package clusteruid + +import ( + "testing" + + "k8s.io/client-go/kubernetes/fake" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestGetClusterUID(t *testing.T) { + client := fake.NewSimpleClientset() + + mockUID := "12345678-1234-5678-1234-567812345678" + + kubeSystemNS := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kube-system", + UID: types.UID(mockUID), + }, + } + + _, err := client.CoreV1().Namespaces().Create(t.Context(), kubeSystemNS, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("failed to create kube-system namespace with fake client: %v", err) + } + + ctx, err := GetClusterUID(t.Context(), client) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + uid := ClusterUIDFromContext(ctx) + + if uid != mockUID { + t.Fatalf("expected to get uid=%v, but got uid=%v", mockUID, uid) + } +}