github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/util/util_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package util
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"os"
    26  	"strings"
    27  	"time"
    28  
    29  	. "github.com/onsi/ginkgo/v2"
    30  	. "github.com/onsi/gomega"
    31  
    32  	"github.com/go-logr/logr"
    33  	corev1 "k8s.io/api/core/v1"
    34  	"k8s.io/apimachinery/pkg/api/resource"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    37  	dynamicfakeclient "k8s.io/client-go/dynamic/fake"
    38  	"k8s.io/client-go/kubernetes/scheme"
    39  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    40  	"sigs.k8s.io/controller-runtime/pkg/log"
    41  
    42  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    43  	"github.com/1aal/kubeblocks/pkg/cli/testing"
    44  	"github.com/1aal/kubeblocks/pkg/cli/types"
    45  	cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core"
    46  	"github.com/1aal/kubeblocks/pkg/constant"
    47  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    48  	"github.com/1aal/kubeblocks/test/testdata"
    49  )
    50  
    51  var _ = Describe("util", func() {
    52  	It("Get home dir", func() {
    53  		home, err := GetCliHomeDir()
    54  		Expect(len(home) > 0).Should(BeTrue())
    55  		Expect(err == nil).Should(BeTrue())
    56  	})
    57  
    58  	It("Get kubeconfig dir", func() {
    59  		dir := GetKubeconfigDir()
    60  		Expect(len(dir) > 0).Should(BeTrue())
    61  	})
    62  
    63  	It("DoWithRetry", func() {
    64  		op := func() error {
    65  			return fmt.Errorf("test DowithRetry")
    66  		}
    67  		logger := logr.New(log.NullLogSink{})
    68  		Expect(DoWithRetry(context.TODO(), logger, op, &RetryOptions{MaxRetry: 2})).Should(HaveOccurred())
    69  	})
    70  
    71  	It("Config path", func() {
    72  		path := ConfigPath("")
    73  		Expect(len(path) == 0).Should(BeTrue())
    74  		path = ConfigPath("test")
    75  		Expect(len(path) > 0).Should(BeTrue())
    76  		Expect(RemoveConfig("")).Should(HaveOccurred())
    77  	})
    78  
    79  	It("Print yaml", func() {
    80  		obj := &unstructured.Unstructured{
    81  			Object: map[string]interface{}{
    82  				"apiVersion": "dataprotection.kubeblocks.io/v1alpha1",
    83  				"kind":       "BackupJob",
    84  				"metadata": map[string]interface{}{
    85  					"namespace": "default",
    86  					"name":      "test",
    87  				},
    88  				"spec": map[string]interface{}{
    89  					"backupPolicyName": "backup-policy-demo",
    90  					"backupType":       "full",
    91  					"ttl":              "7d",
    92  				},
    93  			},
    94  		}
    95  		Expect(PrintObjYAML(obj)).Should(Succeed())
    96  	})
    97  
    98  	It("Print go template", func() {
    99  		Expect(PrintGoTemplate(os.Stdout, `key: {{.Value}}`, struct {
   100  			Value string
   101  		}{"test"})).Should(Succeed())
   102  	})
   103  
   104  	It("GetNodeByName", func() {
   105  		nodes := []*corev1.Node{
   106  			{
   107  				ObjectMeta: metav1.ObjectMeta{
   108  					Name: "test",
   109  				},
   110  			},
   111  		}
   112  
   113  		testFn := func(name string) bool {
   114  			n := GetNodeByName(nodes, name)
   115  			if n != nil {
   116  				return n.Name == name
   117  			}
   118  			return false
   119  		}
   120  		Expect(testFn("test")).Should(BeTrue())
   121  		Expect(testFn("non-exists")).Should(BeFalse())
   122  	})
   123  
   124  	It("GetPodStatus", func() {
   125  		newPod := func(phase corev1.PodPhase) corev1.Pod {
   126  			return corev1.Pod{
   127  				Status: corev1.PodStatus{
   128  					Phase: phase,
   129  				}}
   130  		}
   131  
   132  		var pods []corev1.Pod
   133  		for _, p := range []corev1.PodPhase{corev1.PodRunning, corev1.PodPending, corev1.PodSucceeded, corev1.PodFailed} {
   134  			pods = append(pods, newPod(p))
   135  		}
   136  
   137  		r, w, s, f := GetPodStatus(pods)
   138  		Expect(r).Should(Equal(1))
   139  		Expect(w).Should(Equal(1))
   140  		Expect(s).Should(Equal(1))
   141  		Expect(f).Should(Equal(1))
   142  	})
   143  
   144  	It("TimeFormat", func() {
   145  		t, _ := time.Parse(time.RFC3339, "2023-01-04T01:00:00.000Z")
   146  		metav1Time := metav1.Time{Time: t}
   147  		Expect(TimeFormat(&metav1Time)).Should(Equal("Jan 04,2023 01:00 UTC+0000"))
   148  		Expect(TimeFormatWithDuration(&metav1Time, time.Minute)).Should(Equal("Jan 04,2023 01:00 UTC+0000"))
   149  		Expect(TimeFormatWithDuration(&metav1Time, time.Second)).Should(Equal("Jan 04,2023 01:00:00 UTC+0000"))
   150  		Expect(TimeFormatWithDuration(&metav1Time, time.Millisecond)).Should(Equal("Jan 04,2023 01:00:00.000 UTC+0000"))
   151  	})
   152  
   153  	It("CheckEmpty", func() {
   154  		res := ""
   155  		Expect(CheckEmpty(res)).Should(Equal(types.None))
   156  		res = "test"
   157  		Expect(CheckEmpty(res)).Should(Equal(res))
   158  	})
   159  
   160  	It("BuildLabelSelectorByNames", func() {
   161  		Expect(BuildLabelSelectorByNames("", nil)).Should(Equal(""))
   162  
   163  		names := []string{"n1", "n2"}
   164  		expected := fmt.Sprintf("%s in (%s)", constant.AppInstanceLabelKey, strings.Join(names, ","))
   165  		Expect(BuildLabelSelectorByNames("", names)).Should(Equal(expected))
   166  		Expect(BuildLabelSelectorByNames("label1", names)).Should(Equal("label1," + expected))
   167  	})
   168  
   169  	It("Event utils", func() {
   170  		objs := SortEventsByLastTimestamp(testing.FakeEvents(), "")
   171  		Expect(len(*objs)).Should(Equal(2))
   172  		firstEvent := (*objs)[0].(*corev1.Event)
   173  		secondEvent := (*objs)[1].(*corev1.Event)
   174  		Expect(firstEvent.LastTimestamp.Before(&secondEvent.LastTimestamp)).Should(BeTrue())
   175  		Expect(GetEventTimeStr(firstEvent)).Should(ContainSubstring("Jan 04,2023"))
   176  	})
   177  
   178  	It("Others", func() {
   179  		if os.Getenv("TEST_GET_PUBLIC_IP") != "" {
   180  			_, err := GetPublicIP()
   181  			Expect(err).ShouldNot(HaveOccurred())
   182  		}
   183  		Expect(MakeSSHKeyPair("", "")).Should(HaveOccurred())
   184  		Expect(SetKubeConfig("test")).Should(Succeed())
   185  		Expect(NewFactory()).ShouldNot(BeNil())
   186  
   187  		By("resource is empty")
   188  		res := resource.Quantity{}
   189  		Expect(ResourceIsEmpty(&res)).Should(BeTrue())
   190  		res.Set(20)
   191  		Expect(ResourceIsEmpty(&res)).Should(BeFalse())
   192  
   193  		By("GVRToString")
   194  		Expect(len(GVRToString(types.ClusterGVR())) > 0).Should(BeTrue())
   195  	})
   196  
   197  	It("IsSupportReconfigureParams", func() {
   198  		const (
   199  			ccName = "mysql_cc"
   200  			testNS = "default"
   201  		)
   202  
   203  		configConstraintObj := testapps.NewCustomizedObj("resources/mysql-config-constraint.yaml",
   204  			&appsv1alpha1.ConfigConstraint{}, testapps.WithNamespacedName(ccName, ""), func(cc *appsv1alpha1.ConfigConstraint) {
   205  				if ccContext, err := testdata.GetTestDataFileContent("/cue_testdata/mysql_for_cli.cue"); err == nil {
   206  					cc.Spec.ConfigurationSchema = &appsv1alpha1.CustomParametersValidation{
   207  						CUE: string(ccContext),
   208  					}
   209  				}
   210  			})
   211  		badcaseCCObject := configConstraintObj.DeepCopy()
   212  		badcaseCCObject.Spec.CfgSchemaTopLevelName = "badcase"
   213  		badcaseCCObject.SetName("badcase")
   214  
   215  		tf := cmdtesting.NewTestFactory().WithNamespace(testNS)
   216  		defer tf.Cleanup()
   217  
   218  		Expect(appsv1alpha1.AddToScheme(scheme.Scheme)).Should(Succeed())
   219  		mockClient := dynamicfakeclient.NewSimpleDynamicClientWithCustomListKinds(scheme.Scheme, nil, configConstraintObj, badcaseCCObject)
   220  		configSpec := appsv1alpha1.ComponentConfigSpec{
   221  			ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   222  				Name:        "for_test",
   223  				TemplateRef: ccName,
   224  				VolumeName:  "config",
   225  			},
   226  			ConfigConstraintRef: ccName,
   227  		}
   228  
   229  		type args struct {
   230  			configSpec    appsv1alpha1.ComponentConfigSpec
   231  			updatedParams map[string]string
   232  		}
   233  		tests := []struct {
   234  			name     string
   235  			args     args
   236  			expected bool
   237  			err      error
   238  		}{{
   239  			name: "normal test",
   240  			args: args{
   241  				configSpec:    configSpec,
   242  				updatedParams: testapps.WithMap("automatic_sp_privileges", "OFF", "innodb_autoinc_lock_mode", "1"),
   243  			},
   244  			expected: true,
   245  		}, {
   246  			name: "not match test",
   247  			args: args{
   248  				configSpec:    configSpec,
   249  				updatedParams: testapps.WithMap("not_exist_field", "1"),
   250  			},
   251  			expected: false,
   252  		}, {
   253  			name: "badcase test",
   254  			args: args{
   255  				configSpec: appsv1alpha1.ComponentConfigSpec{
   256  					ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   257  						Name:        "for_test",
   258  						TemplateRef: ccName,
   259  						VolumeName:  "config",
   260  					},
   261  					ConfigConstraintRef: "badcase",
   262  				},
   263  				updatedParams: testapps.WithMap("automatic_sp_privileges", "1"),
   264  			},
   265  			expected: false,
   266  			err:      cfgcore.MakeError("field not found:"),
   267  		}}
   268  
   269  		for _, tt := range tests {
   270  			ret, err := IsSupportReconfigureParams(tt.args.configSpec, cfgcore.FromStringPointerMap(tt.args.updatedParams), mockClient)
   271  			Expect(ret).Should(BeEquivalentTo(tt.expected))
   272  			Expect(err != nil).Should(BeEquivalentTo(tt.err != nil))
   273  			if err != nil {
   274  				Expect(err.Error()).Should(ContainSubstring(tt.err.Error()))
   275  			}
   276  		}
   277  	})
   278  
   279  	It("get IP location", func() {
   280  		_, _ = GetIPLocation()
   281  	})
   282  
   283  	It("get helm chart repo url", func() {
   284  		Expect(GetHelmChartRepoURL()).ShouldNot(BeEmpty())
   285  	})
   286  
   287  	It("new OpsRequest for Reconfiguring ", func() {
   288  		Expect(NewOpsRequestForReconfiguring("logs", "test", "cluster")).ShouldNot(BeNil())
   289  	})
   290  
   291  	It("convert obj to unstructured ", func() {
   292  		unstructuredObj, err := ConvertObjToUnstructured(testing.FakeConfigMap("cm-test", testing.Namespace, map[string]string{"fake": "fake"}))
   293  		Expect(err).ShouldNot(HaveOccurred())
   294  		Expect(unstructuredObj.Object).Should(HaveLen(4))
   295  
   296  		_, err = ConvertObjToUnstructured(struct{ name string }{name: "test"})
   297  		Expect(err).Should(HaveOccurred())
   298  	})
   299  
   300  	It("test build toleration", func() {
   301  		validRaws := []string{"dev=true:NoSchedule,large:NoSchedule"}
   302  		tolerations, err := BuildTolerations(validRaws)
   303  		Expect(err).Should(BeNil())
   304  		Expect(len(tolerations)).Should(Equal(2))
   305  
   306  		// optimize these codes
   307  		invalidRaws := []string{"dev=true"}
   308  		_, err = BuildTolerations(invalidRaws)
   309  		Expect(err).Should(HaveOccurred())
   310  	})
   311  
   312  	It("test build node affinity", func() {
   313  		nodeLabels := make(map[string]string)
   314  		Expect(BuildNodeAffinity(nodeLabels)).Should(BeNil())
   315  
   316  		nodeLabels["testNodeLabels"] = "testNodeLabels"
   317  		Expect(BuildNodeAffinity(nodeLabels)).ShouldNot(BeNil())
   318  	})
   319  
   320  	It("test build pod affinity", func() {
   321  		topologyKey := "testTopologyKey"
   322  
   323  		topologyKeys := []string{topologyKey}
   324  		podAntiAffinityStrategy := "testPodAntiAffinityStrategy"
   325  		podAntiAffinity := BuildPodAntiAffinity(podAntiAffinityStrategy, topologyKeys)
   326  		Expect(podAntiAffinity).ShouldNot(BeNil())
   327  		Expect(podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight).ShouldNot(BeNil())
   328  		Expect(podAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.TopologyKey).Should(Equal(topologyKey))
   329  	})
   330  })