*
* Copyright (c) 2025 Bocloud Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* <http://license.coscl.org.cn/MulanPSL2>
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*
*/
package cluster
import (
"fmt"
"os"
"path"
"testing"
"github.com/agiledragon/gomonkey/v2"
"github.com/stretchr/testify/assert"
configv1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/bkecommon/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"gopkg.openfuyao.cn/bkeadm/pkg/executor/k8s"
"gopkg.openfuyao.cn/bkeadm/pkg/global"
"gopkg.openfuyao.cn/bkeadm/utils"
"gopkg.openfuyao.cn/bkeadm/utils/log"
)
func TestCluster(t *testing.T) {
tempDir := t.TempDir()
testFile := path.Join(tempDir, "test-cluster.yaml")
testConfig := fmt.Sprintf(`apiVersion: cluster.bocloud.com/v1beta1
kind: BKECluster
metadata:
name: test-cluster
namespace: test-namespace
spec:
BKEConfig:
cluster:
imageRepo:
domain: %s
ip: %s
port: %s
prefix: k8s
httpRepo:
domain: %s
ip: %s
port: %s
chartRepo:
domain: %s
ip: %s
port: %s
prefix: charts
ntpServer: pool.ntp.org
`, testRegistryDomain, testRegistryIP, testRegistryPort, testYumDomain, testYumIP, testYumPort, testChartDomain, testChartIP, testChartPort)
err := os.WriteFile(testFile, []byte(testConfig), utils.DefaultFilePermission)
assert.NoError(t, err)
tests := []struct {
name string
file string
ntpServer string
mockNewBKEClusterFromFile func(string) (*configv1beta1.BKECluster, error)
mockMarshalAndWriteClusterYAML func(*configv1beta1.BKECluster) (string, error)
mockCreateNamespace func(*k8s.Client, *corev1.Namespace) error
mockInstallYaml func(*k8s.Client, string, map[string]string, string) error
mockWatchEventByAnnotation func(*k8s.Client, string)
expectError bool
}{
{
name: "successful cluster deployment",
file: testFile,
ntpServer: "",
mockNewBKEClusterFromFile: func(file string) (*configv1beta1.BKECluster, error) {
return &configv1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
Namespace: "test-namespace",
},
Spec: configv1beta1.BKEClusterSpec{
ClusterConfig: &configv1beta1.BKEConfig{
Cluster: configv1beta1.Cluster{
ImageRepo: configv1beta1.Repo{
Domain: "registry.example.com",
},
},
},
},
}, nil
},
mockMarshalAndWriteClusterYAML: func(cluster *configv1beta1.BKECluster) (string, error) {
return "/tmp/test-cluster.yaml", nil
},
mockCreateNamespace: func(c *k8s.Client, ns *corev1.Namespace) error {
return nil
},
mockInstallYaml: func(c *k8s.Client, file string, data map[string]string, template string) error {
return nil
},
mockWatchEventByAnnotation: func(c *k8s.Client, namespace string) {},
expectError: false,
},
{
name: "with NTP server override",
file: testFile,
ntpServer: "pool.ntp.org",
mockNewBKEClusterFromFile: func(file string) (*configv1beta1.BKECluster, error) {
cluster := &configv1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
Namespace: "test-namespace",
},
Spec: configv1beta1.BKEClusterSpec{
ClusterConfig: &configv1beta1.BKEConfig{
Cluster: configv1beta1.Cluster{
ImageRepo: configv1beta1.Repo{
Domain: "registry.example.com",
},
NTPServer: "original.ntp.org",
},
},
},
}
return cluster, nil
},
mockMarshalAndWriteClusterYAML: func(cluster *configv1beta1.BKECluster) (string, error) {
assert.Equal(t, "pool.ntp.org", cluster.Spec.ClusterConfig.Cluster.NTPServer)
return "/tmp/test-cluster.yaml", nil
},
mockCreateNamespace: func(c *k8s.Client, ns *corev1.Namespace) error {
return nil
},
mockInstallYaml: func(c *k8s.Client, file string, data map[string]string, template string) error {
return nil
},
mockWatchEventByAnnotation: func(c *k8s.Client, namespace string) {},
expectError: false,
},
{
name: "NewBKEClusterFromFile error",
file: testFile,
ntpServer: "",
mockNewBKEClusterFromFile: func(file string) (*configv1beta1.BKECluster, error) {
return nil, fmt.Errorf("config error")
},
expectError: false,
},
{
name: "marshalAndWriteClusterYAML error",
file: testFile,
ntpServer: "",
mockNewBKEClusterFromFile: func(file string) (*configv1beta1.BKECluster, error) {
return &configv1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
Namespace: "test-namespace",
},
}, nil
},
mockMarshalAndWriteClusterYAML: func(cluster *configv1beta1.BKECluster) (string, error) {
return "", fmt.Errorf("marshal error")
},
expectError: false,
},
{
name: "InstallYaml error",
file: testFile,
ntpServer: "",
mockNewBKEClusterFromFile: func(file string) (*configv1beta1.BKECluster, error) {
return &configv1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster",
Namespace: "test-namespace",
},
}, nil
},
mockMarshalAndWriteClusterYAML: func(cluster *configv1beta1.BKECluster) (string, error) {
return "/tmp/test-cluster.yaml", nil
},
mockCreateNamespace: func(c *k8s.Client, ns *corev1.Namespace) error {
return nil
},
mockInstallYaml: func(c *k8s.Client, file string, data map[string]string, template string) error {
return fmt.Errorf("install error")
},
expectError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
op := &Options{
File: tt.file,
NtpServer: tt.ntpServer,
}
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(NewBKEClusterFromFile, tt.mockNewBKEClusterFromFile)
patches.ApplyFunc(marshalAndWriteClusterYAML, tt.mockMarshalAndWriteClusterYAML)
patches.ApplyFunc((*k8s.Client).CreateNamespace, tt.mockCreateNamespace)
patches.ApplyFunc((*k8s.Client).InstallYaml, tt.mockInstallYaml)
patches.ApplyFunc((*k8s.Client).WatchEventByAnnotation, tt.mockWatchEventByAnnotation)
patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})
if global.K8s == nil {
global.K8s = &k8s.Client{}
}
op.Cluster()
assert.True(t, true)
})
}
}
func TestClusterWithRealFile(t *testing.T) {
tempDir := t.TempDir()
testFile := path.Join(tempDir, "real-cluster.yaml")
realConfig := fmt.Sprintf(`apiVersion: cluster.bocloud.com/v1beta1
kind: BKECluster
metadata:
name: real-test-cluster
namespace: real-test-namespace
spec:
BKEConfig:
cluster:
imageRepo:
domain: %s
ip: %s
port: %s
prefix: k8s
httpRepo:
domain: %s
ip: %s
port: %s
chartRepo:
domain: %s
ip: %s
port: %s
prefix: charts
ntpServer: pool.ntp.org
`, testRegistryDomain, testRegistryIP, testRegistryPort, testYumDomain, testYumIP, testYumPort, testChartDomain, testChartIP, testChartPort)
err := os.WriteFile(testFile, []byte(realConfig), utils.DefaultFilePermission)
assert.NoError(t, err)
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(NewBKEClusterFromFile, func(file string) (*configv1beta1.BKECluster, error) {
cluster := &configv1beta1.BKECluster{}
cluster.ObjectMeta.Name = "real-test-cluster"
cluster.ObjectMeta.Namespace = "real-test-namespace"
cluster.Spec.ClusterConfig = &configv1beta1.BKEConfig{
Cluster: configv1beta1.Cluster{
ImageRepo: configv1beta1.Repo{
Domain: "registry.example.com",
},
},
}
return cluster, nil
})
patches.ApplyFunc(marshalAndWriteClusterYAML, func(cluster *configv1beta1.BKECluster) (string, error) {
return "/tmp/real-test-cluster.yaml", nil
})
patches.ApplyFunc((*k8s.Client).CreateNamespace, func(c *k8s.Client, ns *corev1.Namespace) error {
return nil
})
patches.ApplyFunc((*k8s.Client).InstallYaml, func(c *k8s.Client, file string, data map[string]string, template string) error {
return nil
})
patches.ApplyFunc((*k8s.Client).WatchEventByAnnotation, func(c *k8s.Client, namespace string) {})
patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})
op := &Options{
File: testFile,
NtpServer: "",
}
op.Cluster()
assert.True(t, true)
}
func TestClusterWithNtpServerOverride(t *testing.T) {
tempDir := t.TempDir()
testFile := path.Join(tempDir, "ntp-test-cluster.yaml")
testNodesFile := path.Join(tempDir, "ntp-test-nodes.yaml")
testConfig := `apiVersion: cluster.bocloud.com/v1beta1
kind: BKECluster
metadata:
name: ntp-test-cluster
namespace: ntp-test-namespace
spec:
BKEConfig:
cluster:
imageRepo:
domain: registry.example.com
ntpServer: original.ntp.org
`
err := os.WriteFile(testFile, []byte(testConfig), utils.DefaultFilePermission)
assert.NoError(t, err)
err = os.WriteFile(testNodesFile, []byte(""), utils.DefaultFilePermission)
assert.NoError(t, err)
var capturedCluster *configv1beta1.BKECluster
patches := gomonkey.NewPatches()
defer patches.Reset()
patches.ApplyFunc(NewClusterResourcesFromFiles, func(clusterFile, nodesFile string) (*ClusterResources, error) {
return &ClusterResources{
BKECluster: &configv1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{
Name: "ntp-test-cluster",
Namespace: "ntp-test-namespace",
},
Spec: configv1beta1.BKEClusterSpec{
ClusterConfig: &configv1beta1.BKEConfig{
Cluster: configv1beta1.Cluster{
ImageRepo: configv1beta1.Repo{
Domain: "registry.example.com",
},
NTPServer: "original.ntp.org",
},
},
},
},
BKENodes: []configv1beta1.BKENode{},
}, nil
})
patches.ApplyFunc(marshalAndWriteClusterYAML, func(cluster *configv1beta1.BKECluster) (string, error) {
capturedCluster = cluster
return "/tmp/ntp-test-cluster.yaml", nil
})
patches.ApplyFunc(marshalAndWriteNodeYAMLs, func(namespace string, nodes []configv1beta1.BKENode) ([]string, error) {
return []string{}, nil
})
patches.ApplyFunc((*k8s.Client).CreateNamespace, func(c *k8s.Client, ns *corev1.Namespace) error {
return nil
})
patches.ApplyFunc((*k8s.Client).InstallYaml, func(c *k8s.Client, file string, data map[string]string, template string) error {
return nil
})
patches.ApplyFunc((*k8s.Client).WatchEventByAnnotation, func(c *k8s.Client, namespace string) {})
patches.ApplyFunc(log.SteppedInfo, func(stepName string, args ...any) {})
if global.K8s == nil {
global.K8s = &k8s.Client{}
}
op := &Options{
File: testFile,
NodesFile: testNodesFile,
NtpServer: "new.ntp.org",
}
op.Cluster()
assert.NotNil(t, capturedCluster)
assert.Equal(t, "new.ntp.org", capturedCluster.Spec.ClusterConfig.Cluster.NTPServer)
}