Merge pull request #5559 from mohamedawnallah/unitTestOperatorCertsPkg
operator/pkg/certs: unit test Certificate Manager and Karmada Store
This commit is contained in:
commit
d172659ff5
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
Copyright 2024 The Karmada Authors.
|
||||
|
||||
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 certs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
|
||||
"github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
|
||||
"github.com/karmada-io/karmada/operator/pkg/constants"
|
||||
"github.com/karmada-io/karmada/operator/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedAPIServerAltDNSNames = []string{
|
||||
"localhost",
|
||||
"kubernetes",
|
||||
"kubernetes.default",
|
||||
"kubernetes.default.svc",
|
||||
fmt.Sprintf("*.%s.svc.cluster.local", constants.KarmadaSystemNamespace),
|
||||
fmt.Sprintf("*.%s.svc", constants.KarmadaSystemNamespace),
|
||||
}
|
||||
expectedAPIServerAltIPs = []net.IP{net.IPv4(127, 0, 0, 1)}
|
||||
)
|
||||
|
||||
func TestCertConfig_defaultPublicKeyAlgorithm(t *testing.T) {
|
||||
c := &CertConfig{}
|
||||
c.defaultPublicKeyAlgorithm()
|
||||
|
||||
if c.PublicKeyAlgorithm != x509.RSA {
|
||||
t.Errorf("expected PublicKeyAlgorithm to be RSA, got %v", c.PublicKeyAlgorithm)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertConfig_defaultNotAfter(t *testing.T) {
|
||||
c := &CertConfig{}
|
||||
c.defaultNotAfter()
|
||||
|
||||
if c.NotAfter == nil {
|
||||
t.Error("expected NotAfter to be set, but it was nil")
|
||||
}
|
||||
|
||||
if !c.NotAfter.After(time.Now()) {
|
||||
t.Errorf("expected NotAfter to be a future time, got %v", c.NotAfter)
|
||||
}
|
||||
|
||||
expectedTime := time.Now().Add(constants.CertificateValidity).UTC()
|
||||
if c.NotAfter.Sub(expectedTime) > time.Minute {
|
||||
t.Errorf("NotAfter time is too far from expected, got %v, expected %v", c.NotAfter, expectedTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertRootCA(t *testing.T) {
|
||||
certConfig := KarmadaCertRootCA()
|
||||
|
||||
expectedCommonName := "karmada"
|
||||
|
||||
if certConfig.Name != constants.CaCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.CaCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertAdmin(t *testing.T) {
|
||||
certConfig := KarmadaCertAdmin()
|
||||
|
||||
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{}, certConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedCommonName := "system:admin"
|
||||
expectedOrganization := []string{"system:masters"}
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
||||
|
||||
if certConfig.Name != constants.KarmadaCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.KarmadaCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.CaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Organization, expectedOrganization) {
|
||||
t.Errorf("expected Organization to contain %v, got %v", expectedOrganization, certConfig.Config.Organization)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth for mutual TLS, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
||||
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedAPIServerAltIPs) {
|
||||
t.Errorf("expected IPs to contain %v, got %v", expectedAPIServerAltIPs, certConfig.Config.AltNames.IPs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertApiserver(t *testing.T) {
|
||||
certConfig := KarmadaCertApiserver()
|
||||
|
||||
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{}, certConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedCommonName := "karmada-apiserver"
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
||||
|
||||
if certConfig.Name != constants.ApiserverCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.ApiserverCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.CaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
||||
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedAPIServerAltIPs) {
|
||||
t.Errorf("expected IPs to contain %v, got %v", expectedAPIServerAltIPs, certConfig.Config.AltNames.IPs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertClient(t *testing.T) {
|
||||
newControlPlaneAddress := "192.168.1.101"
|
||||
newCertSAN := "10.0.0.15"
|
||||
|
||||
certConfig := KarmadaCertClient()
|
||||
|
||||
err := certConfig.AltNamesMutatorFunc(&AltNamesMutatorConfig{
|
||||
ControlplaneAddress: newControlPlaneAddress,
|
||||
Components: &v1alpha1.KarmadaComponents{
|
||||
KarmadaAPIServer: &v1alpha1.KarmadaAPIServer{
|
||||
CertSANs: []string{newCertSAN},
|
||||
},
|
||||
},
|
||||
}, certConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedCertName := "karmada-client"
|
||||
expectedCommonName := "system:admin"
|
||||
expectedOrganization := []string{"system:masters"}
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
||||
|
||||
newIPs := []net.IP{net.ParseIP(newControlPlaneAddress), net.ParseIP(newCertSAN)}
|
||||
expectedNewIPs := append(newIPs, expectedAPIServerAltIPs...)
|
||||
|
||||
if certConfig.Name != expectedCertName {
|
||||
t.Errorf("expected Name to be %s, got %s", expectedCertName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.CaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.CaCertAndKeyName, certConfig.CAName)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Organization, expectedOrganization) {
|
||||
t.Errorf("expected Organization to contain %v, got %v", expectedOrganization, certConfig.Config.Organization)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedAPIServerAltDNSNames) {
|
||||
t.Errorf("expected DNSNames to contain %v, got %v", expectedAPIServerAltDNSNames, certConfig.Config.AltNames.DNSNames)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedNewIPs) {
|
||||
t.Errorf("expected IPs to contain %v, got %v", expectedNewIPs, certConfig.Config.AltNames.IPs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertFrontProxyCA(t *testing.T) {
|
||||
certConfig := KarmadaCertFrontProxyCA()
|
||||
|
||||
expectedCommonName := "front-proxy-ca"
|
||||
|
||||
if certConfig.Name != constants.FrontProxyCaCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.FrontProxyCaCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertFrontProxyClient(t *testing.T) {
|
||||
certConfig := KarmadaCertFrontProxyClient()
|
||||
|
||||
expectedCommonName := "front-proxy-client"
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
|
||||
|
||||
if certConfig.Name != constants.FrontProxyClientCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.FrontProxyClientCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.FrontProxyCaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.FrontProxyCaCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertEtcdCA(t *testing.T) {
|
||||
certConfig := KarmadaCertEtcdCA()
|
||||
|
||||
expectedCommonName := "karmada-etcd-ca"
|
||||
|
||||
if certConfig.Name != constants.EtcdCaCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertEtcdServer(t *testing.T) {
|
||||
certConfig := KarmadaCertEtcdServer()
|
||||
|
||||
cfg := &AltNamesMutatorConfig{Namespace: constants.KarmadaSystemNamespace}
|
||||
err := certConfig.AltNamesMutatorFunc(cfg, certConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("AltNamesMutatorFunc() returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedCommonName := "karmada-etcd-server"
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
||||
expectedDNSNames := []string{
|
||||
"localhost",
|
||||
fmt.Sprintf("%s.%s.svc.cluster.local", util.KarmadaEtcdClientName(cfg.Name), cfg.Namespace),
|
||||
fmt.Sprintf("*.%s.%s.svc.cluster.local", util.KarmadaEtcdName(cfg.Name), cfg.Namespace),
|
||||
}
|
||||
expectedIPs := []net.IP{net.IPv4(127, 0, 0, 1)}
|
||||
|
||||
if certConfig.Name != constants.EtcdServerCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.EtcdServerCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.EtcdCaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.CAName)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.DNSNames, expectedDNSNames) {
|
||||
t.Errorf("expected DNSNames to contain %v, got %v", expectedDNSNames, certConfig.Config.AltNames.DNSNames)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.AltNames.IPs, expectedIPs) {
|
||||
t.Errorf("expected IPs to contain %v, got %v", expectedIPs, certConfig.Config.AltNames.IPs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKarmadaCertEtcdClient(t *testing.T) {
|
||||
certConfig := KarmadaCertEtcdClient()
|
||||
|
||||
expectedCommonName := "karmada-etcd-client"
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}
|
||||
|
||||
if certConfig.Name != constants.EtcdClientCertAndKeyName {
|
||||
t.Errorf("expected Name to be %s, got %s", constants.EtcdClientCertAndKeyName, certConfig.Name)
|
||||
}
|
||||
|
||||
if certConfig.CAName != constants.EtcdCaCertAndKeyName {
|
||||
t.Errorf("expected CAName to be %s, got %s", constants.EtcdCaCertAndKeyName, certConfig.CAName)
|
||||
}
|
||||
|
||||
if certConfig.Config.CommonName != expectedCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCommonName, certConfig.Config.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(certConfig.Config.Usages, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth and ExtKeyUsageClientAuth, got %v", certConfig.Config.Usages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratePrivateKey_ECDSAKey_ShouldReturnValidECDSAKey(t *testing.T) {
|
||||
key, err := GeneratePrivateKey(x509.ECDSA)
|
||||
if err != nil {
|
||||
t.Fatalf("GeneratePrivateKey() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Check that the key is of type *ecdsa.PrivateKey.
|
||||
if _, ok := key.(*ecdsa.PrivateKey); !ok {
|
||||
t.Errorf("GeneratePrivateKey() returned key of type %T, expected *ecdsa.PrivateKey", key)
|
||||
}
|
||||
|
||||
// Verify that the elliptic curve is P-256 (secp256r1).
|
||||
ecdsaKey := key.(*ecdsa.PrivateKey)
|
||||
if ecdsaKey.Curve != elliptic.P256() {
|
||||
t.Errorf("GeneratePrivateKey() returned ECDSA key with elliptic curve %v, expected P-256", ecdsaKey.Curve)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratePrivateKey_RSAKey_ShouldReturnValidRSAKey(t *testing.T) {
|
||||
expectedRSAKeySizeInBytes := rsaKeySize / 8
|
||||
|
||||
key, err := GeneratePrivateKey(x509.RSA)
|
||||
if err != nil {
|
||||
t.Fatalf("GeneratePrivateKey() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Check that the key is of type *rsa.PrivateKey.
|
||||
if _, ok := key.(*rsa.PrivateKey); !ok {
|
||||
t.Errorf("GeneratePrivateKey() returned key of type %T, expected *rsa.PrivateKey", key)
|
||||
}
|
||||
|
||||
// Verify that the key size is correct.
|
||||
rsaKey := key.(*rsa.PrivateKey)
|
||||
if rsaKey.Size() != expectedRSAKeySizeInBytes {
|
||||
t.Errorf("GeneratePrivateKey() returned RSA key of size %d, expected %d", rsaKey.Size()*8, rsaKeySize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratePrivateKey_InvalidKeyType_ShouldReturnError(t *testing.T) {
|
||||
_, err := GeneratePrivateKey(x509.DSA)
|
||||
if err == nil {
|
||||
t.Error("GeneratePrivateKey() expected error for unsupported key type 'DSA', got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeCertPEM_CorrectEncoding(t *testing.T) {
|
||||
cert := &x509.Certificate{
|
||||
Raw: []byte("dummy_certificate_data"),
|
||||
}
|
||||
|
||||
// Base64 encoding of the dummy certificate data.
|
||||
base64EncodedCert := base64.StdEncoding.EncodeToString(cert.Raw)
|
||||
|
||||
// Construct the expected PEM format.
|
||||
expectedPEM := fmt.Sprintf(
|
||||
"-----BEGIN %s-----\n%s\n-----END %s-----\n",
|
||||
CertificateBlockType,
|
||||
base64EncodedCert,
|
||||
CertificateBlockType,
|
||||
)
|
||||
got := EncodeCertPEM(cert)
|
||||
|
||||
// Check if the encoded PEM matches the expected PEM format.
|
||||
if !bytes.Equal(got, []byte(expectedPEM)) {
|
||||
t.Errorf("EncodeCertPEM() = %s; want %s", got, expectedPEM)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCertificateAuthority(t *testing.T) {
|
||||
cc := &CertConfig{
|
||||
Name: "test-ca",
|
||||
CAName: "test-ca",
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
Config: certutil.Config{
|
||||
CommonName: "test-ca",
|
||||
Organization: []string{"test-org"},
|
||||
},
|
||||
}
|
||||
|
||||
cert, err := NewCertificateAuthority(cc)
|
||||
if err != nil {
|
||||
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
||||
}
|
||||
|
||||
if cert == nil {
|
||||
t.Fatal("NewCertificateAuthority() returned nil cert")
|
||||
}
|
||||
|
||||
if cert.pairName != cc.Name {
|
||||
t.Errorf("expected pairName to be %s, got %s", cc.Name, cert.pairName)
|
||||
}
|
||||
|
||||
if cert.caName != cc.CAName {
|
||||
t.Errorf("expected caName to be %s, got %s", cc.CAName, cert.caName)
|
||||
}
|
||||
|
||||
if cert.cert == nil {
|
||||
t.Error("expected cert to be non-nil")
|
||||
}
|
||||
|
||||
if cert.key == nil {
|
||||
t.Error("expected key to be non-nil")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(cert.cert)
|
||||
if block == nil || block.Type != CertificateBlockType {
|
||||
t.Errorf("expected PEM block type to be %s, got %v", CertificateBlockType, block)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePrivateKeyPEM_ValidRSAKey(t *testing.T) {
|
||||
key, err := GeneratePrivateKey(x509.RSA)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate RSA key: %v", err)
|
||||
}
|
||||
|
||||
keyPEM, err := keyutil.MarshalPrivateKeyToPEM(key)
|
||||
if err != nil {
|
||||
t.Errorf("unable to marshal private key to PEM, err: %v", err)
|
||||
}
|
||||
|
||||
signer, err := ParsePrivateKeyPEM(keyPEM)
|
||||
if err != nil {
|
||||
t.Fatalf("ParsePrivateKeyPEM() returned an error: %v", err)
|
||||
}
|
||||
|
||||
if _, ok := signer.(*rsa.PrivateKey); !ok {
|
||||
t.Errorf("ParsePrivateKeyPEM() returned key of type %T, expected *rsa.PrivateKey", signer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePrivateKeyPEM_InvalidKeyType(t *testing.T) {
|
||||
invalidKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "INVALID PRIVATE KEY",
|
||||
Bytes: []byte("invalid"),
|
||||
})
|
||||
|
||||
signer, err := ParsePrivateKeyPEM(invalidKeyPEM)
|
||||
if err == nil {
|
||||
t.Errorf("ParsePrivateKeyPEM() expected error for unsupported key type, got nil")
|
||||
}
|
||||
if signer != nil {
|
||||
t.Errorf("ParsePrivateKeyPEM() expected nil signer, got %v", signer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCertAndKeyFilesWithCA(t *testing.T) {
|
||||
caName, caCommonName := "test-ca", "test-ca"
|
||||
caCertConfig := &CertConfig{
|
||||
Name: caName,
|
||||
CAName: caName,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
Config: certutil.Config{
|
||||
CommonName: caCommonName,
|
||||
Organization: []string{"test-org"},
|
||||
},
|
||||
}
|
||||
|
||||
caCert, err := NewCertificateAuthority(caCertConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
||||
}
|
||||
|
||||
certName, certCommonName := "test-cert", "test-common-name"
|
||||
certConfig := &CertConfig{
|
||||
Name: certName,
|
||||
CAName: caName,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
Config: certutil.Config{
|
||||
CommonName: certCommonName,
|
||||
Organization: []string{"test-org"},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
},
|
||||
}
|
||||
|
||||
cert, err := CreateCertAndKeyFilesWithCA(certConfig, caCert.CertData(), caCert.KeyData())
|
||||
if err != nil {
|
||||
t.Fatalf("CreateCertAndKeyFilesWithCA() returned an error: %v", err)
|
||||
}
|
||||
|
||||
if cert == nil {
|
||||
t.Fatal("CreateCertAndKeyFilesWithCA() returned nil cert")
|
||||
}
|
||||
|
||||
if cert.cert == nil || cert.key == nil {
|
||||
t.Error("Expected cert and key to be non-nil")
|
||||
}
|
||||
|
||||
if cert.pairName != certConfig.Name {
|
||||
t.Errorf("expected pairName to be %s, got %s", certConfig.Name, cert.pairName)
|
||||
}
|
||||
|
||||
if cert.caName != certConfig.CAName {
|
||||
t.Errorf("expected caName to be %s, got %s", certConfig.CAName, cert.caName)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(cert.cert)
|
||||
if block == nil || block.Type != CertificateBlockType {
|
||||
t.Errorf("expected PEM block type to be %s, got %v", CertificateBlockType, block)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSignedCert_Success(t *testing.T) {
|
||||
// Create a CA certificate.
|
||||
caName, caCommonName := "test-ca", "test-ca"
|
||||
caCertConfig := &CertConfig{
|
||||
Name: caName,
|
||||
CAName: caName,
|
||||
PublicKeyAlgorithm: x509.RSA,
|
||||
Config: certutil.Config{
|
||||
CommonName: caCommonName,
|
||||
Organization: []string{"test-org"},
|
||||
},
|
||||
}
|
||||
|
||||
caKarmadaCert, err := NewCertificateAuthority(caCertConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("NewCertificateAuthority() returned an error: %v", err)
|
||||
}
|
||||
|
||||
caCerts, err := certutil.ParseCertsPEM(caKarmadaCert.CertData())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
caCert := caCerts[0]
|
||||
|
||||
caKey, err := ParsePrivateKeyPEM(caKarmadaCert.key)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Create a key pair for the certificate.
|
||||
key, err := GeneratePrivateKey(x509.RSA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
// Create a CertConfig for the test certificate.
|
||||
expectedCertCommonName := "test-cert"
|
||||
expectedCertOrganization := []string{"test-org"}
|
||||
expectedCertDNSNames := []string{"localhost"}
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
||||
certNotAfter := time.Now().Add(constants.CertificateValidity)
|
||||
cc := &CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: expectedCertCommonName,
|
||||
Organization: expectedCertOrganization,
|
||||
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
|
||||
Usages: expectedUsages,
|
||||
},
|
||||
NotAfter: &certNotAfter,
|
||||
}
|
||||
|
||||
cert, err := NewSignedCert(cc, key, caCert, caKey, false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSignedCert returned an error: %v", err)
|
||||
}
|
||||
|
||||
if cert.IsCA {
|
||||
t.Errorf("expected certificate to not be a CA, but it was")
|
||||
}
|
||||
|
||||
if cert.Subject.CommonName != expectedCertCommonName {
|
||||
t.Errorf("expected CommonName to be %s, got %s", expectedCertCommonName, cert.Subject.CommonName)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(cert.Subject.Organization, expectedCertOrganization) {
|
||||
t.Errorf("expected Organization to contain %v, got %v", expectedCertOrganization, cert.Subject.Organization)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(cert.DNSNames, expectedCertDNSNames) {
|
||||
t.Errorf("expected DNSNames to contain %v, got %v", expectedCertDNSNames, cert.DNSNames)
|
||||
}
|
||||
|
||||
if !util.ContainsAllValues(cert.ExtKeyUsage, expectedUsages) {
|
||||
t.Errorf("expected Usages to contain ExtKeyUsageServerAuth, got %v", cert.ExtKeyUsage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSignedCert_ErrorOnEmptyCommonName(t *testing.T) {
|
||||
// Create a key pair for the certificate and CA.
|
||||
key, err := GeneratePrivateKey(x509.RSA)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", err)
|
||||
}
|
||||
|
||||
// Create a CertConfig for the test certificate.
|
||||
expectedCertOrganization := []string{"test-org"}
|
||||
expectedCertDNSNames := []string{"localhost"}
|
||||
expectedUsages := []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
|
||||
certNotAfter := time.Now().Add(constants.CertificateValidity)
|
||||
cc := &CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: "",
|
||||
Organization: expectedCertOrganization,
|
||||
AltNames: certutil.AltNames{DNSNames: expectedCertDNSNames},
|
||||
Usages: expectedUsages,
|
||||
},
|
||||
NotAfter: &certNotAfter,
|
||||
}
|
||||
|
||||
_, err = NewSignedCert(cc, key, &x509.Certificate{}, key, false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for empty CommonName, but got nil")
|
||||
}
|
||||
|
||||
expectedErrorMsg := "must specify a CommonName"
|
||||
if err.Error() != expectedErrorMsg {
|
||||
t.Errorf("expected error %s, got %v", expectedErrorMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendSANsToAltNames(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
SANs []string
|
||||
wantIPs []net.IP
|
||||
wantDNS []string
|
||||
}{
|
||||
{
|
||||
name: "AppendSANsToAltNames_ValidIPAddress_ShouldReturnCorrectIPs",
|
||||
SANs: []string{"192.168.1.1"},
|
||||
wantIPs: []net.IP{net.ParseIP("192.168.1.1")},
|
||||
wantDNS: nil,
|
||||
},
|
||||
{
|
||||
name: "AppendSANsToAltNames_ValidDNSName_ShouldReturnCorrectDNS",
|
||||
SANs: []string{"example.com"},
|
||||
wantIPs: nil,
|
||||
wantDNS: []string{"example.com"},
|
||||
},
|
||||
{
|
||||
name: "AppendSANsToAltNames_InvalidDNSAndValidIP_ShouldIgnoreInvalidDNSAndReturnCorrectIP",
|
||||
SANs: []string{"invalid!.com", "10.0.0.1"},
|
||||
wantIPs: []net.IP{net.ParseIP("10.0.0.1")},
|
||||
wantDNS: nil,
|
||||
},
|
||||
{
|
||||
name: "AppendSANsToAltNames_ValidWildcardDNS_ShouldReturnCorrectWildcardDNS",
|
||||
SANs: []string{"*.example.com"},
|
||||
wantIPs: nil,
|
||||
wantDNS: []string{"*.example.com"},
|
||||
},
|
||||
{
|
||||
name: "AppendSANsToAltNames_MixedValidIPAndDNS_ShouldReturnCorrectIPAndDNS",
|
||||
SANs: []string{"example.com", "10.0.0.1"},
|
||||
wantIPs: []net.IP{net.ParseIP("10.0.0.1")},
|
||||
wantDNS: []string{"example.com"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
altNames := &certutil.AltNames{}
|
||||
appendSANsToAltNames(altNames, tt.SANs)
|
||||
|
||||
if !reflect.DeepEqual(altNames.IPs, tt.wantIPs) {
|
||||
t.Errorf("AppendSANsToAltNames() IPs = %+v, want %+v", altNames.IPs, tt.wantIPs)
|
||||
}
|
||||
if !reflect.DeepEqual(altNames.DNSNames, tt.wantDNS) {
|
||||
t.Errorf("AppendSANsToAltNames() DNS = %+v, want %+v", altNames.IPs, tt.wantIPs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveDuplicateAltNames(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *certutil.AltNames
|
||||
want *certutil.AltNames
|
||||
}{
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_DuplicateDNSNames_ShouldReturnUniqueDNSNames",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_DuplicateIPs_ShouldReturnUniqueIPs",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1")},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_DuplicateDNSNamesAndIPs_ShouldReturnUniqueDNSNamesAndIPs",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_NoDuplicates_ShouldReturnSameAltNames",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.org"},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.2")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_EmptyAltNames_ShouldReturnEmptyAltNames",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{},
|
||||
IPs: []net.IP{},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{},
|
||||
IPs: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_NilAltNames_ShouldReturnNil",
|
||||
input: nil,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_EmptyDNSNamesWithNonEmptyIPs_ShouldReturnUniqueIPs",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.1.1")},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{},
|
||||
IPs: []net.IP{net.ParseIP("192.168.1.1")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RemoveDuplicateAltNames_EmptyIPsWithNonEmptyDNSNames_ShouldReturnUniqueDNSNames",
|
||||
input: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com", "example.com"},
|
||||
IPs: []net.IP{},
|
||||
},
|
||||
want: &certutil.AltNames{
|
||||
DNSNames: []string{"example.com"},
|
||||
IPs: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
RemoveDuplicateAltNames(tt.input)
|
||||
if !reflect.DeepEqual(tt.input, tt.want) {
|
||||
t.Errorf("RemoveDuplicateAltNames() = %+v, want %+v", tt.input, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright 2024 The Karmada Authors.
|
||||
|
||||
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 certs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// Helper function to create a new KarmadaCert with given pairName.
|
||||
func newKarmadaCert(pairName string, certData, keyData []byte) *KarmadaCert {
|
||||
return &KarmadaCert{
|
||||
pairName: pairName,
|
||||
cert: certData,
|
||||
key: keyData,
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCertStore(t *testing.T) {
|
||||
store := NewCertStore()
|
||||
if store == nil {
|
||||
t.Fatalf("expected a non-nil CertStore")
|
||||
}
|
||||
if len(store.(*KarmadaCertStore).certs) != 0 {
|
||||
t.Errorf("expected an empty cert store")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAndGetCert(t *testing.T) {
|
||||
store := NewCertStore()
|
||||
|
||||
cert := newKarmadaCert("testCert", []byte("certData"), []byte("keyData"))
|
||||
store.AddCert(cert)
|
||||
|
||||
retrievedCert := store.GetCert("testCert")
|
||||
if retrievedCert == nil {
|
||||
t.Fatalf("expected to retrieve cert but got nil")
|
||||
}
|
||||
if string(retrievedCert.cert) != "certData" {
|
||||
t.Errorf("expected certData but got %s", string(retrievedCert.cert))
|
||||
}
|
||||
if string(retrievedCert.key) != "keyData" {
|
||||
t.Errorf("expected keyData but got %s", string(retrievedCert.key))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertList(t *testing.T) {
|
||||
store := NewCertStore()
|
||||
|
||||
cert1 := newKarmadaCert("cert1", []byte("cert1Data"), []byte("key1Data"))
|
||||
cert2 := newKarmadaCert("cert2", []byte("cert2Data"), []byte("key2Data"))
|
||||
store.AddCert(cert1)
|
||||
store.AddCert(cert2)
|
||||
|
||||
certs := store.CertList()
|
||||
if len(certs) != 2 {
|
||||
t.Errorf("expected 2 certs but got %d", len(certs))
|
||||
}
|
||||
if certs[0].pairName != "cert1" || certs[1].pairName != "cert2" {
|
||||
t.Errorf("cert list does not match expected values")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCertFromSecret(t *testing.T) {
|
||||
store := NewCertStore()
|
||||
|
||||
secret := &corev1.Secret{
|
||||
Data: map[string][]byte{
|
||||
"cert1.crt": []byte("cert1CertData"),
|
||||
"cert1.key": []byte("cert1KeyData"),
|
||||
"cert2.crt": []byte("cert2CertData"),
|
||||
"cert2.key": []byte("cert2KeyData"),
|
||||
},
|
||||
}
|
||||
|
||||
err := store.LoadCertFromSecret(secret)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cert1 := store.GetCert("cert1")
|
||||
if cert1 == nil || string(cert1.cert) != "cert1CertData" || string(cert1.key) != "cert1KeyData" {
|
||||
t.Errorf("cert1 content is incorrect expected cert %s key %s, got cert %s key %s", "cert1CertData", "cert1KeyData", string(cert1.cert), string(cert1.key))
|
||||
}
|
||||
|
||||
cert2 := store.GetCert("cert2")
|
||||
if cert2 == nil || string(cert2.cert) != "cert2CertData" || string(cert2.key) != "cert2KeyData" {
|
||||
t.Errorf("cert2 content is incorrect expected cert %s key %s, got cert %s key %s", "cert2CertData", "cert2KeyData", string(cert2.cert), string(cert2.key))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCertFromSecret_EmptyData(t *testing.T) {
|
||||
store := NewCertStore()
|
||||
|
||||
secret := &corev1.Secret{
|
||||
Data: map[string][]byte{},
|
||||
}
|
||||
|
||||
err := store.LoadCertFromSecret(secret)
|
||||
if err == nil {
|
||||
t.Error("expected error that cert data is empty")
|
||||
}
|
||||
if len(store.CertList()) != 0 {
|
||||
t.Errorf("expected 0 certs but got %d", len(store.CertList()))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCertFromSecret_InvalidFormat(t *testing.T) {
|
||||
pairName := "invalid.data"
|
||||
|
||||
store := NewCertStore()
|
||||
|
||||
secret := &corev1.Secret{
|
||||
Data: map[string][]byte{
|
||||
pairName: []byte("invalidData"),
|
||||
},
|
||||
}
|
||||
|
||||
err := store.LoadCertFromSecret(secret)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(store.CertList()) != 1 {
|
||||
t.Errorf("expected 1 cert but got %d", len(store.CertList()))
|
||||
}
|
||||
|
||||
karmadaCert := store.GetCert(pairName)
|
||||
if len(karmadaCert.key) != 0 {
|
||||
t.Errorf("expected the cert data content to be empty but got %v", karmadaCert.cert)
|
||||
}
|
||||
if len(karmadaCert.key) != 0 {
|
||||
t.Errorf("expected the key data content to be empty but got %v", karmadaCert.key)
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -286,3 +287,37 @@ func DeepEqualTasks(t1, t2 workflow.Task) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainsAllValues checks if all values in the 'values' slice exist in the 'container' slice or array.
|
||||
func ContainsAllValues(container interface{}, values interface{}) bool {
|
||||
// Ensure the provided container is a slice or array.
|
||||
vContainer := reflect.ValueOf(container)
|
||||
if vContainer.Kind() != reflect.Slice && vContainer.Kind() != reflect.Array {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure the provided values are a slice or array.
|
||||
vValues := reflect.ValueOf(values)
|
||||
if vValues.Kind() != reflect.Slice && vValues.Kind() != reflect.Array {
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterate over the 'values' and ensure each value exists in the container.
|
||||
for i := 0; i < vValues.Len(); i++ {
|
||||
value := vValues.Index(i).Interface()
|
||||
found := false
|
||||
// Check if this value exists in the container.
|
||||
for j := 0; j < vContainer.Len(); j++ {
|
||||
if reflect.DeepEqual(vContainer.Index(j).Interface(), value) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// If any value is not found, return false.
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// If all values were found, return true.
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue