blob: 719366c43a26d06c1ff9f0fdffaeccda92749002 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
)
func TestCtlPlaneConfig(t *testing.T) {
istiodConfigMap := map[string][]byte{
"istiod-7b69ff6f8c-fvjvw": []byte("Active scopes:\n ads:info"),
}
cases := []execTestCase{
{
args: strings.Split("admin", " "),
expectedString: "Manage istiod logging",
},
{
args: strings.Split("admin log -l app=invalid", " "),
expectedString: "no pods found",
wantException: true,
},
{
args: strings.Split("admin log", " "),
expectedString: "no pods found",
wantException: true,
},
{
execClientConfig: istiodConfigMap,
args: strings.Split("admin log istiod-7b69ff6f8c-fvjvw --level invalid", " "),
expectedString: "pattern invalid did not match",
wantException: true,
},
{
execClientConfig: istiodConfigMap,
args: strings.Split("admin log istiod-7b69ff6f8c-fvjvw --stack-trace-level invalid", " "),
expectedString: "pattern invalid did not match",
wantException: true,
},
{
args: strings.Split("admin log --reset --level invalid", " "),
expectedString: "--level cannot be combined with --reset",
wantException: true,
},
}
for i, c := range cases {
t.Run(fmt.Sprintf("case %d %s", i, strings.Join(c.args, " ")), func(t *testing.T) {
verifyExecTestOutput(t, c)
})
}
}
func Test_newScopeLevelPair(t *testing.T) {
validationPattern := `^\w+:(debug|error|warn|info|debug)`
type args struct {
slp string
validationPattern string
}
tests := []struct {
name string
args args
want *ScopeLevelPair
wantErr bool
}{
{
name: "Fail when logs scope-level pair don't match pattern",
args: args{validationPattern: validationPattern, slp: "invalid:pattern"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newScopeLevelPair(tt.args.slp, tt.args.validationPattern)
if (err != nil) != tt.wantErr {
t.Errorf("newScopeLevelPair() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("newScopeLevelPair() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_newScopeStackTraceLevelPair(t *testing.T) {
validationPattern := `^\w+:(debug|error|warn|info|debug)`
type args struct {
sslp string
validationPattern string
}
tests := []struct {
name string
args args
want *scopeStackTraceLevelPair
wantErr bool
}{
{
name: "Fail when logs scope-level pair don't match pattern",
args: args{validationPattern: validationPattern, sslp: "invalid:pattern"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newScopeStackTraceLevelPair(tt.args.sslp, tt.args.validationPattern)
if (err != nil) != tt.wantErr {
t.Errorf("newScopeStackTraceLevelPair() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("newScopeStackTraceLevelPair() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_chooseClientFlag(t *testing.T) {
url, _ := url.Parse("http://localhost/scopej/resource")
ctrzClient := &ControlzClient{
baseURL: url,
httpClient: &http.Client{},
}
type args struct {
ctrzClient *ControlzClient
reset bool
outputLogLevel string
stackTraceLevel string
outputFormat string
}
tests := []struct {
name string
args args
want *istiodConfigLog
}{
{
name: "given --reset flag return reset command",
args: args{
ctrzClient: ctrzClient,
reset: true,
outputLogLevel: "",
stackTraceLevel: "",
outputFormat: "",
},
want: &istiodConfigLog{state: &resetState{
client: ctrzClient,
}},
},
{
name: "given --level flag return outputLogLevel command",
args: args{
ctrzClient: ctrzClient,
reset: false,
outputLogLevel: "resource:info",
stackTraceLevel: "",
outputFormat: "",
},
want: &istiodConfigLog{state: &logLevelState{
client: ctrzClient,
outputLogLevel: "resource:info",
}},
},
{
name: "given --stack-trace-level flag return stackTraceLevelState",
args: args{
ctrzClient: ctrzClient,
reset: false,
outputLogLevel: "",
stackTraceLevel: "resource:info",
outputFormat: "",
},
want: &istiodConfigLog{
state: &stackTraceLevelState{
client: ctrzClient,
stackTraceLevel: "resource:info",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := chooseClientFlag(tt.args.ctrzClient, tt.args.reset, tt.args.outputLogLevel,
tt.args.stackTraceLevel, tt.args.outputFormat); !reflect.DeepEqual(got, tt.want) {
t.Errorf("chooseClientFlag() = %v, want %v", got, tt.want)
}
})
}
}
func resourceHandler(writer http.ResponseWriter, request *http.Request) {
const getResponse = `{"name":"resource","description":"Core resource model scope","output_level":"info","stack_trace_level":"none","log_callers":false}`
switch request.Method {
case http.MethodGet:
_, _ = writer.Write([]byte(getResponse))
}
}
func adsHandler(writer http.ResponseWriter, request *http.Request) {
const getResponse = `{"name":"ads","description":"ads debugging","output_level":"info","stack_trace_level":"none","log_callers":false}`
switch request.Method {
case http.MethodGet:
_, _ = writer.Write([]byte(getResponse))
}
}
func setupHTTPServer() (*httptest.Server, *url.URL) {
handler := http.NewServeMux()
handler.HandleFunc("/scopej/ads", adsHandler)
handler.HandleFunc("/scopej/resource", resourceHandler)
server := httptest.NewServer(handler)
url, _ := url.Parse(server.URL)
return server, url
}
func Test_flagState_run(t *testing.T) {
server, url := setupHTTPServer()
defer server.Close()
ctrzClientNoScopejHandler := &ControlzClient{
baseURL: url,
httpClient: &http.Client{},
}
tests := []struct {
name string
state flagState
want string
wantErr bool
}{
{
name: "resetState.run() should throw an error if the /scopej endpoint is missing",
state: &resetState{client: ctrzClientNoScopejHandler},
want: "",
wantErr: true,
},
{
name: "logLevelState.run() should throw an error if the /scopej endpoint is missing",
state: &logLevelState{
client: ctrzClientNoScopejHandler,
outputLogLevel: "test:debug",
},
want: "",
wantErr: true,
},
{
name: "stackTraceLevelState.run() should throw an error if the /scopej endpoint is missing",
state: &stackTraceLevelState{
client: ctrzClientNoScopejHandler,
stackTraceLevel: "test:debug",
},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.state.run()
if (err != nil) != tt.wantErr {
t.Errorf("run() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("run() got = %v, want %v", got, tt.want)
}
})
}
}