github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/cluster/logs_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 cluster 21 22 import ( 23 "net/http" 24 "os" 25 "time" 26 27 . "github.com/onsi/ginkgo/v2" 28 . "github.com/onsi/gomega" 29 30 corev1 "k8s.io/api/core/v1" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/cli-runtime/pkg/genericiooptions" 34 "k8s.io/client-go/kubernetes/scheme" 35 restclient "k8s.io/client-go/rest" 36 "k8s.io/client-go/rest/fake" 37 cmdexec "k8s.io/kubectl/pkg/cmd/exec" 38 cmdlogs "k8s.io/kubectl/pkg/cmd/logs" 39 cmdtesting "k8s.io/kubectl/pkg/cmd/testing" 40 41 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 42 "github.com/1aal/kubeblocks/pkg/cli/cluster" 43 "github.com/1aal/kubeblocks/pkg/cli/exec" 44 "github.com/1aal/kubeblocks/pkg/constant" 45 ) 46 47 var _ = Describe("logs", func() { 48 It("isStdoutForContainer Test", func() { 49 o := &LogsOptions{} 50 Expect(o.isStdoutForContainer()).Should(BeTrue()) 51 o.fileType = "stdout" 52 Expect(o.isStdoutForContainer()).Should(BeTrue()) 53 o.fileType = "slow" 54 Expect(o.isStdoutForContainer()).Should(BeFalse()) 55 o.filePath = "/var/log/yum.log" 56 Expect(o.isStdoutForContainer()).Should(BeFalse()) 57 }) 58 59 It("prefixingWriter Test", func() { 60 pw := &prefixingWriter{ 61 prefix: []byte("prefix"), 62 writer: os.Stdout, 63 } 64 n, _ := pw.Write([]byte("")) 65 Expect(n).Should(Equal(0)) 66 num, _ := pw.Write([]byte("test")) 67 Expect(num).Should(Equal(4)) 68 }) 69 70 It("assembleTailCommand Test", func() { 71 command := assembleTail(true, 1, 100) 72 Expect(command).ShouldNot(BeNil()) 73 Expect(command).Should(Equal("tail -f --lines=1 --bytes=100")) 74 }) 75 76 It("addPrefixIfNeeded Test", func() { 77 l := &LogsOptions{ 78 ExecOptions: &exec.ExecOptions{ 79 StreamOptions: cmdexec.StreamOptions{ 80 ContainerName: "container", 81 }, 82 }, 83 } 84 // no set prefix 85 w := l.addPrefixIfNeeded(corev1.ObjectReference{}, os.Stdout) 86 Expect(w).Should(Equal(os.Stdout)) 87 // set prefix 88 o := corev1.ObjectReference{ 89 Name: "name", 90 FieldPath: "FieldPath", 91 } 92 l.logOptions.Prefix = true 93 w = l.addPrefixIfNeeded(o, os.Stdout) 94 _, ok := w.(*prefixingWriter) 95 Expect(ok).Should(BeTrue()) 96 }) 97 98 It("new logs command Test", func() { 99 tf := cmdtesting.NewTestFactory().WithNamespace("test") 100 defer tf.Cleanup() 101 codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) 102 ns := scheme.Codecs.WithoutConversion() 103 tf.Client = &fake.RESTClient{ 104 GroupVersion: schema.GroupVersion{Group: "", Version: "v1"}, 105 NegotiatedSerializer: ns, 106 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { 107 body := cmdtesting.ObjBody(codec, mockPod()) 108 return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil 109 }), 110 } 111 tf.ClientConfigVal = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: scheme.Codecs, GroupVersion: &schema.GroupVersion{Version: "v1"}}} 112 113 stream := genericiooptions.NewTestIOStreamsDiscard() 114 l := &LogsOptions{ 115 ExecOptions: exec.NewExecOptions(tf, stream), 116 logOptions: cmdlogs.LogsOptions{ 117 IOStreams: stream, 118 }, 119 } 120 121 cmd := NewLogsCmd(tf, stream) 122 Expect(cmd).ShouldNot(BeNil()) 123 Expect(cmd.Use).ShouldNot(BeNil()) 124 Expect(cmd.Example).ShouldNot(BeNil()) 125 126 // Complete without args 127 Expect(l.complete([]string{})).Should(MatchError("cluster name or instance name should be specified")) 128 // Complete with args 129 l.PodName = "foo" 130 l.Client, _ = l.Factory.KubernetesClientSet() 131 l.filePath = "/var/log" 132 Expect(l.complete([]string{"cluster-name"})).Should(HaveOccurred()) 133 Expect(l.clusterName).Should(Equal("cluster-name")) 134 // Validate stdout 135 l.filePath = "" 136 l.fileType = "" 137 l.Namespace = "test" 138 l.logOptions.SinceSeconds = time.Minute 139 Expect(l.complete([]string{"cluster-name"})).Should(Succeed()) 140 Expect(l.validate()).Should(Succeed()) 141 Expect(l.logOptions.Options).ShouldNot(BeNil()) 142 143 }) 144 145 It("createFileTypeCommand Test", func() { 146 pod := &corev1.Pod{ 147 ObjectMeta: metav1.ObjectMeta{ 148 Name: "foo", 149 Namespace: "test", 150 ResourceVersion: "10", 151 Labels: map[string]string{ 152 "app.kubernetes.io/name": "mysql-apecloud-mysql", 153 constant.KBAppComponentLabelKey: "component-name", 154 }, 155 }, 156 } 157 obj := cluster.NewClusterObjects() 158 l := &LogsOptions{} 159 // corner case 160 cmd, err := l.createFileTypeCommand(pod, obj) 161 Expect(cmd).Should(Equal("")) 162 Expect(err).Should(HaveOccurred()) 163 // normal case 164 obj.Cluster = &appsv1alpha1.Cluster{ 165 Spec: appsv1alpha1.ClusterSpec{ 166 ComponentSpecs: []appsv1alpha1.ClusterComponentSpec{ 167 { 168 Name: "component-name", 169 ComponentDefRef: "component-type", 170 }, 171 }, 172 }, 173 } 174 obj.ClusterDef = &appsv1alpha1.ClusterDefinition{ 175 Spec: appsv1alpha1.ClusterDefinitionSpec{ 176 ComponentDefs: []appsv1alpha1.ClusterComponentDefinition{ 177 { 178 Name: "component-type", 179 LogConfigs: []appsv1alpha1.LogConfig{ 180 { 181 Name: "slow", 182 FilePathPattern: "/log/mysql/*slow.log", 183 }, 184 { 185 Name: "error", 186 FilePathPattern: "/log/mysql/*.err", 187 }, 188 }, 189 }, 190 }, 191 }, 192 } 193 l.fileType = "slow" 194 cmd, err = l.createFileTypeCommand(pod, obj) 195 Expect(err).Should(BeNil()) 196 Expect(cmd).Should(Equal("ls /log/mysql/*slow.log | xargs tail --lines=0")) 197 // error case 198 l.fileType = "slow-error" 199 cmd, err = l.createFileTypeCommand(pod, obj) 200 Expect(err).Should(HaveOccurred()) 201 Expect(cmd).Should(Equal("")) 202 }) 203 })