Merge pull request #75593 from pohly/e2e-no-global-flags

e2e: avoid mandatory command line flags
This commit is contained in:
Kubernetes Prow Robot 2019-07-09 03:10:03 -07:00 committed by GitHub
commit a61006bbc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 322 additions and 161 deletions

View File

@ -61,9 +61,13 @@ import (
var viperConfig = flag.String("viper-config", "", "The name of a viper config file (https://github.com/spf13/viper#what-is-viper). All e2e command line parameters can also be configured in such a file. May contain a path and may or may not contain the file suffix. The default is to look for an optional file with `e2e` as base name. If a file is specified explicitly, it must be present.") var viperConfig = flag.String("viper-config", "", "The name of a viper config file (https://github.com/spf13/viper#what-is-viper). All e2e command line parameters can also be configured in such a file. May contain a path and may or may not contain the file suffix. The default is to look for an optional file with `e2e` as base name. If a file is specified explicitly, it must be present.")
func init() { func init() {
// Register framework flags, then handle flags and Viper config. // Register framework and test flags, then parse flags.
framework.HandleFlags() framework.HandleFlags()
if err := viperconfig.ViperizeFlags(*viperConfig, "e2e"); err != nil {
// Now that we know which Viper config (if any) was chosen,
// parse it and update those options which weren't already set via command line flags
// (which have higher priority).
if err := viperconfig.ViperizeFlags(*viperConfig, "e2e", flag.CommandLine); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }

View File

@ -97,6 +97,7 @@ go_library(
"//staging/src/k8s.io/client-go/util/retry:go_default_library", "//staging/src/k8s.io/client-go/util/retry:go_default_library",
"//staging/src/k8s.io/component-base/cli/flag:go_default_library", "//staging/src/k8s.io/component-base/cli/flag:go_default_library",
"//test/e2e/framework/auth:go_default_library", "//test/e2e/framework/auth:go_default_library",
"//test/e2e/framework/config:go_default_library",
"//test/e2e/framework/ginkgowrapper:go_default_library", "//test/e2e/framework/ginkgowrapper:go_default_library",
"//test/e2e/framework/log:go_default_library", "//test/e2e/framework/log:go_default_library",
"//test/e2e/framework/metrics:go_default_library", "//test/e2e/framework/metrics:go_default_library",

View File

@ -15,11 +15,22 @@ limitations under the License.
*/ */
// Package config simplifies the declaration of configuration options. // Package config simplifies the declaration of configuration options.
// Right now the implementation maps them directly command line // Right now the implementation maps them directly to command line
// flags. When combined with test/e2e/framework/viper in a test suite, // flags. When combined with test/e2e/framework/viperconfig in a test
// those flags then can also be read from a config file. // suite, those flags then can also be read from a config file.
// //
// Instead of defining flags one-by-one, developers annotate a // The command line flags all get stored in a private flag set. The
// developer of the E2E test suite decides how they are exposed. Options
// include:
// - exposing as normal flags in the actual command line:
// CopyFlags(Flags, flag.CommandLine)
// - populate via test/e2e/framework/viperconfig:
// viperconfig.ViperizeFlags("my-config.yaml", "", Flags)
// - a combination of both:
// CopyFlags(Flags, flag.CommandLine)
// viperconfig.ViperizeFlags("my-config.yaml", "", flag.CommandLine)
//
// Instead of defining flags one-by-one, test developers annotate a
// structure with tags and then call a single function. This is the // structure with tags and then call a single function. This is the
// same approach as in https://godoc.org/github.com/jessevdk/go-flags, // same approach as in https://godoc.org/github.com/jessevdk/go-flags,
// but implemented so that a test suite can continue to use the normal // but implemented so that a test suite can continue to use the normal
@ -84,10 +95,23 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// CommandLine is the flag set that AddOptions adds to. Usually this // Flags is the flag set that AddOptions adds to. Test authors should
// is the same as the default in the flag package, but can also be // also use it instead of directly adding to the global command line.
// something else (for example during testing). var Flags = flag.NewFlagSet("", flag.ContinueOnError)
var CommandLine = flag.CommandLine
// CopyFlags ensures that all flags that are defined in the source flag
// set appear in the target flag set as if they had been defined there
// directly. From the flag package it inherits the behavior that there
// is a panic if the target already contains a flag from the source.
func CopyFlags(source *flag.FlagSet, target *flag.FlagSet) {
source.VisitAll(func(flag *flag.Flag) {
// We don't need to copy flag.DefValue. The original
// default (from, say, flag.String) was stored in
// the value and gets extracted by Var for the help
// message.
target.Var(flag.Value, flag.Name, flag.Usage)
})
}
// AddOptions analyzes the options value and creates the necessary // AddOptions analyzes the options value and creates the necessary
// flags to populate it. // flags to populate it.
@ -102,6 +126,11 @@ var CommandLine = flag.CommandLine
// It panics when it encounters an error, like unsupported types // It panics when it encounters an error, like unsupported types
// or option name conflicts. // or option name conflicts.
func AddOptions(options interface{}, prefix string) bool { func AddOptions(options interface{}, prefix string) bool {
return AddOptionsToSet(Flags, options, prefix)
}
// AddOptionsToSet is the same as AddOption, except that it allows choosing the flag set.
func AddOptionsToSet(flags *flag.FlagSet, options interface{}, prefix string) bool {
optionsType := reflect.TypeOf(options) optionsType := reflect.TypeOf(options)
if optionsType == nil { if optionsType == nil {
panic("options parameter without a type - nil?!") panic("options parameter without a type - nil?!")
@ -109,11 +138,11 @@ func AddOptions(options interface{}, prefix string) bool {
if optionsType.Kind() != reflect.Ptr || optionsType.Elem().Kind() != reflect.Struct { if optionsType.Kind() != reflect.Ptr || optionsType.Elem().Kind() != reflect.Struct {
panic(fmt.Sprintf("need a pointer to a struct, got instead: %T", options)) panic(fmt.Sprintf("need a pointer to a struct, got instead: %T", options))
} }
addStructFields(optionsType.Elem(), reflect.Indirect(reflect.ValueOf(options)), prefix) addStructFields(flags, optionsType.Elem(), reflect.Indirect(reflect.ValueOf(options)), prefix)
return true return true
} }
func addStructFields(structType reflect.Type, structValue reflect.Value, prefix string) { func addStructFields(flags *flag.FlagSet, structType reflect.Type, structValue reflect.Value, prefix string) {
for i := 0; i < structValue.NumField(); i++ { for i := 0; i < structValue.NumField(); i++ {
entry := structValue.Field(i) entry := structValue.Field(i)
addr := entry.Addr() addr := entry.Addr()
@ -134,12 +163,12 @@ func addStructFields(structType reflect.Type, structValue reflect.Value, prefix
// Entries in embedded fields are treated like // Entries in embedded fields are treated like
// entries, in the struct itself, i.e. we add // entries, in the struct itself, i.e. we add
// them with the same prefix. // them with the same prefix.
addStructFields(structField.Type, entry, prefix) addStructFields(flags, structField.Type, entry, prefix)
continue continue
} }
if structField.Type.Kind() == reflect.Struct { if structField.Type.Kind() == reflect.Struct {
// Add nested options. // Add nested options.
addStructFields(structField.Type, entry, name) addStructFields(flags, structField.Type, entry, name)
continue continue
} }
// We could switch based on structField.Type. Doing a // We could switch based on structField.Type. Doing a
@ -153,33 +182,33 @@ func addStructFields(structType reflect.Type, structValue reflect.Value, prefix
case *bool: case *bool:
var defValue bool var defValue bool
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.BoolVar(ptr, name, defValue, usage) flags.BoolVar(ptr, name, defValue, usage)
case *time.Duration: case *time.Duration:
var defValue time.Duration var defValue time.Duration
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.DurationVar(ptr, name, defValue, usage) flags.DurationVar(ptr, name, defValue, usage)
case *float64: case *float64:
var defValue float64 var defValue float64
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.Float64Var(ptr, name, defValue, usage) flags.Float64Var(ptr, name, defValue, usage)
case *string: case *string:
CommandLine.StringVar(ptr, name, def, usage) flags.StringVar(ptr, name, def, usage)
case *int: case *int:
var defValue int var defValue int
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.IntVar(ptr, name, defValue, usage) flags.IntVar(ptr, name, defValue, usage)
case *int64: case *int64:
var defValue int64 var defValue int64
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.Int64Var(ptr, name, defValue, usage) flags.Int64Var(ptr, name, defValue, usage)
case *uint: case *uint:
var defValue uint var defValue uint
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.UintVar(ptr, name, defValue, usage) flags.UintVar(ptr, name, defValue, usage)
case *uint64: case *uint64:
var defValue uint64 var defValue uint64
parseDefault(&defValue, name, def) parseDefault(&defValue, name, def)
CommandLine.Uint64Var(ptr, name, defValue, usage) flags.Uint64Var(ptr, name, defValue, usage)
default: default:
panic(fmt.Sprintf("unsupported struct entry type %q: %T", name, entry.Interface())) panic(fmt.Sprintf("unsupported struct entry type %q: %T", name, entry.Interface()))
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package config package config
import ( import (
"bytes"
"flag" "flag"
"testing" "testing"
"time" "time"
@ -26,12 +27,12 @@ import (
) )
func TestInt(t *testing.T) { func TestInt(t *testing.T) {
CommandLine = flag.NewFlagSet("test", 0) flags := flag.NewFlagSet("test", 0)
var context struct { var context struct {
Number int `default:"5" usage:"some number"` Number int `default:"5" usage:"some number"`
} }
require.NotPanics(t, func() { require.NotPanics(t, func() {
AddOptions(&context, "") AddOptionsToSet(flags, &context, "")
}) })
require.Equal(t, []simpleFlag{ require.Equal(t, []simpleFlag{
{ {
@ -39,18 +40,18 @@ func TestInt(t *testing.T) {
usage: "some number", usage: "some number",
defValue: "5", defValue: "5",
}}, }},
allFlags(CommandLine)) allFlags(flags))
assert.Equal(t, 5, context.Number) assert.Equal(t, 5, context.Number)
} }
func TestLower(t *testing.T) { func TestLower(t *testing.T) {
CommandLine = flag.NewFlagSet("test", 0) flags := flag.NewFlagSet("test", 0)
var context struct { var context struct {
Ähem string Ähem string
MixedCase string MixedCase string
} }
require.NotPanics(t, func() { require.NotPanics(t, func() {
AddOptions(&context, "") AddOptionsToSet(flags, &context, "")
}) })
require.Equal(t, []simpleFlag{ require.Equal(t, []simpleFlag{
{ {
@ -60,16 +61,16 @@ func TestLower(t *testing.T) {
name: "ähem", name: "ähem",
}, },
}, },
allFlags(CommandLine)) allFlags(flags))
} }
func TestPrefix(t *testing.T) { func TestPrefix(t *testing.T) {
CommandLine = flag.NewFlagSet("test", 0) flags := flag.NewFlagSet("test", 0)
var context struct { var context struct {
Number int `usage:"some number"` Number int `usage:"some number"`
} }
require.NotPanics(t, func() { require.NotPanics(t, func() {
AddOptions(&context, "some.prefix") AddOptionsToSet(flags, &context, "some.prefix")
}) })
require.Equal(t, []simpleFlag{ require.Equal(t, []simpleFlag{
{ {
@ -77,11 +78,11 @@ func TestPrefix(t *testing.T) {
usage: "some number", usage: "some number",
defValue: "0", defValue: "0",
}}, }},
allFlags(CommandLine)) allFlags(flags))
} }
func TestRecursion(t *testing.T) { func TestRecursion(t *testing.T) {
CommandLine = flag.NewFlagSet("test", 0) flags := flag.NewFlagSet("test", 0)
type Nested struct { type Nested struct {
Number1 int `usage:"embedded number"` Number1 int `usage:"embedded number"`
} }
@ -96,7 +97,7 @@ func TestRecursion(t *testing.T) {
} }
} }
require.NotPanics(t, func() { require.NotPanics(t, func() {
AddOptions(&context, "") AddOptionsToSet(flags, &context, "")
}) })
require.Equal(t, []simpleFlag{ require.Equal(t, []simpleFlag{
{ {
@ -110,38 +111,39 @@ func TestRecursion(t *testing.T) {
defValue: "0", defValue: "0",
}, },
}, },
allFlags(CommandLine)) allFlags(flags))
} }
func TestPanics(t *testing.T) { func TestPanics(t *testing.T) {
flags := flag.NewFlagSet("test", 0)
assert.PanicsWithValue(t, `invalid default "a" for int entry prefix.number: strconv.Atoi: parsing "a": invalid syntax`, func() { assert.PanicsWithValue(t, `invalid default "a" for int entry prefix.number: strconv.Atoi: parsing "a": invalid syntax`, func() {
var context struct { var context struct {
Number int `default:"a"` Number int `default:"a"`
} }
AddOptions(&context, "prefix") AddOptionsToSet(flags, &context, "prefix")
}) })
assert.PanicsWithValue(t, `invalid default "10000000000000000000" for int entry prefix.number: strconv.Atoi: parsing "10000000000000000000": value out of range`, func() { assert.PanicsWithValue(t, `invalid default "10000000000000000000" for int entry prefix.number: strconv.Atoi: parsing "10000000000000000000": value out of range`, func() {
var context struct { var context struct {
Number int `default:"10000000000000000000"` Number int `default:"10000000000000000000"`
} }
AddOptions(&context, "prefix") AddOptionsToSet(flags, &context, "prefix")
}) })
assert.PanicsWithValue(t, `options parameter without a type - nil?!`, func() { assert.PanicsWithValue(t, `options parameter without a type - nil?!`, func() {
AddOptions(nil, "") AddOptionsToSet(flags, nil, "")
}) })
assert.PanicsWithValue(t, `need a pointer to a struct, got instead: *int`, func() { assert.PanicsWithValue(t, `need a pointer to a struct, got instead: *int`, func() {
number := 0 number := 0
AddOptions(&number, "") AddOptionsToSet(flags, &number, "")
}) })
assert.PanicsWithValue(t, `struct entry "prefix.number" not exported`, func() { assert.PanicsWithValue(t, `struct entry "prefix.number" not exported`, func() {
var context struct { var context struct {
number int number int
} }
AddOptions(&context, "prefix") AddOptionsToSet(flags, &context, "prefix")
}) })
assert.PanicsWithValue(t, `unsupported struct entry type "prefix.someNumber": config.MyInt`, func() { assert.PanicsWithValue(t, `unsupported struct entry type "prefix.someNumber": config.MyInt`, func() {
@ -149,12 +151,28 @@ func TestPanics(t *testing.T) {
var context struct { var context struct {
SomeNumber MyInt SomeNumber MyInt
} }
AddOptions(&context, "prefix") AddOptionsToSet(flags, &context, "prefix")
}) })
} }
type TypesTestCase struct {
name string
copyFlags bool
}
func TestTypes(t *testing.T) { func TestTypes(t *testing.T) {
CommandLine = flag.NewFlagSet("test", 0) testcases := []TypesTestCase{
{name: "directly"},
{name: "CopyFlags", copyFlags: true},
}
for _, testcase := range testcases {
testTypes(t, testcase)
}
}
func testTypes(t *testing.T, testcase TypesTestCase) {
flags := flag.NewFlagSet("test", 0)
type Context struct { type Context struct {
Bool bool `default:"true"` Bool bool `default:"true"`
Duration time.Duration `default:"1ms"` Duration time.Duration `default:"1ms"`
@ -167,8 +185,25 @@ func TestTypes(t *testing.T) {
} }
var context Context var context Context
require.NotPanics(t, func() { require.NotPanics(t, func() {
AddOptions(&context, "") AddOptionsToSet(flags, &context, "")
}) })
if testcase.copyFlags {
original := bytes.Buffer{}
flags.SetOutput(&original)
flags.PrintDefaults()
flags2 := flag.NewFlagSet("test", 0)
CopyFlags(flags, flags2)
flags = flags2
copy := bytes.Buffer{}
flags.SetOutput(&copy)
flags.PrintDefaults()
assert.Equal(t, original.String(), copy.String(), testcase.name+": help messages equal")
assert.Contains(t, copy.String(), "some number", testcase.name+": copied help message contains defaults")
}
require.Equal(t, []simpleFlag{ require.Equal(t, []simpleFlag{
{ {
name: "bool", name: "bool",
@ -205,14 +240,14 @@ func TestTypes(t *testing.T) {
defValue: "1234567890123456789", defValue: "1234567890123456789",
}, },
}, },
allFlags(CommandLine)) allFlags(flags), testcase.name)
assert.Equal(t, assert.Equal(t,
Context{true, time.Millisecond, 1.23456789, "hello world", Context{true, time.Millisecond, 1.23456789, "hello world",
-1, -1234567890123456789, 1, 1234567890123456789, -1, -1234567890123456789, 1, 1234567890123456789,
}, },
context, context,
"default values must match") "default values must match")
require.NoError(t, CommandLine.Parse([]string{ require.NoError(t, flags.Parse([]string{
"-int", "-2", "-int", "-2",
"-int64", "-9123456789012345678", "-int64", "-9123456789012345678",
"-uint", "2", "-uint", "2",
@ -221,13 +256,13 @@ func TestTypes(t *testing.T) {
"-float64", "-1.23456789", "-float64", "-1.23456789",
"-bool=false", "-bool=false",
"-duration=1s", "-duration=1s",
})) }), testcase.name)
assert.Equal(t, assert.Equal(t,
Context{false, time.Second, -1.23456789, "pong", Context{false, time.Second, -1.23456789, "pong",
-2, -9123456789012345678, 2, 9123456789012345678, -2, -9123456789012345678, 2, 9123456789012345678,
}, },
context, context,
"parsed values must match") testcase.name+": parsed values must match")
} }
func allFlags(fs *flag.FlagSet) []simpleFlag { func allFlags(fs *flag.FlagSet) []simpleFlag {

View File

@ -33,6 +33,7 @@ import (
cliflag "k8s.io/component-base/cli/flag" cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog" "k8s.io/klog"
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
e2econfig "k8s.io/kubernetes/test/e2e/framework/config"
e2elog "k8s.io/kubernetes/test/e2e/framework/log" e2elog "k8s.io/kubernetes/test/e2e/framework/log"
) )
@ -230,7 +231,17 @@ type CloudConfig struct {
var TestContext TestContextType var TestContext TestContextType
// RegisterCommonFlags registers flags common to all e2e test suites. // RegisterCommonFlags registers flags common to all e2e test suites.
func RegisterCommonFlags() { // The flag set can be flag.CommandLine (if desired) or a custom
// flag set that then gets passed to viperconfig.ViperizeFlags.
//
// The other Register*Flags methods below can be used to add more
// test-specific flags. However, those settings then get added
// regardless whether the test is actually in the test suite.
//
// For tests that have been converted to registering their
// options themselves, copy flags from test/e2e/framework/config
// as shown in HandleFlags.
func RegisterCommonFlags(flags *flag.FlagSet) {
// Turn on verbose by default to get spec names // Turn on verbose by default to get spec names
config.DefaultReporterConfig.Verbose = true config.DefaultReporterConfig.Verbose = true
@ -240,117 +251,118 @@ func RegisterCommonFlags() {
// Randomize specs as well as suites // Randomize specs as well as suites
config.GinkgoConfig.RandomizeAllSpecs = true config.GinkgoConfig.RandomizeAllSpecs = true
flag.StringVar(&TestContext.GatherKubeSystemResourceUsageData, "gather-resource-usage", "false", "If set to 'true' or 'all' framework will be monitoring resource usage of system all add-ons in (some) e2e tests, if set to 'master' framework will be monitoring master node only, if set to 'none' of 'false' monitoring will be turned off.") flags.StringVar(&TestContext.GatherKubeSystemResourceUsageData, "gather-resource-usage", "false", "If set to 'true' or 'all' framework will be monitoring resource usage of system all add-ons in (some) e2e tests, if set to 'master' framework will be monitoring master node only, if set to 'none' of 'false' monitoring will be turned off.")
flag.BoolVar(&TestContext.GatherLogsSizes, "gather-logs-sizes", false, "If set to true framework will be monitoring logs sizes on all machines running e2e tests.") flags.BoolVar(&TestContext.GatherLogsSizes, "gather-logs-sizes", false, "If set to true framework will be monitoring logs sizes on all machines running e2e tests.")
flag.IntVar(&TestContext.MaxNodesToGather, "max-nodes-to-gather-from", 20, "The maximum number of nodes to gather extended info from on test failure.") flags.IntVar(&TestContext.MaxNodesToGather, "max-nodes-to-gather-from", 20, "The maximum number of nodes to gather extended info from on test failure.")
flag.StringVar(&TestContext.GatherMetricsAfterTest, "gather-metrics-at-teardown", "false", "If set to 'true' framework will gather metrics from all components after each test. If set to 'master' only master component metrics would be gathered.") flags.StringVar(&TestContext.GatherMetricsAfterTest, "gather-metrics-at-teardown", "false", "If set to 'true' framework will gather metrics from all components after each test. If set to 'master' only master component metrics would be gathered.")
flag.BoolVar(&TestContext.GatherSuiteMetricsAfterTest, "gather-suite-metrics-at-teardown", false, "If set to true framwork will gather metrics from all components after the whole test suite completes.") flags.BoolVar(&TestContext.GatherSuiteMetricsAfterTest, "gather-suite-metrics-at-teardown", false, "If set to true framwork will gather metrics from all components after the whole test suite completes.")
flag.BoolVar(&TestContext.AllowGatheringProfiles, "allow-gathering-profiles", true, "If set to true framework will allow to gather CPU/memory allocation pprof profiles from the master.") flags.BoolVar(&TestContext.AllowGatheringProfiles, "allow-gathering-profiles", true, "If set to true framework will allow to gather CPU/memory allocation pprof profiles from the master.")
flag.BoolVar(&TestContext.IncludeClusterAutoscalerMetrics, "include-cluster-autoscaler", false, "If set to true, framework will include Cluster Autoscaler when gathering metrics.") flags.BoolVar(&TestContext.IncludeClusterAutoscalerMetrics, "include-cluster-autoscaler", false, "If set to true, framework will include Cluster Autoscaler when gathering metrics.")
flag.StringVar(&TestContext.OutputPrintType, "output-print-type", "json", "Format in which summaries should be printed: 'hr' for human readable, 'json' for JSON ones.") flags.StringVar(&TestContext.OutputPrintType, "output-print-type", "json", "Format in which summaries should be printed: 'hr' for human readable, 'json' for JSON ones.")
flag.BoolVar(&TestContext.DumpLogsOnFailure, "dump-logs-on-failure", true, "If set to true test will dump data about the namespace in which test was running.") flags.BoolVar(&TestContext.DumpLogsOnFailure, "dump-logs-on-failure", true, "If set to true test will dump data about the namespace in which test was running.")
flag.BoolVar(&TestContext.DisableLogDump, "disable-log-dump", false, "If set to true, logs from master and nodes won't be gathered after test run.") flags.BoolVar(&TestContext.DisableLogDump, "disable-log-dump", false, "If set to true, logs from master and nodes won't be gathered after test run.")
flag.StringVar(&TestContext.LogexporterGCSPath, "logexporter-gcs-path", "", "Path to the GCS artifacts directory to dump logs from nodes. Logexporter gets enabled if this is non-empty.") flags.StringVar(&TestContext.LogexporterGCSPath, "logexporter-gcs-path", "", "Path to the GCS artifacts directory to dump logs from nodes. Logexporter gets enabled if this is non-empty.")
flag.BoolVar(&TestContext.DeleteNamespace, "delete-namespace", true, "If true tests will delete namespace after completion. It is only designed to make debugging easier, DO NOT turn it off by default.") flags.BoolVar(&TestContext.DeleteNamespace, "delete-namespace", true, "If true tests will delete namespace after completion. It is only designed to make debugging easier, DO NOT turn it off by default.")
flag.BoolVar(&TestContext.DeleteNamespaceOnFailure, "delete-namespace-on-failure", true, "If true, framework will delete test namespace on failure. Used only during test debugging.") flags.BoolVar(&TestContext.DeleteNamespaceOnFailure, "delete-namespace-on-failure", true, "If true, framework will delete test namespace on failure. Used only during test debugging.")
flag.IntVar(&TestContext.AllowedNotReadyNodes, "allowed-not-ready-nodes", 0, "If non-zero, framework will allow for that many non-ready nodes when checking for all ready nodes.") flags.IntVar(&TestContext.AllowedNotReadyNodes, "allowed-not-ready-nodes", 0, "If non-zero, framework will allow for that many non-ready nodes when checking for all ready nodes.")
flag.StringVar(&TestContext.Host, "host", "", fmt.Sprintf("The host, or apiserver, to connect to. Will default to %s if this argument and --kubeconfig are not set", defaultHost)) flags.StringVar(&TestContext.Host, "host", "", fmt.Sprintf("The host, or apiserver, to connect to. Will default to %s if this argument and --kubeconfig are not set", defaultHost))
flag.StringVar(&TestContext.ReportPrefix, "report-prefix", "", "Optional prefix for JUnit XML reports. Default is empty, which doesn't prepend anything to the default name.") flags.StringVar(&TestContext.ReportPrefix, "report-prefix", "", "Optional prefix for JUnit XML reports. Default is empty, which doesn't prepend anything to the default name.")
flag.StringVar(&TestContext.ReportDir, "report-dir", "", "Path to the directory where the JUnit XML reports should be saved. Default is empty, which doesn't generate these reports.") flags.StringVar(&TestContext.ReportDir, "report-dir", "", "Path to the directory where the JUnit XML reports should be saved. Default is empty, which doesn't generate these reports.")
flag.Var(cliflag.NewMapStringBool(&TestContext.FeatureGates), "feature-gates", "A set of key=value pairs that describe feature gates for alpha/experimental features.") flags.Var(cliflag.NewMapStringBool(&TestContext.FeatureGates), "feature-gates", "A set of key=value pairs that describe feature gates for alpha/experimental features.")
flag.StringVar(&TestContext.ContainerRuntime, "container-runtime", "docker", "The container runtime of cluster VM instances (docker/remote).") flags.StringVar(&TestContext.ContainerRuntime, "container-runtime", "docker", "The container runtime of cluster VM instances (docker/remote).")
flag.StringVar(&TestContext.ContainerRuntimeEndpoint, "container-runtime-endpoint", "unix:///var/run/dockershim.sock", "The container runtime endpoint of cluster VM instances.") flags.StringVar(&TestContext.ContainerRuntimeEndpoint, "container-runtime-endpoint", "unix:///var/run/dockershim.sock", "The container runtime endpoint of cluster VM instances.")
flag.StringVar(&TestContext.ContainerRuntimeProcessName, "container-runtime-process-name", "dockerd", "The name of the container runtime process.") flags.StringVar(&TestContext.ContainerRuntimeProcessName, "container-runtime-process-name", "dockerd", "The name of the container runtime process.")
flag.StringVar(&TestContext.ContainerRuntimePidFile, "container-runtime-pid-file", "/var/run/docker.pid", "The pid file of the container runtime.") flags.StringVar(&TestContext.ContainerRuntimePidFile, "container-runtime-pid-file", "/var/run/docker.pid", "The pid file of the container runtime.")
flag.StringVar(&TestContext.SystemdServices, "systemd-services", "docker", "The comma separated list of systemd services the framework will dump logs for.") flags.StringVar(&TestContext.SystemdServices, "systemd-services", "docker", "The comma separated list of systemd services the framework will dump logs for.")
flag.BoolVar(&TestContext.DumpSystemdJournal, "dump-systemd-journal", false, "Whether to dump the full systemd journal.") flags.BoolVar(&TestContext.DumpSystemdJournal, "dump-systemd-journal", false, "Whether to dump the full systemd journal.")
flag.StringVar(&TestContext.ImageServiceEndpoint, "image-service-endpoint", "", "The image service endpoint of cluster VM instances.") flags.StringVar(&TestContext.ImageServiceEndpoint, "image-service-endpoint", "", "The image service endpoint of cluster VM instances.")
flag.StringVar(&TestContext.DockershimCheckpointDir, "dockershim-checkpoint-dir", "/var/lib/dockershim/sandbox", "The directory for dockershim to store sandbox checkpoints.") flags.StringVar(&TestContext.DockershimCheckpointDir, "dockershim-checkpoint-dir", "/var/lib/dockershim/sandbox", "The directory for dockershim to store sandbox checkpoints.")
flag.StringVar(&TestContext.KubernetesAnywherePath, "kubernetes-anywhere-path", "/workspace/k8s.io/kubernetes-anywhere", "Which directory kubernetes-anywhere is installed to.") flags.StringVar(&TestContext.KubernetesAnywherePath, "kubernetes-anywhere-path", "/workspace/k8s.io/kubernetes-anywhere", "Which directory kubernetes-anywhere is installed to.")
flag.BoolVar(&TestContext.ListImages, "list-images", false, "If true, will show list of images used for runnning tests.") flags.BoolVar(&TestContext.ListImages, "list-images", false, "If true, will show list of images used for runnning tests.")
} }
// RegisterClusterFlags registers flags specific to the cluster e2e test suite. // RegisterClusterFlags registers flags specific to the cluster e2e test suite.
func RegisterClusterFlags() { func RegisterClusterFlags(flags *flag.FlagSet) {
flag.BoolVar(&TestContext.VerifyServiceAccount, "e2e-verify-service-account", true, "If true tests will verify the service account before running.") flags.BoolVar(&TestContext.VerifyServiceAccount, "e2e-verify-service-account", true, "If true tests will verify the service account before running.")
flag.StringVar(&TestContext.KubeConfig, clientcmd.RecommendedConfigPathFlag, os.Getenv(clientcmd.RecommendedConfigPathEnvVar), "Path to kubeconfig containing embedded authinfo.") flags.StringVar(&TestContext.KubeConfig, clientcmd.RecommendedConfigPathFlag, os.Getenv(clientcmd.RecommendedConfigPathEnvVar), "Path to kubeconfig containing embedded authinfo.")
flag.StringVar(&TestContext.KubeContext, clientcmd.FlagContext, "", "kubeconfig context to use/override. If unset, will use value from 'current-context'") flags.StringVar(&TestContext.KubeContext, clientcmd.FlagContext, "", "kubeconfig context to use/override. If unset, will use value from 'current-context'")
flag.StringVar(&TestContext.KubeAPIContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "ContentType used to communicate with apiserver") flags.StringVar(&TestContext.KubeAPIContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "ContentType used to communicate with apiserver")
flag.StringVar(&TestContext.KubeVolumeDir, "volume-dir", "/var/lib/kubelet", "Path to the directory containing the kubelet volumes.") flags.StringVar(&TestContext.KubeVolumeDir, "volume-dir", "/var/lib/kubelet", "Path to the directory containing the kubelet volumes.")
flag.StringVar(&TestContext.CertDir, "cert-dir", "", "Path to the directory containing the certs. Default is empty, which doesn't use certs.") flags.StringVar(&TestContext.CertDir, "cert-dir", "", "Path to the directory containing the certs. Default is empty, which doesn't use certs.")
flag.StringVar(&TestContext.RepoRoot, "repo-root", "../../", "Root directory of kubernetes repository, for finding test files.") flags.StringVar(&TestContext.RepoRoot, "repo-root", "../../", "Root directory of kubernetes repository, for finding test files.")
flag.StringVar(&TestContext.Provider, "provider", "", "The name of the Kubernetes provider (gce, gke, local, skeleton (the fallback if not set), etc.)") flags.StringVar(&TestContext.Provider, "provider", "", "The name of the Kubernetes provider (gce, gke, local, skeleton (the fallback if not set), etc.)")
flag.StringVar(&TestContext.Tooling, "tooling", "", "The tooling in use (kops, gke, etc.)") flags.StringVar(&TestContext.Tooling, "tooling", "", "The tooling in use (kops, gke, etc.)")
flag.StringVar(&TestContext.KubectlPath, "kubectl-path", "kubectl", "The kubectl binary to use. For development, you might use 'cluster/kubectl.sh' here.") flags.StringVar(&TestContext.KubectlPath, "kubectl-path", "kubectl", "The kubectl binary to use. For development, you might use 'cluster/kubectl.sh' here.")
flag.StringVar(&TestContext.OutputDir, "e2e-output-dir", "/tmp", "Output directory for interesting/useful test data, like performance data, benchmarks, and other metrics.") flags.StringVar(&TestContext.OutputDir, "e2e-output-dir", "/tmp", "Output directory for interesting/useful test data, like performance data, benchmarks, and other metrics.")
flag.StringVar(&TestContext.Prefix, "prefix", "e2e", "A prefix to be added to cloud resources created during testing.") flags.StringVar(&TestContext.Prefix, "prefix", "e2e", "A prefix to be added to cloud resources created during testing.")
flag.StringVar(&TestContext.MasterOSDistro, "master-os-distro", "debian", "The OS distribution of cluster master (debian, ubuntu, gci, coreos, or custom).") flags.StringVar(&TestContext.MasterOSDistro, "master-os-distro", "debian", "The OS distribution of cluster master (debian, ubuntu, gci, coreos, or custom).")
flag.StringVar(&TestContext.NodeOSDistro, "node-os-distro", "debian", "The OS distribution of cluster VM instances (debian, ubuntu, gci, coreos, or custom).") flags.StringVar(&TestContext.NodeOSDistro, "node-os-distro", "debian", "The OS distribution of cluster VM instances (debian, ubuntu, gci, coreos, or custom).")
flag.StringVar(&TestContext.ClusterMonitoringMode, "cluster-monitoring-mode", "standalone", "The monitoring solution that is used in the cluster.") flags.StringVar(&TestContext.ClusterMonitoringMode, "cluster-monitoring-mode", "standalone", "The monitoring solution that is used in the cluster.")
flag.BoolVar(&TestContext.EnablePrometheusMonitoring, "prometheus-monitoring", false, "Separate Prometheus monitoring deployed in cluster.") flags.BoolVar(&TestContext.EnablePrometheusMonitoring, "prometheus-monitoring", false, "Separate Prometheus monitoring deployed in cluster.")
flag.StringVar(&TestContext.ClusterDNSDomain, "dns-domain", "cluster.local", "The DNS Domain of the cluster.") flags.StringVar(&TestContext.ClusterDNSDomain, "dns-domain", "cluster.local", "The DNS Domain of the cluster.")
// TODO: Flags per provider? Rename gce-project/gce-zone? // TODO: Flags per provider? Rename gce-project/gce-zone?
cloudConfig := &TestContext.CloudConfig cloudConfig := &TestContext.CloudConfig
flag.StringVar(&cloudConfig.MasterName, "kube-master", "", "Name of the kubernetes master. Only required if provider is gce or gke") flags.StringVar(&cloudConfig.MasterName, "kube-master", "", "Name of the kubernetes master. Only required if provider is gce or gke")
flag.StringVar(&cloudConfig.APIEndpoint, "gce-api-endpoint", "", "The GCE APIEndpoint being used, if applicable") flags.StringVar(&cloudConfig.APIEndpoint, "gce-api-endpoint", "", "The GCE APIEndpoint being used, if applicable")
flag.StringVar(&cloudConfig.ProjectID, "gce-project", "", "The GCE project being used, if applicable") flags.StringVar(&cloudConfig.ProjectID, "gce-project", "", "The GCE project being used, if applicable")
flag.StringVar(&cloudConfig.Zone, "gce-zone", "", "GCE zone being used, if applicable") flags.StringVar(&cloudConfig.Zone, "gce-zone", "", "GCE zone being used, if applicable")
flag.StringVar(&cloudConfig.Region, "gce-region", "", "GCE region being used, if applicable") flags.StringVar(&cloudConfig.Region, "gce-region", "", "GCE region being used, if applicable")
flag.BoolVar(&cloudConfig.MultiZone, "gce-multizone", false, "If true, start GCE cloud provider with multizone support.") flags.BoolVar(&cloudConfig.MultiZone, "gce-multizone", false, "If true, start GCE cloud provider with multizone support.")
flag.BoolVar(&cloudConfig.MultiMaster, "gce-multimaster", false, "If true, the underlying GCE/GKE cluster is assumed to be multi-master.") flags.BoolVar(&cloudConfig.MultiMaster, "gce-multimaster", false, "If true, the underlying GCE/GKE cluster is assumed to be multi-master.")
flag.StringVar(&cloudConfig.Cluster, "gke-cluster", "", "GKE name of cluster being used, if applicable") flags.StringVar(&cloudConfig.Cluster, "gke-cluster", "", "GKE name of cluster being used, if applicable")
flag.StringVar(&cloudConfig.NodeInstanceGroup, "node-instance-group", "", "Name of the managed instance group for nodes. Valid only for gce, gke or aws. If there is more than one group: comma separated list of groups.") flags.StringVar(&cloudConfig.NodeInstanceGroup, "node-instance-group", "", "Name of the managed instance group for nodes. Valid only for gce, gke or aws. If there is more than one group: comma separated list of groups.")
flag.StringVar(&cloudConfig.Network, "network", "e2e", "The cloud provider network for this e2e cluster.") flags.StringVar(&cloudConfig.Network, "network", "e2e", "The cloud provider network for this e2e cluster.")
flag.IntVar(&cloudConfig.NumNodes, "num-nodes", DefaultNumNodes, fmt.Sprintf("Number of nodes in the cluster. If the default value of '%q' is used the number of schedulable nodes is auto-detected.", DefaultNumNodes)) flags.IntVar(&cloudConfig.NumNodes, "num-nodes", DefaultNumNodes, fmt.Sprintf("Number of nodes in the cluster. If the default value of '%q' is used the number of schedulable nodes is auto-detected.", DefaultNumNodes))
flag.StringVar(&cloudConfig.ClusterIPRange, "cluster-ip-range", "10.64.0.0/14", "A CIDR notation IP range from which to assign IPs in the cluster.") flags.StringVar(&cloudConfig.ClusterIPRange, "cluster-ip-range", "10.64.0.0/14", "A CIDR notation IP range from which to assign IPs in the cluster.")
flag.StringVar(&cloudConfig.NodeTag, "node-tag", "", "Network tags used on node instances. Valid only for gce, gke") flags.StringVar(&cloudConfig.NodeTag, "node-tag", "", "Network tags used on node instances. Valid only for gce, gke")
flag.StringVar(&cloudConfig.MasterTag, "master-tag", "", "Network tags used on master instances. Valid only for gce, gke") flags.StringVar(&cloudConfig.MasterTag, "master-tag", "", "Network tags used on master instances. Valid only for gce, gke")
flag.StringVar(&cloudConfig.ClusterTag, "cluster-tag", "", "Tag used to identify resources. Only required if provider is aws.") flags.StringVar(&cloudConfig.ClusterTag, "cluster-tag", "", "Tag used to identify resources. Only required if provider is aws.")
flag.StringVar(&cloudConfig.ConfigFile, "cloud-config-file", "", "Cloud config file. Only required if provider is azure.") flags.StringVar(&cloudConfig.ConfigFile, "cloud-config-file", "", "Cloud config file. Only required if provider is azure.")
flag.IntVar(&TestContext.MinStartupPods, "minStartupPods", 0, "The number of pods which we need to see in 'Running' state with a 'Ready' condition of true, before we try running tests. This is useful in any cluster which needs some base pod-based services running before it can be used.") flags.IntVar(&TestContext.MinStartupPods, "minStartupPods", 0, "The number of pods which we need to see in 'Running' state with a 'Ready' condition of true, before we try running tests. This is useful in any cluster which needs some base pod-based services running before it can be used.")
flag.DurationVar(&TestContext.SystemPodsStartupTimeout, "system-pods-startup-timeout", 10*time.Minute, "Timeout for waiting for all system pods to be running before starting tests.") flags.DurationVar(&TestContext.SystemPodsStartupTimeout, "system-pods-startup-timeout", 10*time.Minute, "Timeout for waiting for all system pods to be running before starting tests.")
flag.DurationVar(&TestContext.NodeSchedulableTimeout, "node-schedulable-timeout", 30*time.Minute, "Timeout for waiting for all nodes to be schedulable.") flags.DurationVar(&TestContext.NodeSchedulableTimeout, "node-schedulable-timeout", 30*time.Minute, "Timeout for waiting for all nodes to be schedulable.")
flag.DurationVar(&TestContext.SystemDaemonsetStartupTimeout, "system-daemonsets-startup-timeout", 5*time.Minute, "Timeout for waiting for all system daemonsets to be ready.") flags.DurationVar(&TestContext.SystemDaemonsetStartupTimeout, "system-daemonsets-startup-timeout", 5*time.Minute, "Timeout for waiting for all system daemonsets to be ready.")
flag.StringVar(&TestContext.EtcdUpgradeStorage, "etcd-upgrade-storage", "", "The storage version to upgrade to (either 'etcdv2' or 'etcdv3') if doing an etcd upgrade test.") flags.StringVar(&TestContext.EtcdUpgradeStorage, "etcd-upgrade-storage", "", "The storage version to upgrade to (either 'etcdv2' or 'etcdv3') if doing an etcd upgrade test.")
flag.StringVar(&TestContext.EtcdUpgradeVersion, "etcd-upgrade-version", "", "The etcd binary version to upgrade to (e.g., '3.0.14', '2.3.7') if doing an etcd upgrade test.") flags.StringVar(&TestContext.EtcdUpgradeVersion, "etcd-upgrade-version", "", "The etcd binary version to upgrade to (e.g., '3.0.14', '2.3.7') if doing an etcd upgrade test.")
flag.StringVar(&TestContext.GCEUpgradeScript, "gce-upgrade-script", "", "Script to use to upgrade a GCE cluster.") flags.StringVar(&TestContext.GCEUpgradeScript, "gce-upgrade-script", "", "Script to use to upgrade a GCE cluster.")
flag.BoolVar(&TestContext.CleanStart, "clean-start", false, "If true, purge all namespaces except default and system before running tests. This serves to Cleanup test namespaces from failed/interrupted e2e runs in a long-lived cluster.") flags.BoolVar(&TestContext.CleanStart, "clean-start", false, "If true, purge all namespaces except default and system before running tests. This serves to Cleanup test namespaces from failed/interrupted e2e runs in a long-lived cluster.")
nodeKiller := &TestContext.NodeKiller nodeKiller := &TestContext.NodeKiller
flag.BoolVar(&nodeKiller.Enabled, "node-killer", false, "Whether NodeKiller should kill any nodes.") flags.BoolVar(&nodeKiller.Enabled, "node-killer", false, "Whether NodeKiller should kill any nodes.")
flag.Float64Var(&nodeKiller.FailureRatio, "node-killer-failure-ratio", 0.01, "Percentage of nodes to be killed") flags.Float64Var(&nodeKiller.FailureRatio, "node-killer-failure-ratio", 0.01, "Percentage of nodes to be killed")
flag.DurationVar(&nodeKiller.Interval, "node-killer-interval", 1*time.Minute, "Time between node failures.") flags.DurationVar(&nodeKiller.Interval, "node-killer-interval", 1*time.Minute, "Time between node failures.")
flag.Float64Var(&nodeKiller.JitterFactor, "node-killer-jitter-factor", 60, "Factor used to jitter node failures.") flags.Float64Var(&nodeKiller.JitterFactor, "node-killer-jitter-factor", 60, "Factor used to jitter node failures.")
flag.DurationVar(&nodeKiller.SimulatedDowntime, "node-killer-simulated-downtime", 10*time.Minute, "A delay between node death and recreation") flags.DurationVar(&nodeKiller.SimulatedDowntime, "node-killer-simulated-downtime", 10*time.Minute, "A delay between node death and recreation")
} }
// RegisterNodeFlags registers flags specific to the node e2e test suite. // RegisterNodeFlags registers flags specific to the node e2e test suite.
func RegisterNodeFlags() { func RegisterNodeFlags(flags *flag.FlagSet) {
// Mark the test as node e2e when node flags are api.Registry. // Mark the test as node e2e when node flags are api.Registry.
TestContext.NodeE2E = true TestContext.NodeE2E = true
flag.StringVar(&TestContext.NodeName, "node-name", "", "Name of the node to run tests on.") flags.StringVar(&TestContext.NodeName, "node-name", "", "Name of the node to run tests on.")
// TODO(random-liu): Move kubelet start logic out of the test. // TODO(random-liu): Move kubelet start logic out of the test.
// TODO(random-liu): Move log fetch logic out of the test. // TODO(random-liu): Move log fetch logic out of the test.
// There are different ways to start kubelet (systemd, initd, docker, manually started etc.) // There are different ways to start kubelet (systemd, initd, docker, manually started etc.)
// and manage logs (journald, upstart etc.). // and manage logs (journald, upstart etc.).
// For different situation we need to mount different things into the container, run different commands. // For different situation we need to mount different things into the container, run different commands.
// It is hard and unnecessary to deal with the complexity inside the test suite. // It is hard and unnecessary to deal with the complexity inside the test suite.
flag.BoolVar(&TestContext.NodeConformance, "conformance", false, "If true, the test suite will not start kubelet, and fetch system log (kernel, docker, kubelet log etc.) to the report directory.") flags.BoolVar(&TestContext.NodeConformance, "conformance", false, "If true, the test suite will not start kubelet, and fetch system log (kernel, docker, kubelet log etc.) to the report directory.")
flag.BoolVar(&TestContext.PrepullImages, "prepull-images", true, "If true, prepull images so image pull failures do not cause test failures.") flags.BoolVar(&TestContext.PrepullImages, "prepull-images", true, "If true, prepull images so image pull failures do not cause test failures.")
flag.StringVar(&TestContext.ImageDescription, "image-description", "", "The description of the image which the test will be running on.") flags.StringVar(&TestContext.ImageDescription, "image-description", "", "The description of the image which the test will be running on.")
flag.StringVar(&TestContext.SystemSpecName, "system-spec-name", "", "The name of the system spec (e.g., gke) that's used in the node e2e test. The system specs are in test/e2e_node/system/specs/. This is used by the test framework to determine which tests to run for validating the system requirements.") flags.StringVar(&TestContext.SystemSpecName, "system-spec-name", "", "The name of the system spec (e.g., gke) that's used in the node e2e test. The system specs are in test/e2e_node/system/specs/. This is used by the test framework to determine which tests to run for validating the system requirements.")
flag.Var(cliflag.NewMapStringString(&TestContext.ExtraEnvs), "extra-envs", "The extra environment variables needed for node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2") flags.Var(cliflag.NewMapStringString(&TestContext.ExtraEnvs), "extra-envs", "The extra environment variables needed for node e2e tests. Format: a list of key=value pairs, e.g., env1=val1,env2=val2")
} }
// HandleFlags sets up all flags and parses the command line. // HandleFlags sets up all flags and parses the command line.
func HandleFlags() { func HandleFlags() {
RegisterCommonFlags() e2econfig.CopyFlags(e2econfig.Flags, flag.CommandLine)
RegisterClusterFlags() RegisterCommonFlags(flag.CommandLine)
RegisterClusterFlags(flag.CommandLine)
flag.Parse() flag.Parse()
} }

View File

@ -1,4 +1,4 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library") load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
@ -24,3 +24,13 @@ filegroup(
tags = ["automanaged"], tags = ["automanaged"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )
go_test(
name = "go_default_test",
srcs = ["viperconfig_test.go"],
embed = [":go_default_library"],
deps = [
"//test/e2e/framework/config:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
],
)

View File

@ -25,12 +25,9 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
const ( // ViperizeFlags checks whether a configuration file was specified,
viperFileNotFound = "Unsupported Config Type \"\"" // reads it, and updates the configuration variables in the specified
) // flag set accordingly. Must be called after framework.HandleFlags()
// ViperizeFlags checks whether a configuration file was specified, reads it, and updates
// the configuration variables accordingly. Must be called after framework.HandleFlags()
// and before framework.AfterReadingAllFlags(). // and before framework.AfterReadingAllFlags().
// //
// The logic is so that a required configuration file must be present. If empty, // The logic is so that a required configuration file must be present. If empty,
@ -38,7 +35,7 @@ const (
// //
// Files can be specified with just a base name ("e2e", matches "e2e.json/yaml/..." in // Files can be specified with just a base name ("e2e", matches "e2e.json/yaml/..." in
// the current directory) or with path and suffix. // the current directory) or with path and suffix.
func ViperizeFlags(requiredConfig, optionalConfig string) error { func ViperizeFlags(requiredConfig, optionalConfig string, flags *flag.FlagSet) error {
viperConfig := optionalConfig viperConfig := optionalConfig
required := false required := false
if requiredConfig != "" { if requiredConfig != "" {
@ -68,7 +65,7 @@ func ViperizeFlags(requiredConfig, optionalConfig string) error {
// of file suffices. Therefore try once more without // of file suffices. Therefore try once more without
// suffix. // suffix.
ext := filepath.Ext(viperConfig) ext := filepath.Ext(viperConfig)
if ext != "" && err.Error() == viperFileNotFound { if _, ok := err.(viper.ConfigFileNotFoundError); ok && ext != "" {
viper.SetConfigName(filepath.Base(viperConfig[0 : len(viperConfig)-len(ext)])) viper.SetConfigName(filepath.Base(viperConfig[0 : len(viperConfig)-len(ext)]))
err = viper.ReadInConfig() err = viper.ReadInConfig()
} }
@ -103,24 +100,24 @@ func ViperizeFlags(requiredConfig, optionalConfig string) error {
// something like viper.Unmarshal(&TestContext) because we // something like viper.Unmarshal(&TestContext) because we
// want to support all values, regardless where they are // want to support all values, regardless where they are
// stored. // stored.
return wrapError(viperUnmarshal()) return wrapError(viperUnmarshal(flags))
} }
// viperUnmarshall updates all command line flags with the corresponding values found // viperUnmarshall updates all flags with the corresponding values found
// via Viper, regardless whether the flag value is stored in TestContext, some other // via Viper, regardless whether the flag value is stored in TestContext, some other
// context or a local variable. // context or a local variable.
func viperUnmarshal() error { func viperUnmarshal(flags *flag.FlagSet) error {
var result error var result error
set := make(map[string]bool) set := make(map[string]bool)
// Determine which values were already set explicitly via // Determine which values were already set explicitly via
// flags. Those we don't overwrite because command line // flags. Those we don't overwrite because command line
// flags have a higher priority. // flags have a higher priority.
flag.Visit(func(f *flag.Flag) { flags.Visit(func(f *flag.Flag) {
set[f.Name] = true set[f.Name] = true
}) })
flag.VisitAll(func(f *flag.Flag) { flags.VisitAll(func(f *flag.Flag) {
if result != nil || if result != nil ||
set[f.Name] || set[f.Name] ||
!viper.IsSet(f.Name) { !viper.IsSet(f.Name) {

View File

@ -0,0 +1,72 @@
/*
Copyright 2019 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 viperconfig
import (
"flag"
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"k8s.io/kubernetes/test/e2e/framework/config"
)
func TestViperConfig(t *testing.T) {
flags := flag.NewFlagSet("test", 0)
type Context struct {
Bool bool `default:"true"`
Duration time.Duration `default:"1ms"`
Float64 float64 `default:"1.23456789"`
String string `default:"hello world"`
Int int `default:"-1" usage:"some number"`
Int64 int64 `default:"-1234567890123456789"`
Uint uint `default:"1"`
Uint64 uint64 `default:"1234567890123456789"`
}
var context Context
require.NotPanics(t, func() {
config.AddOptionsToSet(flags, &context, "")
})
config := `
bool: false
duration: 1s
float64: -1.23456789
string: pong
int: -2
int64: -9123456789012345678
uint: 2
uint64: 9123456789012345678
`
tmpfile, err := ioutil.TempFile("", "viperconfig-*.yaml")
require.NoError(t, err, "temp file")
defer os.Remove(tmpfile.Name())
if _, err := tmpfile.Write([]byte(config)); err != nil {
require.NoError(t, err, "write config")
}
require.NoError(t, tmpfile.Close(), "close temp file")
require.NoError(t, ViperizeFlags(tmpfile.Name(), "", flags), "read config file")
require.Equal(t,
Context{false, time.Second, -1.23456789, "pong",
-2, -9123456789012345678, 2, 9123456789012345678,
},
context,
"values from viper must match")
}

View File

@ -33,6 +33,7 @@ go_library(
"//test/e2e/chaosmonkey:go_default_library", "//test/e2e/chaosmonkey:go_default_library",
"//test/e2e/common:go_default_library", "//test/e2e/common:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/e2e/framework/config:go_default_library",
"//test/e2e/framework/ginkgowrapper:go_default_library", "//test/e2e/framework/ginkgowrapper:go_default_library",
"//test/e2e/framework/lifecycle:go_default_library", "//test/e2e/framework/lifecycle:go_default_library",
"//test/e2e/framework/log:go_default_library", "//test/e2e/framework/log:go_default_library",

View File

@ -18,7 +18,6 @@ package lifecycle
import ( import (
"encoding/xml" "encoding/xml"
"flag"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -31,6 +30,7 @@ import (
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/kubernetes/test/e2e/chaosmonkey" "k8s.io/kubernetes/test/e2e/chaosmonkey"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/framework/config"
"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper" "k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
e2elifecycle "k8s.io/kubernetes/test/e2e/framework/lifecycle" e2elifecycle "k8s.io/kubernetes/test/e2e/framework/lifecycle"
"k8s.io/kubernetes/test/e2e/upgrades" "k8s.io/kubernetes/test/e2e/upgrades"
@ -42,8 +42,8 @@ import (
) )
var ( var (
upgradeTarget = flag.String("upgrade-target", "ci/latest", "Version to upgrade to (e.g. 'release/stable', 'release/latest', 'ci/latest', '0.19.1', '0.19.1-669-gabac8c8') if doing an upgrade test.") upgradeTarget = config.Flags.String("upgrade-target", "ci/latest", "Version to upgrade to (e.g. 'release/stable', 'release/latest', 'ci/latest', '0.19.1', '0.19.1-669-gabac8c8') if doing an upgrade test.")
upgradeImage = flag.String("upgrade-image", "", "Image to upgrade to (e.g. 'container_vm' or 'gci') if doing an upgrade test.") upgradeImage = config.Flags.String("upgrade-image", "", "Image to upgrade to (e.g. 'container_vm' or 'gci') if doing an upgrade test.")
) )
var upgradeTests = []upgrades.Test{ var upgradeTests = []upgrades.Test{

View File

@ -144,8 +144,6 @@ func (h *hostpathCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.Per
ClientNodeName: nodeName, ClientNodeName: nodeName,
} }
// TODO (?): the storage.csi.image.version and storage.csi.image.registry
// settings are ignored for this test. We could patch the image definitions.
o := utils.PatchCSIOptions{ o := utils.PatchCSIOptions{
OldDriverName: h.driverInfo.Name, OldDriverName: h.driverInfo.Name,
NewDriverName: config.GetUniqueDriverName(), NewDriverName: config.GetUniqueDriverName(),
@ -288,8 +286,6 @@ func (m *mockCSIDriver) PrepareTest(f *framework.Framework) (*testsuites.PerTest
containerArgs = append(containerArgs, "--node-expand-required=true") containerArgs = append(containerArgs, "--node-expand-required=true")
} }
// TODO (?): the storage.csi.image.version and storage.csi.image.registry
// settings are ignored for this test. We could patch the image definitions.
o := utils.PatchCSIOptions{ o := utils.PatchCSIOptions{
OldDriverName: "csi-mock", OldDriverName: "csi-mock",
NewDriverName: "csi-mock-" + f.UniqueName, NewDriverName: "csi-mock-" + f.UniqueName,

View File

@ -14,6 +14,7 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library", "//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/e2e/framework/config:go_default_library",
"//test/e2e/storage/testpatterns:go_default_library", "//test/e2e/storage/testpatterns:go_default_library",
"//test/e2e/storage/testsuites:go_default_library", "//test/e2e/storage/testsuites:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library",

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/framework/config"
"k8s.io/kubernetes/test/e2e/storage/testpatterns" "k8s.io/kubernetes/test/e2e/storage/testpatterns"
"k8s.io/kubernetes/test/e2e/storage/testsuites" "k8s.io/kubernetes/test/e2e/storage/testsuites"
@ -49,7 +50,7 @@ var csiTestSuites = []func() testsuites.TestSuite{
} }
func init() { func init() {
flag.Var(testDriverParameter{}, "storage.testdriver", "name of a .yaml or .json file that defines a driver for storage testing, can be used more than once") config.Flags.Var(testDriverParameter{}, "storage.testdriver", "name of a .yaml or .json file that defines a driver for storage testing, can be used more than once")
} }
// testDriverParameter is used to hook loading of the driver // testDriverParameter is used to hook loading of the driver

View File

@ -49,10 +49,6 @@ import (
// //
// Driver deployments that are different will have to do the patching // Driver deployments that are different will have to do the patching
// without this function, or skip patching entirely. // without this function, or skip patching entirely.
//
// TODO (?): the storage.csi.image.version and storage.csi.image.registry
// settings are ignored. We could patch the image definitions or deprecate
// those options.
func PatchCSIDeployment(f *framework.Framework, o PatchCSIOptions, object interface{}) error { func PatchCSIDeployment(f *framework.Framework, o PatchCSIOptions, object interface{}) error {
rename := o.OldDriverName != "" && o.NewDriverName != "" && rename := o.OldDriverName != "" && o.NewDriverName != "" &&
o.OldDriverName != o.NewDriverName o.OldDriverName != o.NewDriverName

View File

@ -33,6 +33,7 @@ go_test(
"//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library", "//staging/src/k8s.io/cluster-bootstrap/token/api:go_default_library",
"//test/e2e/framework:go_default_library", "//test/e2e/framework:go_default_library",
"//test/e2e/framework/config:go_default_library",
"//test/e2e/framework/log:go_default_library", "//test/e2e/framework/log:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/ginkgo/config:go_default_library", "//vendor/github.com/onsi/ginkgo/config:go_default_library",

View File

@ -30,11 +30,13 @@ import (
morereporters "github.com/onsi/ginkgo/reporters" morereporters "github.com/onsi/ginkgo/reporters"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
e2econfig "k8s.io/kubernetes/test/e2e/framework/config"
) )
func init() { func init() {
framework.RegisterCommonFlags() e2econfig.CopyFlags(e2econfig.Flags, flag.CommandLine)
framework.RegisterClusterFlags() framework.RegisterCommonFlags(flag.CommandLine)
framework.RegisterClusterFlags(flag.CommandLine)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
} }

View File

@ -192,6 +192,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
"//test/e2e/framework/config:go_default_library",
"//test/e2e/framework/testfiles:go_default_library", "//test/e2e/framework/testfiles:go_default_library",
"//test/e2e/generated:go_default_library", "//test/e2e/generated:go_default_library",
"//vendor/github.com/kardianos/osext:go_default_library", "//vendor/github.com/kardianos/osext:go_default_library",

View File

@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/system" "k8s.io/kubernetes/cmd/kubeadm/app/util/system"
commontest "k8s.io/kubernetes/test/e2e/common" commontest "k8s.io/kubernetes/test/e2e/common"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
e2econfig "k8s.io/kubernetes/test/e2e/framework/config"
"k8s.io/kubernetes/test/e2e/framework/testfiles" "k8s.io/kubernetes/test/e2e/framework/testfiles"
"k8s.io/kubernetes/test/e2e/generated" "k8s.io/kubernetes/test/e2e/generated"
"k8s.io/kubernetes/test/e2e_node/services" "k8s.io/kubernetes/test/e2e_node/services"
@ -63,8 +64,9 @@ var systemValidateMode = flag.Bool("system-validate-mode", false, "If true, only
var systemSpecFile = flag.String("system-spec-file", "", "The name of the system spec file that will be used for node conformance test. If it's unspecified or empty, the default system spec (system.DefaultSysSpec) will be used.") var systemSpecFile = flag.String("system-spec-file", "", "The name of the system spec file that will be used for node conformance test. If it's unspecified or empty, the default system spec (system.DefaultSysSpec) will be used.")
func init() { func init() {
framework.RegisterCommonFlags() e2econfig.CopyFlags(e2econfig.Flags, flag.CommandLine)
framework.RegisterNodeFlags() framework.RegisterCommonFlags(flag.CommandLine)
framework.RegisterNodeFlags(flag.CommandLine)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
// Mark the run-services-mode flag as hidden to prevent user from using it. // Mark the run-services-mode flag as hidden to prevent user from using it.
pflag.CommandLine.MarkHidden("run-services-mode") pflag.CommandLine.MarkHidden("run-services-mode")