mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-20 20:56:52 +00:00
Add TLS profile compliance to the gRPC server, completing TLS support for all hub components. The operator reads the ocm-tls-profile ConfigMap and injects --tls-min-version and --tls-cipher-suites flags into the gRPC server deployment, matching the pattern used by all other hub component deployments. Changes: - Add TLS flag injection to gRPC server deployment manifest - Wire TLS flags from common options to gRPC server via closure - Call ApplyTLSToCommand for the 8443 health server endpoint - Apply TLS overrides to the 8090 gRPC port via SDK ApplyTLSFlags - Update vendored sdk-go with CipherSuites support for gRPC server - Add unit, controller, and integration tests Assisted by Claude Signed-off-by: zhujian <jiazhu@redhat.com>
241 lines
6.9 KiB
Go
241 lines
6.9 KiB
Go
package grpc
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/openshift/library-go/pkg/controller/controllercmd"
|
|
"github.com/spf13/pflag"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
func TestNewGRPCServerOptions(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
|
|
if opts == nil {
|
|
t.Fatal("NewGRPCServerOptions() returned nil")
|
|
}
|
|
|
|
if opts.GRPCServerConfig != "" {
|
|
t.Errorf("Expected GRPCServerConfig to be empty by default, got %q", opts.GRPCServerConfig)
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsAddFlags(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
|
|
|
opts.AddFlags(fs)
|
|
|
|
// Test that server-config flag is added
|
|
serverConfigFlag := fs.Lookup("server-config")
|
|
if serverConfigFlag == nil {
|
|
t.Error("Expected server-config flag to be added")
|
|
}
|
|
|
|
// Test that flag has correct description
|
|
if serverConfigFlag.Usage != "Location of the server configuration file." {
|
|
t.Errorf("Expected flag usage to be 'Location of the server configuration file.', got %q", serverConfigFlag.Usage)
|
|
}
|
|
|
|
// Test setting the flag value
|
|
err := fs.Set("server-config", "/path/to/config.yaml")
|
|
if err != nil {
|
|
t.Errorf("Failed to set server-config flag: %v", err)
|
|
}
|
|
|
|
if opts.GRPCServerConfig != "/path/to/config.yaml" {
|
|
t.Errorf("Expected GRPCServerConfig to be '/path/to/config.yaml', got %q", opts.GRPCServerConfig)
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsStruct(t *testing.T) {
|
|
opts := &GRPCServerOptions{
|
|
GRPCServerConfig: "test-config.yaml",
|
|
}
|
|
|
|
if opts.GRPCServerConfig != "test-config.yaml" {
|
|
t.Error("GRPCServerConfig field not set correctly")
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsFlagTypes(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
|
|
|
opts.AddFlags(fs)
|
|
|
|
flag := fs.Lookup("server-config")
|
|
if flag == nil {
|
|
t.Fatal("server-config flag not found")
|
|
}
|
|
|
|
if flag.Value.Type() != "string" {
|
|
t.Errorf("Expected server-config flag to be string type, got %q", flag.Value.Type())
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsRunWithInvalidConfig(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
opts.GRPCServerConfig = "/nonexistent/path/to/config.yaml"
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
controllerContext := &controllercmd.ControllerContext{
|
|
KubeConfig: &rest.Config{Host: "https://example.com"},
|
|
}
|
|
|
|
// This should return an error because the config file doesn't exist
|
|
err := opts.Run(ctx, controllerContext)
|
|
if err == nil {
|
|
t.Error("Expected error when config file doesn't exist, but got none")
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsRunWithInvalidKubeConfig(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
// Invalid kubeconfig that should cause client creation to fail
|
|
controllerContext := &controllercmd.ControllerContext{
|
|
KubeConfig: &rest.Config{Host: "://invalid-url"},
|
|
}
|
|
|
|
// This should return an error because the kubeconfig is invalid
|
|
err := opts.Run(ctx, controllerContext)
|
|
if err == nil {
|
|
t.Error("Expected error when kubeconfig is invalid, but got none")
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsRunWithTLSOverride(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
tlsMinVersion string
|
|
tlsCipherSuites string
|
|
wantError bool
|
|
}{
|
|
{
|
|
name: "valid TLS 1.3 override",
|
|
tlsMinVersion: "VersionTLS13",
|
|
},
|
|
{
|
|
name: "valid TLS 1.2 override",
|
|
tlsMinVersion: "VersionTLS12",
|
|
},
|
|
{
|
|
name: "valid cipher suites override",
|
|
tlsCipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
},
|
|
{
|
|
name: "valid TLS 1.2 with cipher suites",
|
|
tlsMinVersion: "VersionTLS12",
|
|
tlsCipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
},
|
|
{
|
|
name: "invalid TLS version returns error",
|
|
tlsMinVersion: "BadVersion",
|
|
wantError: true,
|
|
},
|
|
{
|
|
name: "invalid cipher suite returns error",
|
|
tlsCipherSuites: "NOT_A_REAL_CIPHER",
|
|
wantError: true,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
opts := NewGRPCServerOptions()
|
|
opts.TLSMinVersionOverride = c.tlsMinVersion
|
|
opts.TLSCipherSuitesOverride = c.tlsCipherSuites
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
controllerContext := &controllercmd.ControllerContext{
|
|
KubeConfig: &rest.Config{Host: "https://example.com"},
|
|
}
|
|
|
|
err := opts.Run(ctx, controllerContext)
|
|
if c.wantError {
|
|
if err == nil {
|
|
t.Fatal("expected an error but got none")
|
|
}
|
|
if !strings.Contains(err.Error(), "TLS") && !strings.Contains(err.Error(), "tls") {
|
|
t.Errorf("expected TLS-related error, got: %v", err)
|
|
}
|
|
return
|
|
}
|
|
// For valid TLS overrides, Run() will proceed past TLS parsing but eventually
|
|
// fail due to missing certificates or kubeconfig — that's expected.
|
|
// We just verify it didn't fail on TLS parsing.
|
|
if err != nil && (strings.Contains(err.Error(), "failed to apply gRPC TLS overrides") || strings.Contains(err.Error(), "tls-min-version")) {
|
|
t.Errorf("unexpected TLS parsing error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGRPCServerOptionsRunWithValidConfigFile(t *testing.T) {
|
|
// Create a temporary config file for testing
|
|
tempDir := t.TempDir()
|
|
configFile := filepath.Join(tempDir, "grpc-config.yaml")
|
|
|
|
// Create temporary certificate files
|
|
certFile := filepath.Join(tempDir, "tls.crt")
|
|
keyFile := filepath.Join(tempDir, "tls.key")
|
|
|
|
// Create dummy certificate and key files
|
|
err := os.WriteFile(certFile, []byte("dummy-cert"), 0644)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test cert file: %v", err)
|
|
}
|
|
err = os.WriteFile(keyFile, []byte("dummy-key"), 0644)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test key file: %v", err)
|
|
}
|
|
|
|
// Create a valid GRPC server config with certificate paths
|
|
configContent := `
|
|
port: 0
|
|
grpc_certificate_file: ` + certFile + `
|
|
grpc_private_key_file: ` + keyFile + `
|
|
`
|
|
err = os.WriteFile(configFile, []byte(configContent), 0644)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test config file: %v", err)
|
|
}
|
|
|
|
opts := NewGRPCServerOptions()
|
|
opts.GRPCServerConfig = configFile
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
controllerContext := &controllercmd.ControllerContext{
|
|
KubeConfig: &rest.Config{Host: "https://example.com"},
|
|
}
|
|
|
|
// This should try to start the server but will likely fail due to invalid certificates
|
|
// We mainly want to test that it gets past the config loading phase
|
|
err = opts.Run(ctx, controllerContext)
|
|
|
|
// We expect this to fail, but it should be because of invalid certificates, not missing config
|
|
if err == nil {
|
|
t.Error("Expected error due to invalid certificates, but got none")
|
|
}
|
|
|
|
// The error should be related to certificate loading, not config file reading
|
|
if err != nil && !strings.Contains(err.Error(), "certificate") && !strings.Contains(err.Error(), "tls") {
|
|
t.Errorf("Expected certificate-related error, got: %v", err)
|
|
}
|
|
}
|