k8s.io/kubernetes@v1.29.3/pkg/kubelet/kubelet_server_journal_test.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package kubelet 18 19 import ( 20 "net/url" 21 "reflect" 22 "runtime" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/google/go-cmp/cmp" 28 "github.com/google/go-cmp/cmp/cmpopts" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 func Test_getLoggingCmd(t *testing.T) { 33 tests := []struct { 34 name string 35 args nodeLogQuery 36 wantLinux []string 37 wantWindows []string 38 wantOtherOS []string 39 }{ 40 { 41 args: nodeLogQuery{}, 42 wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, 43 wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, 44 }, 45 } 46 for _, tt := range tests { 47 t.Run(tt.name, func(t *testing.T) { 48 _, got, err := getLoggingCmd(&tt.args, []string{}) 49 switch os := runtime.GOOS; os { 50 case "linux": 51 if !reflect.DeepEqual(got, tt.wantLinux) { 52 t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux) 53 } 54 case "windows": 55 if !reflect.DeepEqual(got, tt.wantWindows) { 56 t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows) 57 } 58 default: 59 if err == nil { 60 t.Errorf("getLoggingCmd() = %v, want err", got) 61 } 62 } 63 }) 64 } 65 } 66 67 func Test_newNodeLogQuery(t *testing.T) { 68 validTimeValue := "2019-12-04T02:00:00Z" 69 validT, _ := time.Parse(time.RFC3339, validTimeValue) 70 tests := []struct { 71 name string 72 query url.Values 73 want *nodeLogQuery 74 wantErr bool 75 }{ 76 {name: "empty", query: url.Values{}, want: nil}, 77 {query: url.Values{"unknown": []string{"true"}}, want: nil}, 78 79 {query: url.Values{"sinceTime": []string{""}}, want: nil}, 80 {query: url.Values{"sinceTime": []string{"2019-12-04 02:00:00"}}, wantErr: true}, 81 {query: url.Values{"sinceTime": []string{"2019-12-04 02:00:00.000"}}, wantErr: true}, 82 {query: url.Values{"sinceTime": []string{"2019-12-04 02"}}, wantErr: true}, 83 {query: url.Values{"sinceTime": []string{"2019-12-04 02:00"}}, wantErr: true}, 84 {query: url.Values{"sinceTime": []string{validTimeValue}}, 85 want: &nodeLogQuery{options: options{SinceTime: &validT}}}, 86 87 {query: url.Values{"untilTime": []string{""}}, want: nil}, 88 {query: url.Values{"untilTime": []string{"2019-12-04 02:00:00"}}, wantErr: true}, 89 {query: url.Values{"untilTime": []string{"2019-12-04 02:00:00.000"}}, wantErr: true}, 90 {query: url.Values{"untilTime": []string{"2019-12-04 02"}}, wantErr: true}, 91 {query: url.Values{"untilTime": []string{"2019-12-04 02:00"}}, wantErr: true}, 92 {query: url.Values{"untilTime": []string{validTimeValue}}, 93 want: &nodeLogQuery{options: options{UntilTime: &validT}}}, 94 95 {query: url.Values{"tailLines": []string{"100"}}, want: &nodeLogQuery{options: options{TailLines: intPtr(100)}}}, 96 {query: url.Values{"tailLines": []string{"foo"}}, wantErr: true}, 97 {query: url.Values{"tailLines": []string{" "}}, wantErr: true}, 98 99 {query: url.Values{"pattern": []string{"foo"}}, want: &nodeLogQuery{options: options{Pattern: "foo"}}}, 100 101 {query: url.Values{"boot": []string{""}}, want: nil}, 102 {query: url.Values{"boot": []string{"0"}}, want: &nodeLogQuery{options: options{Boot: intPtr(0)}}}, 103 {query: url.Values{"boot": []string{"-23"}}, want: &nodeLogQuery{options: options{Boot: intPtr(-23)}}}, 104 {query: url.Values{"boot": []string{"foo"}}, wantErr: true}, 105 {query: url.Values{"boot": []string{" "}}, wantErr: true}, 106 107 {query: url.Values{"query": []string{""}}, wantErr: true}, 108 {query: url.Values{"query": []string{" ", " "}}, wantErr: true}, 109 {query: url.Values{"query": []string{"foo"}}, want: &nodeLogQuery{Services: []string{"foo"}}}, 110 {query: url.Values{"query": []string{"foo", "bar"}}, want: &nodeLogQuery{Services: []string{"foo", "bar"}}}, 111 {query: url.Values{"query": []string{"foo", "/bar"}}, want: &nodeLogQuery{Services: []string{"foo"}, 112 Files: []string{"/bar"}}}, 113 {query: url.Values{"query": []string{"/foo", `\bar`}}, want: &nodeLogQuery{Files: []string{"/foo", `\bar`}}}, 114 } 115 for _, tt := range tests { 116 t.Run(tt.query.Encode(), func(t *testing.T) { 117 got, err := newNodeLogQuery(tt.query) 118 if len(err) > 0 != tt.wantErr { 119 t.Errorf("newNodeLogQuery() error = %v, wantErr %v", err, tt.wantErr) 120 return 121 } 122 if !reflect.DeepEqual(got, tt.want) { 123 t.Errorf("different: %s", cmp.Diff(tt.want, got, cmpopts.IgnoreUnexported(nodeLogQuery{}))) 124 } 125 }) 126 } 127 } 128 129 func Test_validateServices(t *testing.T) { 130 var ( 131 service1 = "svc1" 132 service2 = "svc2" 133 service3 = "svc.foo" 134 service4 = "svc_foo" 135 service5 = "svc@foo" 136 service6 = "svc:foo" 137 invalid1 = "svc\n" 138 invalid2 = "svc.foo\n" 139 ) 140 tests := []struct { 141 name string 142 services []string 143 wantErr bool 144 }{ 145 {name: "one service", services: []string{service1}}, 146 {name: "two services", services: []string{service1, service2}}, 147 {name: "dot service", services: []string{service3}}, 148 {name: "underscore service", services: []string{service4}}, 149 {name: "at service", services: []string{service5}}, 150 {name: "colon service", services: []string{service6}}, 151 {name: "invalid service new line", services: []string{invalid1}, wantErr: true}, 152 {name: "invalid service with dot", services: []string{invalid2}, wantErr: true}, 153 {name: "long service", services: []string{strings.Repeat(service1, 100)}, wantErr: true}, 154 {name: "max number of services", services: []string{service1, service2, service3, service4, service5}, wantErr: true}, 155 } 156 for _, tt := range tests { 157 errs := validateServices(tt.services) 158 t.Run(tt.name, func(t *testing.T) { 159 if len(errs) > 0 != tt.wantErr { 160 t.Errorf("validateServices() error = %v, wantErr %v", errs, tt.wantErr) 161 return 162 } 163 }) 164 } 165 } 166 167 func Test_nodeLogQuery_validate(t *testing.T) { 168 var ( 169 service1 = "svc1" 170 service2 = "svc2" 171 file1 = "/test1.log" 172 file2 = "/test2.log" 173 pattern = "foo" 174 invalid = "foo\\" 175 ) 176 since, err := time.Parse(time.RFC3339, "2023-01-04T02:00:00Z") 177 assert.NoError(t, err) 178 until, err := time.Parse(time.RFC3339, "2023-02-04T02:00:00Z") 179 assert.NoError(t, err) 180 181 tests := []struct { 182 name string 183 Services []string 184 Files []string 185 options options 186 wantErr bool 187 }{ 188 {name: "empty", wantErr: true}, 189 {name: "empty with options", options: options{SinceTime: &since}, wantErr: true}, 190 {name: "one service", Services: []string{service1}}, 191 {name: "two services", Services: []string{service1, service2}}, 192 {name: "one service one file", Services: []string{service1}, Files: []string{file1}, wantErr: true}, 193 {name: "two files", Files: []string{file1, file2}, wantErr: true}, 194 {name: "one file options", Files: []string{file1}, options: options{Pattern: pattern}, wantErr: true}, 195 {name: "invalid pattern", Services: []string{service1}, options: options{Pattern: invalid}, wantErr: true}, 196 {name: "since", Services: []string{service1}, options: options{SinceTime: &since}}, 197 {name: "until", Services: []string{service1}, options: options{UntilTime: &until}}, 198 {name: "since until", Services: []string{service1}, options: options{SinceTime: &until, UntilTime: &since}, 199 wantErr: true}, 200 // boot is not supported on Windows. 201 {name: "boot", Services: []string{service1}, options: options{Boot: intPtr(-1)}, wantErr: runtime.GOOS == "windows"}, 202 {name: "boot out of range", Services: []string{service1}, options: options{Boot: intPtr(1)}, wantErr: true}, 203 {name: "tailLines", Services: []string{service1}, options: options{TailLines: intPtr(100)}}, 204 {name: "tailLines out of range", Services: []string{service1}, options: options{TailLines: intPtr(100000)}}, 205 } 206 for _, tt := range tests { 207 t.Run(tt.name, func(t *testing.T) { 208 n := &nodeLogQuery{ 209 Services: tt.Services, 210 Files: tt.Files, 211 options: tt.options, 212 } 213 errs := n.validate() 214 if len(errs) > 0 != tt.wantErr { 215 t.Errorf("nodeLogQuery.validate() error = %v, wantErr %v", errs, tt.wantErr) 216 return 217 } 218 }) 219 } 220 } 221 222 func intPtr(i int) *int { 223 return &i 224 }