* 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 n 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 kube
import (
"bytes"
"io"
"testing"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
fakedynamic "k8s.io/client-go/dynamic/fake"
"gopkg.openfuyao.cn/cluster-api-provider-bke/common/cluster/addon"
"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/log"
)
func TestConstants(t *testing.T) {
if DefaultFilePermission != 0644 {
t.Errorf("Expected DefaultFilePermission to be 0644, got %o", DefaultFilePermission)
}
if DefaultYamlDecoderBufferSize != 4096 {
t.Errorf("Expected DefaultYamlDecoderBufferSize to be 4096, got %d", DefaultYamlDecoderBufferSize)
}
}
func TestUnStructYaml(t *testing.T) {
yamlContent := `---
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
spec:
containers:
- name: test
image: nginx:latest
`
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(yamlContent)), 4096)
unstruct, gvk, err := UnStructYaml(decoder)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if unstruct == nil {
t.Fatal("Expected unstruct to be not nil")
}
if gvk == nil {
t.Fatal("Expected gvk to be not nil")
}
if gvk.Kind != "Pod" {
t.Errorf("Expected Kind to be Pod, got %s", gvk.Kind)
}
if gvk.Group != "" {
t.Errorf("Expected Group to be empty, got %s", gvk.Group)
}
if gvk.Version != "v1" {
t.Errorf("Expected Version to be v1, got %s", gvk.Version)
}
}
func TestUnStructYamlInvalidYAML(t *testing.T) {
yamlContent := `---
invalid: yaml: content: [`
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(yamlContent)), 4096)
unstruct, gvk, err := UnStructYaml(decoder)
if err == nil {
t.Fatal("Expected error for invalid YAML")
}
if unstruct != nil {
t.Error("Expected unstruct to be nil")
}
if gvk != nil {
t.Error("Expected gvk to be nil")
}
}
func TestUnStructYamlEmpty(t *testing.T) {
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte("")), 4096)
unstruct, gvk, err := UnStructYaml(decoder)
if err != io.EOF {
t.Errorf("Expected EOF error, got %v", err)
}
if unstruct != nil {
t.Error("Expected unstruct to be nil for empty input")
}
if gvk != nil {
t.Error("Expected gvk to be nil for empty input")
}
}
func TestGetUnStructListFromDecoder(t *testing.T) {
yamlContent := `---
apiVersion: v1
kind: Pod
metadata:
name: pod1
---
apiVersion: v1
kind: Service
metadata:
name: svc1
`
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(yamlContent)), 4096)
list, err := GetUnStructListFromDecoder(decoder)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(list) != 2 {
t.Errorf("Expected 2 items in list, got %d", len(list))
}
if list[0].GetKind() != "Pod" {
t.Errorf("Expected first item to be Pod, got %s", list[0].GetKind())
}
if list[1].GetKind() != "Service" {
t.Errorf("Expected second item to be Service, got %s", list[1].GetKind())
}
}
func TestGetUnStructListFromDecoderWithError(t *testing.T) {
yamlContent := `---
invalid: yaml: content: [`
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(yamlContent)), 4096)
list, err := GetUnStructListFromDecoder(decoder)
if err == nil {
t.Fatal("Expected error for invalid YAML")
}
if list != nil {
t.Error("Expected list to be nil")
}
}
func TestProcessUnstructuredList(t *testing.T) {
c := &Client{}
unstructList := []*unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]interface{}{
"name": "pod1",
},
},
},
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
},
},
},
}
result := c.processUnstructuredList(unstructList)
if len(result) != 2 {
t.Errorf("Expected 2 items, got %d", len(result))
}
}
func TestSortUnstructuredListInstall(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
},
},
}
task := &Task{
Operate: addon.CreateAddon,
}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 1 {
t.Errorf("Expected 1 item, got %d", len(result))
}
}
func TestSortUnstructuredListUpgrade(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
},
},
}
task := &Task{
Operate: addon.UpgradeAddon,
}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 1 {
t.Errorf("Expected 1 item, got %d", len(result))
}
}
func TestSortUnstructuredListRemove(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
},
},
}
task := &Task{
Operate: addon.RemoveAddon,
}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 1 {
t.Errorf("Expected 1 item, got %d", len(result))
}
}
func TestTrueVar(t *testing.T) {
if !trueVar {
t.Error("Expected trueVar to be true")
}
}
func TestGetResourceInterface_Namespaced(t *testing.T) {
scheme := runtime.NewScheme()
dynamicClient := fakedynamic.NewSimpleDynamicClient(scheme)
c := &Client{DynamicClient: dynamicClient}
mapping := &meta.RESTMapping{
Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Scope: meta.RESTScopeNamespace,
}
unstruct := unstructured.Unstructured{}
unstruct.SetNamespace("default")
ri, err := c.getResourceInterface(mapping, unstruct)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if ri == nil {
t.Fatal("Expected resource interface to be not nil")
}
}
func TestGetResourceInterface_ClusterScoped(t *testing.T) {
scheme := runtime.NewScheme()
dynamicClient := fakedynamic.NewSimpleDynamicClient(scheme)
c := &Client{DynamicClient: dynamicClient}
mapping := &meta.RESTMapping{
Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes"},
Scope: meta.RESTScopeRoot,
}
unstruct := unstructured.Unstructured{}
ri, err := c.getResourceInterface(mapping, unstruct)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if ri == nil {
t.Fatal("Expected resource interface to be not nil")
}
}
func TestHandleWaitAndLogging_NoBlock(t *testing.T) {
logger := (*log.Logger)(nil)
c := &Client{Log: logger}
obj := &unstructured.Unstructured{}
obj.SetKind("Pod")
obj.SetName("test-pod")
task := &Task{Block: false, Operate: addon.CreateAddon}
err := c.handleWaitAndLogging(obj, *obj, task)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
}
func TestHandleWaitAndLogging_RemoveAddon(t *testing.T) {
logger := (*log.Logger)(nil)
c := &Client{Log: logger}
obj := &unstructured.Unstructured{}
obj.SetKind("Pod")
obj.SetName("test-pod")
task := &Task{Block: true, Operate: addon.RemoveAddon}
err := c.handleWaitAndLogging(obj, *obj, task)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
}
func TestHandleWaitAndLogging_NilObj(t *testing.T) {
logger := (*log.Logger)(nil)
c := &Client{Log: logger}
unstruct := unstructured.Unstructured{}
unstruct.SetKind("Pod")
unstruct.SetName("test-pod")
task := &Task{Block: false, Operate: addon.CreateAddon}
err := c.handleWaitAndLogging(nil, unstruct, task)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
}
func TestProcessUnstructuredList_WithList(t *testing.T) {
c := &Client{}
unstructList := []*unstructured.Unstructured{
{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "List",
"items": []interface{}{
map[string]interface{}{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]interface{}{"name": "pod1"},
},
},
},
},
}
result := c.processUnstructuredList(unstructList)
if len(result) == 0 {
t.Error("Expected at least one item")
}
}
func TestSortUnstructuredList_UpdateAddon(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{Object: map[string]interface{}{"apiVersion": "v1", "kind": "Pod"}},
}
task := &Task{Operate: addon.UpdateAddon}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 1 {
t.Errorf("Expected 1 item, got %d", len(result))
}
}
func TestGetUnStructListFromDecoder_Success(t *testing.T) {
yamlContent := "apiVersion: v1\nkind: Pod\nmetadata:\n name: test"
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte(yamlContent)), 4096)
list, err := GetUnStructListFromDecoder(decoder)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(list) == 0 {
t.Error("Expected at least one item")
}
}
func TestGetUnStructListFromDecoder_EmptyYaml(t *testing.T) {
decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader([]byte("")), 4096)
list, err := GetUnStructListFromDecoder(decoder)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(list) != 0 {
t.Errorf("Expected empty list, got %d items", len(list))
}
}
func TestSortUnstructuredList_RemoveAddon(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{Object: map[string]interface{}{"apiVersion": "v1", "kind": "Pod"}},
{Object: map[string]interface{}{"apiVersion": "v1", "kind": "Service"}},
}
task := &Task{Operate: addon.RemoveAddon}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 2 {
t.Errorf("Expected 2 items, got %d", len(result))
}
}
func TestSortUnstructuredList_InstallAddon(t *testing.T) {
c := &Client{}
unstructList := []unstructured.Unstructured{
{Object: map[string]interface{}{"apiVersion": "v1", "kind": "Pod"}},
}
task := &Task{Operate: addon.CreateAddon}
result := c.sortUnstructuredList(unstructList, task)
if len(result) != 1 {
t.Errorf("Expected 1 item, got %d", len(result))
}
}
func TestHandleOperation_UnknownOperation(t *testing.T) {
logger := (*log.Logger)(nil)
c := &Client{Log: logger}
task := &Task{Operate: "unknown"}
unstruct := unstructured.Unstructured{}
gvk := schema.GroupVersionKind{Kind: "Pod"}
obj, err := c.handleOperation(nil, unstruct, task, gvk)
if err == nil {
t.Error("Expected error for unknown operation")
}
if obj != nil {
t.Error("Expected nil obj")
}
}
func TestHandleRemoveOperation_NotFound(t *testing.T) {
logger := (*log.Logger)(nil)
scheme := runtime.NewScheme()
dynamicClient := fakedynamic.NewSimpleDynamicClient(scheme)
c := &Client{Log: logger, DynamicClient: dynamicClient}
unstruct := unstructured.Unstructured{}
unstruct.SetName("test")
unstruct.SetNamespace("default")
mapping := &meta.RESTMapping{
Resource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Scope: meta.RESTScopeNamespace,
}
dr, _ := c.getResourceInterface(mapping, unstruct)
task := &Task{Operate: addon.RemoveAddon}
err := c.handleRemoveOperation(dr, unstruct, task)
if err != nil {
t.Errorf("Expected no error for not found, got %v", err)
}
}
func TestRenderYamlToDecoder_FileNotFound(t *testing.T) {
task := &Task{
Name: "test",
FilePath: "/nonexistent/file.yaml",
Param: map[string]interface{}{},
}
_, err := RenderYamlToDecoder(task)
if err == nil {
t.Error("Expected error for non-existent file")
}
}
func TestHandleUpdateOperation_CRD(t *testing.T) {
logger := (*log.Logger)(nil)
c := &Client{Log: logger}
unstruct := unstructured.Unstructured{}
unstruct.SetName("test-crd")
gvk := schema.GroupVersionKind{Kind: "CustomResourceDefinition"}
task := &Task{Operate: addon.UpdateAddon}
obj, err := c.handleUpdateOperation(nil, unstruct, task, gvk)
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
if obj != nil {
t.Error("Expected obj to be nil for CRD")
}
}