Merge pull request #5869 from mohamedawnallah/unitTestAPIClientUtil
pkg/karmadactl/util: unit test apiclient
This commit is contained in:
commit
44b59a7d5b
|
@ -18,17 +18,11 @@ package util
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -38,6 +32,7 @@ import (
|
|||
coretesting "k8s.io/client-go/testing"
|
||||
|
||||
operatorv1alpha1 "github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1"
|
||||
testingutil "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
)
|
||||
|
||||
func TestBuildClientFromSecretRef(t *testing.T) {
|
||||
|
@ -120,7 +115,7 @@ users:
|
|||
},
|
||||
prep: func(client clientset.Interface) error {
|
||||
// Generate kubeconfig bytes.
|
||||
caCert, err := generateTestCACertificate()
|
||||
caCert, _, err := testingutil.GenerateTestCACertificate()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate CA certificate: %v", err)
|
||||
}
|
||||
|
@ -210,43 +205,3 @@ func TestIsInCluster(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generateTestCACertificate returns a self-signed CA certificate as a PEM string.
|
||||
func generateTestCACertificate() (string, error) {
|
||||
// Generate a new RSA private key
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Set the certificate parameters.
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cert := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
// Create the certificate.
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, cert, cert, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// PEM encode the certificate.
|
||||
certPEM := &pem.Block{Type: "CERTIFICATE", Bytes: certDER}
|
||||
certPEMData := pem.EncodeToMemory(certPEM)
|
||||
|
||||
return string(certPEMData), nil
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import (
|
|||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||
utilhelper "github.com/karmada-io/karmada/pkg/util/helper"
|
||||
testing3 "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
testingutil "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
"github.com/karmada-io/karmada/test/helper"
|
||||
)
|
||||
|
||||
|
@ -93,7 +93,7 @@ func makeFakeRBCByResource(rs *workv1alpha2.ObjectReference) (*ResourceBindingCo
|
|||
return &ResourceBindingController{
|
||||
Client: c,
|
||||
RESTMapper: helper.NewGroupRESTMapper(rs.Kind, meta.RESTScopeNamespace),
|
||||
InformerManager: testing3.NewSingleClusterInformerManagerByRS(src, obj),
|
||||
InformerManager: testingutil.NewSingleClusterInformerManagerByRS(src, obj),
|
||||
DynamicClient: tempDyClient,
|
||||
EventRecorder: record.NewFakeRecorder(1024),
|
||||
}, nil
|
||||
|
|
|
@ -44,7 +44,7 @@ import (
|
|||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||
utilhelper "github.com/karmada-io/karmada/pkg/util/helper"
|
||||
testing3 "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
testingutil "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
"github.com/karmada-io/karmada/test/helper"
|
||||
)
|
||||
|
||||
|
@ -86,7 +86,7 @@ func makeFakeCRBCByResource(rs *workv1alpha2.ObjectReference) (*ClusterResourceB
|
|||
return &ClusterResourceBindingController{
|
||||
Client: c,
|
||||
RESTMapper: helper.NewGroupRESTMapper(rs.Kind, meta.RESTScopeNamespace),
|
||||
InformerManager: testing3.NewSingleClusterInformerManagerByRS(src, obj),
|
||||
InformerManager: testingutil.NewSingleClusterInformerManagerByRS(src, obj),
|
||||
DynamicClient: tempDyClient,
|
||||
EventRecorder: record.NewFakeRecorder(1024),
|
||||
}, nil
|
||||
|
|
|
@ -42,23 +42,9 @@ var (
|
|||
`)
|
||||
)
|
||||
|
||||
// KubeConfigPath is to return kubeconfig file path in the following order:
|
||||
// 1. Via the command-line flag --kubeconfig
|
||||
// 2. Via the KUBECONFIG environment variable
|
||||
// 3. In your home directory as ~/.kube/config
|
||||
func KubeConfigPath(kubeconfigPath string) string {
|
||||
if kubeconfigPath == "" {
|
||||
kubeconfigPath = env.GetString("KUBECONFIG", defaultKubeConfig)
|
||||
}
|
||||
|
||||
return kubeconfigPath
|
||||
}
|
||||
|
||||
// RestConfig is to create a rest config from the context and kubeconfig passed as arguments.
|
||||
func RestConfig(context, kubeconfigPath string) (*rest.Config, error) {
|
||||
if kubeconfigPath == "" {
|
||||
kubeconfigPath = env.GetString("KUBECONFIG", defaultKubeConfig)
|
||||
}
|
||||
kubeconfigPath = KubeConfigPath(kubeconfigPath)
|
||||
if !Exists(kubeconfigPath) {
|
||||
// Given no kubeconfig is provided, give it a try to load the config by
|
||||
// in-cluster mode if the client running inside a pod running on kubernetes.
|
||||
|
@ -92,6 +78,18 @@ func RestConfig(context, kubeconfigPath string) (*rest.Config, error) {
|
|||
return restConfig, err
|
||||
}
|
||||
|
||||
// KubeConfigPath is to return kubeconfig file path in the following order:
|
||||
// 1. Via the command-line flag --kubeconfig
|
||||
// 2. Via the KUBECONFIG environment variable
|
||||
// 3. In your home directory as ~/.kube/config
|
||||
func KubeConfigPath(kubeconfigPath string) string {
|
||||
if kubeconfigPath == "" {
|
||||
kubeconfigPath = env.GetString("KUBECONFIG", defaultKubeConfig)
|
||||
}
|
||||
|
||||
return kubeconfigPath
|
||||
}
|
||||
|
||||
// NewClientSet is to create a kubernetes ClientSet
|
||||
func NewClientSet(c *rest.Config) (*kubernetes.Clientset, error) {
|
||||
return kubernetes.NewForConfig(c)
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
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 apiclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
testingutil "github.com/karmada-io/karmada/pkg/util/testing"
|
||||
)
|
||||
|
||||
func TestRestConfig(t *testing.T) {
|
||||
kubeconfig := `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://127.0.0.1:6443
|
||||
certificate-authority-data: %s
|
||||
name: member1
|
||||
contexts:
|
||||
- context:
|
||||
cluster: member1
|
||||
user: member1
|
||||
name: member1-context
|
||||
current-context: member1-context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: test-user
|
||||
user:
|
||||
client-certificate-data: %s
|
||||
client-key-data: %s
|
||||
`
|
||||
tests := []struct {
|
||||
name string
|
||||
context, kubeConfigPath string
|
||||
prep func(kubeConfigPath string) (string, error)
|
||||
verify func(restConfig *rest.Config, caCert string) error
|
||||
cleanup func(kubeConfigPath string) error
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "RestConfig_KubeConfigNotExist_MissingConfigurationInfo",
|
||||
context: "member1-context",
|
||||
kubeConfigPath: filepath.Join(os.TempDir(), "member1-cluster.config"),
|
||||
prep: func(string) (string, error) { return "", nil },
|
||||
verify: func(*rest.Config, string) error { return nil },
|
||||
cleanup: func(string) error { return nil },
|
||||
wantErr: true,
|
||||
errMsg: ErrEmptyConfig.Error(),
|
||||
},
|
||||
{
|
||||
name: "RestConfig_CreateRestConfig_RestConfigCreated",
|
||||
context: "member1-context",
|
||||
kubeConfigPath: filepath.Join(os.TempDir(), "member1-cluster.config"),
|
||||
prep: func(kubeConfigPath string) (string, error) {
|
||||
return prepRestConfig(kubeconfig, kubeConfigPath)
|
||||
},
|
||||
verify: func(restConfig *rest.Config, caCert string) error {
|
||||
return verifyRestConfig(restConfig, caCert)
|
||||
},
|
||||
cleanup: func(kubeConfigPath string) error {
|
||||
if err := os.Remove(kubeConfigPath); err != nil {
|
||||
return fmt.Errorf("failed to clean up config file %s, got error: %v", kubeConfigPath, err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caCert, err := test.prep(test.kubeConfigPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prep test environment, got error: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := test.cleanup(test.kubeConfigPath); err != nil {
|
||||
t.Errorf("failed to cleanup test environment, got error: %v", err)
|
||||
}
|
||||
}()
|
||||
restConfig, err := RestConfig(test.context, test.kubeConfigPath)
|
||||
if err == nil && test.wantErr {
|
||||
t.Fatal("expected an error, but got none")
|
||||
}
|
||||
if err != nil && !test.wantErr {
|
||||
t.Errorf("unexpected error, got: %v", err)
|
||||
}
|
||||
if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
|
||||
t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
|
||||
}
|
||||
if err := test.verify(restConfig, caCert); err != nil {
|
||||
t.Errorf("failed to verify creating rest config, got error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func prepRestConfig(kubeConfig, kubeConfigPath string) (string, error) {
|
||||
caCert, privateKey, err := testingutil.GenerateTestCACertificate()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate CA certificate: %v", err)
|
||||
}
|
||||
base64CACert := base64.StdEncoding.EncodeToString([]byte(caCert))
|
||||
base64PrivateKey := base64.StdEncoding.EncodeToString([]byte(privateKey))
|
||||
kubeconfigFormatted := fmt.Sprintf(kubeConfig, base64CACert, base64CACert, base64PrivateKey)
|
||||
if err := os.WriteFile(kubeConfigPath, []byte(kubeconfigFormatted), 0600); err != nil {
|
||||
return "", fmt.Errorf("failed to write kubeconfig to file: %v", err)
|
||||
}
|
||||
return base64CACert, nil
|
||||
}
|
||||
|
||||
func verifyRestConfig(restConfig *rest.Config, caCert string) error {
|
||||
if restConfig == nil {
|
||||
return errors.New("expected rest config, but got nil")
|
||||
}
|
||||
if restConfig.Host != "https://127.0.0.1:6443" {
|
||||
return fmt.Errorf("expected rest config host to be %s, but got %s", "https://127.0.0.1:6443", restConfig.Host)
|
||||
}
|
||||
|
||||
caCertDecoded, err := base64.StdEncoding.DecodeString(caCert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode ca certificate, got error: %v", err)
|
||||
}
|
||||
if !bytes.Equal(restConfig.TLSClientConfig.CAData, caCertDecoded) {
|
||||
return fmt.Errorf("expected ca cert bytes %v to be %v", restConfig.TLSClientConfig.CAData, caCertDecoded)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
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 testing
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GenerateTestCACertificate generates a self-signed CA certificate and its associated RSA private key.
|
||||
// The certificate and private key are returned as PEM-encoded strings.
|
||||
func GenerateTestCACertificate() (string, string, error) {
|
||||
// Generate a new RSA private key
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Set the certificate parameters.
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour)
|
||||
|
||||
serialNumber, err := rand.Int(rand.Reader, big.NewInt(1<<62))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cert := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
// Create the certificate.
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, cert, cert, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// PEM encode the certificate.
|
||||
certPEM := &pem.Block{Type: "CERTIFICATE", Bytes: certDER}
|
||||
certPEMData := pem.EncodeToMemory(certPEM)
|
||||
|
||||
// PEM encode the private key.
|
||||
privKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}
|
||||
privKeyPEMData := pem.EncodeToMemory(privKeyPEM)
|
||||
|
||||
return string(certPEMData), string(privKeyPEMData), nil
|
||||
}
|
Loading…
Reference in New Issue