mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-02-14 10:00:11 +00:00
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
Signed-off-by: Yang Le <yangle@redhat.com>
440 lines
12 KiB
Go
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 := ®ister.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: ®istertesting.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: ®istertesting.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: ®istertesting.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: ®istertesting.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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|