2017-09-16 20:19:33 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 The Kubernetes 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 options
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/util/diff"
|
|
|
|
utilflag "k8s.io/apiserver/pkg/util/flag"
|
|
|
|
)
|
|
|
|
|
|
|
|
func newKubeletServerOrDie() *KubeletServer {
|
|
|
|
s, err := NewKubeletServer()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func cleanFlags(s *KubeletServer) {
|
|
|
|
s.DynamicConfigDir = utilflag.NewStringFlag(s.DynamicConfigDir.Value())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestRoundTrip ensures that flag values from the Kubelet can be serialized
|
|
|
|
// to arguments and then read back and have the same value. Also catches cases
|
|
|
|
// where the default value reported by the flag is not actually allowed to be
|
|
|
|
// specified.
|
|
|
|
func TestRoundTrip(t *testing.T) {
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
inputFlags func() *KubeletServer
|
|
|
|
outputFlags func() *KubeletServer
|
|
|
|
flagDefaulter func(*pflag.FlagSet)
|
|
|
|
skipDefault bool
|
|
|
|
err bool
|
|
|
|
expectArgs bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "default flags are eliminated",
|
|
|
|
inputFlags: newKubeletServerOrDie,
|
|
|
|
outputFlags: newKubeletServerOrDie,
|
|
|
|
flagDefaulter: newKubeletServerOrDie().AddFlags,
|
|
|
|
err: false,
|
|
|
|
expectArgs: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "default flag values round trip",
|
|
|
|
inputFlags: newKubeletServerOrDie,
|
|
|
|
outputFlags: newKubeletServerOrDie,
|
|
|
|
flagDefaulter: func(*pflag.FlagSet) {},
|
|
|
|
err: false,
|
|
|
|
expectArgs: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "nil address does not fail for optional argument",
|
|
|
|
inputFlags: func() *KubeletServer {
|
|
|
|
s := newKubeletServerOrDie()
|
|
|
|
s.HealthzBindAddress = ""
|
|
|
|
return s
|
|
|
|
},
|
|
|
|
outputFlags: func() *KubeletServer {
|
|
|
|
s := newKubeletServerOrDie()
|
|
|
|
s.HealthzBindAddress = ""
|
|
|
|
return s
|
|
|
|
},
|
|
|
|
flagDefaulter: func(*pflag.FlagSet) {},
|
|
|
|
err: false,
|
|
|
|
expectArgs: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
|
|
modifiedFlags := testCase.inputFlags()
|
|
|
|
args := asArgs(modifiedFlags.AddFlags, testCase.flagDefaulter)
|
|
|
|
if testCase.expectArgs != (len(args) > 0) {
|
|
|
|
t.Errorf("%s: unexpected args: %v", testCase.name, args)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
t.Logf("%s: args: %v", testCase.name, args)
|
|
|
|
flagSet := pflag.NewFlagSet("output", pflag.ContinueOnError)
|
|
|
|
outputFlags := testCase.outputFlags()
|
|
|
|
outputFlags.AddFlags(flagSet)
|
|
|
|
if err := flagSet.Parse(args); err != nil {
|
|
|
|
if !testCase.err {
|
|
|
|
t.Errorf("%s: unexpected flag error: %v", testCase.name, err)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
cleanFlags(outputFlags)
|
|
|
|
if !reflect.DeepEqual(modifiedFlags, outputFlags) {
|
|
|
|
t.Errorf("%s: flags did not round trip: %s", testCase.name, diff.ObjectReflectDiff(modifiedFlags, outputFlags))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func asArgs(fn, defaultFn func(*pflag.FlagSet)) []string {
|
|
|
|
fs := pflag.NewFlagSet("extended", pflag.ContinueOnError)
|
|
|
|
fn(fs)
|
|
|
|
defaults := pflag.NewFlagSet("defaults", pflag.ContinueOnError)
|
|
|
|
defaultFn(defaults)
|
|
|
|
var args []string
|
|
|
|
fs.VisitAll(func(flag *pflag.Flag) {
|
2017-11-16 22:47:39 +00:00
|
|
|
// if the flag implements utilflag.OmitEmpty and the value is Empty, then just omit it from the command line
|
|
|
|
if omit, ok := flag.Value.(utilflag.OmitEmpty); ok && omit.Empty() {
|
|
|
|
return
|
|
|
|
}
|
2017-09-16 20:19:33 +00:00
|
|
|
s := flag.Value.String()
|
2017-11-16 22:47:39 +00:00
|
|
|
// if the flag has the same value as the default, we can omit it without changing the meaning of the command line
|
2017-09-16 20:19:33 +00:00
|
|
|
var defaultValue string
|
|
|
|
if defaultFlag := defaults.Lookup(flag.Name); defaultFlag != nil {
|
|
|
|
defaultValue = defaultFlag.Value.String()
|
|
|
|
if s == defaultValue {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2017-11-16 22:47:39 +00:00
|
|
|
// if the flag is a string slice, each element is specified with an independent flag invocation
|
2017-09-16 20:19:33 +00:00
|
|
|
if values, err := fs.GetStringSlice(flag.Name); err == nil {
|
|
|
|
for _, s := range values {
|
|
|
|
args = append(args, fmt.Sprintf("--%s=%s", flag.Name, s))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if len(s) == 0 {
|
|
|
|
s = defaultValue
|
|
|
|
}
|
|
|
|
args = append(args, fmt.Sprintf("--%s=%s", flag.Name, s))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return args
|
|
|
|
}
|