Files
open-cluster-management/pkg/registration/register/grpc/spoke_driver_test.go
Yang Le 9d1a993e2c
Some checks failed
Post / images (amd64, placement) (push) Failing after 50s
Post / images (amd64, registration) (push) Failing after 41s
Post / images (amd64, registration-operator) (push) Failing after 40s
Post / images (amd64, work) (push) Failing after 44s
Post / images (arm64, addon-manager) (push) Failing after 41s
Post / images (arm64, placement) (push) Failing after 42s
Post / images (arm64, registration) (push) Failing after 41s
Post / images (amd64, addon-manager) (push) Failing after 5m29s
Post / images (arm64, registration-operator) (push) Failing after 43s
Post / images (arm64, work) (push) Failing after 5m35s
Post / image manifest (addon-manager) (push) Has been skipped
Post / image manifest (placement) (push) Has been skipped
Post / image manifest (registration) (push) Has been skipped
Post / image manifest (registration-operator) (push) Has been skipped
Post / image manifest (work) (push) Has been skipped
Post / trigger clusteradm e2e (push) Has been skipped
Post / coverage (push) Failing after 40m4s
Scorecard supply-chain security / Scorecard analysis (push) Failing after 4m1s
add token driver for addon registration (#1343)
Signed-off-by: Yang Le <yangle@redhat.com>
2026-01-28 05:41:52 +00:00

440 lines
12 KiB
Go

package grpc
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
certificates "k8s.io/api/certificates/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
addonfake "open-cluster-management.io/api/client/addon/clientset/versioned/fake"
addoninformers "open-cluster-management.io/api/client/addon/informers/externalversions"
"open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc"
testinghelpers "open-cluster-management.io/ocm/pkg/registration/helpers/testing"
"open-cluster-management.io/ocm/pkg/registration/register"
"open-cluster-management.io/ocm/pkg/registration/register/csr"
registertesting "open-cluster-management.io/ocm/pkg/registration/register/testing"
"open-cluster-management.io/ocm/pkg/registration/register/token"
)
func TestIsHubKubeConfigValid(t *testing.T) {
tempDir, err := os.MkdirTemp("", "grpc-test-is-hub-kubeconfig-valid")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
defer os.RemoveAll(tempDir)
cert := testinghelpers.NewTestCert("system:open-cluster-management:cluster1:agent1", 60*time.Second)
cases := []struct {
name string
clusterName string
agentName string
tlsCert []byte
tlsKey []byte
valid bool
}{
{
name: "no cert",
valid: false,
},
{
name: "no key",
tlsCert: cert.Cert,
valid: false,
},
{
name: "cert is not issued for cluster1:agent1",
clusterName: "cluster2",
agentName: "agent2",
tlsCert: cert.Cert,
tlsKey: cert.Key,
valid: false,
},
{
name: "valid",
tlsCert: cert.Cert,
tlsKey: cert.Key,
valid: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if c.tlsCert != nil {
if err := os.WriteFile(filepath.Join(tempDir, "tls.crt"), c.tlsCert, 0600); err != nil {
t.Fatal(err)
}
}
if c.tlsKey != nil {
if err := os.WriteFile(filepath.Join(tempDir, "tls.key"), c.tlsKey, 0600); err != nil {
t.Fatal(err)
}
}
secretOption := register.SecretOption{
ClusterName: c.clusterName,
AgentName: c.agentName,
HubKubeconfigDir: tempDir,
}
driver, err := NewGRPCDriver(nil, csr.NewCSROption(), secretOption)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
valid, err := driver.IsHubKubeConfigValid(context.Background(), secretOption)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if valid != c.valid {
t.Errorf("expected valid: %v, got: %v", c.valid, valid)
}
})
}
}
func TestLoadConfig(t *testing.T) {
tempDir, err := os.MkdirTemp("", "grpc-test-load-config")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
defer os.RemoveAll(tempDir)
configFile := filepath.Join(tempDir, "config.yaml")
if err := os.WriteFile(configFile, []byte(`url: "https://localhost:8443"`), 0600); err != nil {
t.Fatal(err)
}
cases := []struct {
name string
bootstrapped bool
bootstrapConfigFile string
configFile string
expectedErr bool
}{
{
name: "no bootstrap config file",
bootstrapped: true,
expectedErr: true,
},
{
name: "no config file",
bootstrapped: false,
expectedErr: true,
},
{
name: "load bootstrap config",
bootstrapped: true,
bootstrapConfigFile: configFile,
expectedErr: false,
},
{
name: "load bootstrap config",
bootstrapped: false,
configFile: configFile,
expectedErr: false,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
driver := &GRPCDriver{
opt: &Option{
BootstrapConfigFile: c.bootstrapConfigFile,
ConfigFile: c.configFile,
},
}
config, configData, err := driver.loadConfig(register.SecretOption{}, c.bootstrapped)
if c.expectedErr {
if err == nil {
t.Errorf("expected error, but failed")
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if _, ok := config.(*grpc.GRPCOptions); !ok {
t.Errorf("expected config to be a *grpc.GRPCOptions, got: %T", config)
}
if len(configData) == 0 {
t.Errorf("expected config data, but got empty")
}
})
}
}
func TestGRPCDriver_Fork_TokenAuth(t *testing.T) {
// Setup addon client and informer
addon := &addonv1alpha1.ManagedClusterAddOn{
ObjectMeta: metav1.ObjectMeta{
Name: "addon1",
Namespace: "cluster1",
},
}
addonClient := addonfake.NewSimpleClientset(addon)
addonInformerFactory := addoninformers.NewSharedInformerFactory(addonClient, 10*time.Minute)
addonInformer := addonInformerFactory.Addon().V1alpha1().ManagedClusterAddOns()
addonClients := &register.AddOnClients{
AddonClient: addonClient,
AddonInformer: addonInformer,
}
tests := []struct {
name string
setupDriver func() *GRPCDriver
addonName string
secretOption register.SecretOption
regOption register.AddonAuthConfig
expectErr bool
expectErrMsg string
validateResult func(t *testing.T, driver register.RegisterDriver)
}{
{
name: "token auth - success",
setupDriver: func() *GRPCDriver {
tokenControl := &grpcTokenControl{}
csrDriver := &csr.CSRDriver{}
csrDriver.SetTokenControl(tokenControl)
csrDriver.SetAddonClients(addonClients)
driver := &GRPCDriver{
tokenControl: tokenControl,
addonClients: addonClients,
csrDriver: csrDriver,
}
return driver
},
addonName: "addon1",
secretOption: register.SecretOption{
ClusterName: "cluster1",
Signer: certificates.KubeAPIServerClientSignerName,
},
regOption: &registertesting.TestAddonAuthConfig{
KubeClientAuth: "token",
TokenOption: token.NewTokenOption(),
},
expectErr: false,
validateResult: func(t *testing.T, driver register.RegisterDriver) {
if _, ok := driver.(*token.TokenDriver); !ok {
t.Errorf("expected TokenDriver, got %T", driver)
}
},
},
{
name: "token auth - invalid token option",
setupDriver: func() *GRPCDriver {
tokenControl := &grpcTokenControl{}
csrDriver := &csr.CSRDriver{}
csrDriver.SetTokenControl(tokenControl)
csrDriver.SetAddonClients(addonClients)
driver := &GRPCDriver{
tokenControl: tokenControl,
addonClients: addonClients,
csrDriver: csrDriver,
}
return driver
},
addonName: "addon1",
secretOption: register.SecretOption{
ClusterName: "cluster1",
Signer: certificates.KubeAPIServerClientSignerName,
},
regOption: &registertesting.TestAddonAuthConfig{
KubeClientAuth: "token",
TokenOption: nil,
},
expectErr: true,
expectErrMsg: "token authentication requested but TokenConfiguration is nil",
},
{
name: "token auth - tokenControl not initialized",
setupDriver: func() *GRPCDriver {
csrDriver := &csr.CSRDriver{}
csrDriver.SetTokenControl(nil)
csrDriver.SetAddonClients(addonClients)
driver := &GRPCDriver{
tokenControl: nil,
addonClients: addonClients,
csrDriver: csrDriver,
}
return driver
},
addonName: "addon1",
secretOption: register.SecretOption{
ClusterName: "cluster1",
Signer: certificates.KubeAPIServerClientSignerName,
},
regOption: &registertesting.TestAddonAuthConfig{
KubeClientAuth: "token",
TokenOption: token.NewTokenOption(),
},
expectErr: true,
expectErrMsg: "tokenControl is not initialized",
},
{
name: "token auth - addonClients not initialized",
setupDriver: func() *GRPCDriver {
tokenControl := &grpcTokenControl{}
csrDriver := &csr.CSRDriver{}
csrDriver.SetTokenControl(tokenControl)
csrDriver.SetAddonClients(nil)
driver := &GRPCDriver{
tokenControl: tokenControl,
addonClients: nil,
csrDriver: csrDriver,
}
return driver
},
addonName: "addon1",
secretOption: register.SecretOption{
ClusterName: "cluster1",
Signer: certificates.KubeAPIServerClientSignerName,
},
regOption: &registertesting.TestAddonAuthConfig{
KubeClientAuth: "token",
TokenOption: token.NewTokenOption(),
},
expectErr: true,
expectErrMsg: "addonClients is not initialized",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
driver := tt.setupDriver()
forkedDriver, err := driver.Fork(tt.addonName, tt.regOption, tt.secretOption)
if tt.expectErr {
if err == nil {
t.Errorf("expected error but got nil")
return
}
if tt.expectErrMsg != "" && !strings.Contains(err.Error(), tt.expectErrMsg) {
t.Errorf("expected error message to contain %q, got %q", tt.expectErrMsg, err.Error())
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if tt.validateResult != nil {
tt.validateResult(t, forkedDriver)
}
})
}
}
func TestGRPCDriver_BuildClients_InitializesTokenControl(t *testing.T) {
// This test verifies that BuildClients properly initializes tokenControl
// We can't fully test BuildClients without a real gRPC server, so we just
// verify the structure is correct
tempDir, err := os.MkdirTemp("", "grpc-test-build-clients")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(tempDir)
configFile := filepath.Join(tempDir, "config.yaml")
if err := os.WriteFile(configFile, []byte(`url: "https://localhost:8443"`), 0600); err != nil {
t.Fatal(err)
}
kubeconfig := testinghelpers.NewKubeconfig("cluster1", "https://127.0.0.1:6001", "", "", nil, nil, nil)
kubeconfigFile := filepath.Join(tempDir, "kubeconfig")
if err := os.WriteFile(kubeconfigFile, kubeconfig, 0600); err != nil {
t.Fatal(err)
}
secretOption := register.SecretOption{
ClusterName: "cluster1",
AgentName: "agent1",
HubKubeconfigFile: kubeconfigFile,
BootStrapKubeConfigFile: kubeconfigFile,
}
driverInterface, err := NewGRPCDriver(&Option{
ConfigFile: configFile,
BootstrapConfigFile: configFile,
}, csr.NewCSROption(), secretOption)
if err != nil {
t.Fatalf("failed to create GRPC driver: %v", err)
}
// Type assert to access internal fields for verification
driver, ok := driverInterface.(*GRPCDriver)
if !ok {
t.Fatalf("expected *GRPCDriver, got %T", driverInterface)
}
// Verify driver was created with expected structure
if driver.csrDriver == nil {
t.Error("csrDriver should be initialized")
}
if driver.opt == nil {
t.Error("opt should be initialized")
}
}
func TestGRPCTokenControl_CreateToken(t *testing.T) {
tests := []struct {
name string
saClient interface{}
expectErr bool
expectErrContains string
}{
{
name: "saClient is nil",
saClient: nil,
expectErr: true,
expectErrContains: "ServiceAccount client is not initialized",
},
{
name: "saClient is set but we can't actually call it without a real server",
saClient: &struct{}{}, // Mock object, will fail when we try to use it
expectErr: false, // We just test that nil check passes
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := &grpcTokenControl{}
if tt.saClient != nil {
// We can't easily test the actual CreateToken call without a real gRPC server,
// so we just verify the nil check works
if ctrl.saClient == nil && tt.saClient == nil {
_, err := ctrl.CreateToken(context.Background(), "test-sa", "test-ns", 3600)
if !tt.expectErr {
t.Errorf("expected no error, got: %v", err)
}
if err != nil && !strings.Contains(err.Error(), tt.expectErrContains) {
t.Errorf("expected error to contain %q, got: %v", tt.expectErrContains, err)
}
}
} else {
_, err := ctrl.CreateToken(context.Background(), "test-sa", "test-ns", 3600)
if !tt.expectErr {
t.Errorf("expected no error, got: %v", err)
}
if err != nil && !strings.Contains(err.Error(), tt.expectErrContains) {
t.Errorf("expected error to contain %q, got: %v", tt.expectErrContains, err)
}
}
})
}
}