mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Add composition flags for emulation version and feature gate.
Signed-off-by: Siyuan Zhang <sizhang@google.com>
This commit is contained in:
parent
403301bfdf
commit
701e5fc374
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
|
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
|
||||||
|
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
@ -50,13 +51,14 @@ func TestAddFlags(t *testing.T) {
|
|||||||
fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError)
|
fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError)
|
||||||
|
|
||||||
featureGate := featuregate.NewFeatureGate()
|
featureGate := featuregate.NewFeatureGate()
|
||||||
|
componentRegistry := utilversion.NewComponentGlobalsRegistry()
|
||||||
effectiveVersion := utilversion.NewEffectiveVersion("1.32")
|
effectiveVersion := utilversion.NewEffectiveVersion("1.32")
|
||||||
|
_ = componentRegistry.Register("test", effectiveVersion, featureGate, true)
|
||||||
s := NewServerRunOptions(featureGate, effectiveVersion)
|
s := NewServerRunOptions(featureGate, effectiveVersion)
|
||||||
for _, f := range s.Flags().FlagSets {
|
for _, f := range s.Flags().FlagSets {
|
||||||
fs.AddFlagSet(f)
|
fs.AddFlagSet(f)
|
||||||
}
|
}
|
||||||
featureGate.AddFlag(fs, "")
|
componentRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--enable-admission-plugins=AlwaysDeny",
|
"--enable-admission-plugins=AlwaysDeny",
|
||||||
@ -128,9 +130,10 @@ func TestAddFlags(t *testing.T) {
|
|||||||
"--storage-backend=etcd3",
|
"--storage-backend=etcd3",
|
||||||
"--service-cluster-ip-range=192.168.128.0/17",
|
"--service-cluster-ip-range=192.168.128.0/17",
|
||||||
"--lease-reuse-duration-seconds=100",
|
"--lease-reuse-duration-seconds=100",
|
||||||
"--emulated-version=1.31",
|
"--emulated-version=test=1.31",
|
||||||
}
|
}
|
||||||
fs.Parse(args)
|
fs.Parse(args)
|
||||||
|
utilruntime.Must(componentRegistry.Set())
|
||||||
|
|
||||||
// This is a snapshot of expected options parsed by args.
|
// This is a snapshot of expected options parsed by args.
|
||||||
expected := &ServerRunOptions{
|
expected := &ServerRunOptions{
|
||||||
|
@ -65,7 +65,7 @@ func init() {
|
|||||||
// NewAPIServerCommand creates a *cobra.Command object with default parameters
|
// NewAPIServerCommand creates a *cobra.Command object with default parameters
|
||||||
func NewAPIServerCommand() *cobra.Command {
|
func NewAPIServerCommand() *cobra.Command {
|
||||||
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
||||||
utilversion.ComponentGenericAPIServer, utilversion.DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
utilversion.DefaultKubeComponent, utilversion.DefaultBuildEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
||||||
s := options.NewServerRunOptions(featureGate, effectiveVersion)
|
s := options.NewServerRunOptions(featureGate, effectiveVersion)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@ -78,6 +78,9 @@ cluster's shared state through which all other components interact.`,
|
|||||||
// stop printing usage when the command errors
|
// stop printing usage when the command errors
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
PersistentPreRunE: func(*cobra.Command, []string) error {
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// silence client-go warnings.
|
// silence client-go warnings.
|
||||||
// kube-apiserver loopback clients should not log self-issued warnings.
|
// kube-apiserver loopback clients should not log self-issued warnings.
|
||||||
rest.SetDefaultWarningHandler(rest.NoWarnings{})
|
rest.SetDefaultWarningHandler(rest.NoWarnings{})
|
||||||
@ -86,11 +89,6 @@ cluster's shared state through which all other components interact.`,
|
|||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
verflag.PrintAndExitIfRequested()
|
verflag.PrintAndExitIfRequested()
|
||||||
fs := cmd.Flags()
|
fs := cmd.Flags()
|
||||||
|
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Activate logging as soon as possible, after that
|
// Activate logging as soon as possible, after that
|
||||||
// show flags with the final logging configuration.
|
// show flags with the final logging configuration.
|
||||||
if err := logsapi.ValidateAndApply(s.Logs, featureGate); err != nil {
|
if err := logsapi.ValidateAndApply(s.Logs, featureGate); err != nil {
|
||||||
@ -126,8 +124,7 @@ cluster's shared state through which all other components interact.`,
|
|||||||
fs := cmd.Flags()
|
fs := cmd.Flags()
|
||||||
namedFlagSets := s.Flags()
|
namedFlagSets := s.Flags()
|
||||||
verflag.AddFlags(namedFlagSets.FlagSet("global"))
|
verflag.AddFlags(namedFlagSets.FlagSet("global"))
|
||||||
featureGate.AddFlag(namedFlagSets.FlagSet("global"), "")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(namedFlagSets.FlagSet("global"))
|
||||||
effectiveVersion.AddFlags(namedFlagSets.FlagSet("global"), "")
|
|
||||||
|
|
||||||
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name(), logs.SkipLoggingConfigurationFlags())
|
globalflag.AddGlobalFlags(namedFlagSets.FlagSet("global"), cmd.Name(), logs.SkipLoggingConfigurationFlags())
|
||||||
options.AddCustomGlobalFlags(namedFlagSets.FlagSet("generic"))
|
options.AddCustomGlobalFlags(namedFlagSets.FlagSet("generic"))
|
||||||
|
@ -187,15 +187,14 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions,
|
|||||||
binaryVersion = instanceOptions.BinaryVersion
|
binaryVersion = instanceOptions.BinaryVersion
|
||||||
}
|
}
|
||||||
effectiveVersion := utilversion.NewEffectiveVersion(binaryVersion)
|
effectiveVersion := utilversion.NewEffectiveVersion(binaryVersion)
|
||||||
_ = utilversion.DefaultComponentGlobalsRegistry.Register(utilversion.ComponentGenericAPIServer, effectiveVersion, featureGate, true)
|
_ = utilversion.DefaultComponentGlobalsRegistry.Register(utilversion.DefaultKubeComponent, effectiveVersion, featureGate, true)
|
||||||
|
|
||||||
s := options.NewServerRunOptions(featureGate, effectiveVersion)
|
s := options.NewServerRunOptions(featureGate, effectiveVersion)
|
||||||
|
|
||||||
for _, f := range s.Flags().FlagSets {
|
for _, f := range s.Flags().FlagSets {
|
||||||
fs.AddFlagSet(f)
|
fs.AddFlagSet(f)
|
||||||
}
|
}
|
||||||
featureGate.AddFlag(fs, "")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
|
|
||||||
s.SecureServing.Listener, s.SecureServing.BindPort, err = createLocalhostListenerOnFreePort()
|
s.SecureServing.Listener, s.SecureServing.BindPort, err = createLocalhostListenerOnFreePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -336,7 +335,7 @@ func StartTestServer(t ktesting.TB, instanceOptions *TestServerInstanceOptions,
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy
|
|||||||
fs := fss.FlagSet("misc")
|
fs := fss.FlagSet("misc")
|
||||||
fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
||||||
fs.StringVar(&s.Generic.ClientConnection.Kubeconfig, "kubeconfig", s.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).")
|
fs.StringVar(&s.Generic.ClientConnection.Kubeconfig, "kubeconfig", s.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).")
|
||||||
utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("generic"), "")
|
utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("generic"))
|
||||||
|
|
||||||
return fss
|
return fss
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ func (o *Options) initFlags() {
|
|||||||
o.Authorization.AddFlags(nfs.FlagSet("authorization"))
|
o.Authorization.AddFlags(nfs.FlagSet("authorization"))
|
||||||
o.Deprecated.AddFlags(nfs.FlagSet("deprecated"))
|
o.Deprecated.AddFlags(nfs.FlagSet("deprecated"))
|
||||||
options.BindLeaderElectionFlags(o.LeaderElection, nfs.FlagSet("leader election"))
|
options.BindLeaderElectionFlags(o.LeaderElection, nfs.FlagSet("leader election"))
|
||||||
utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"), "")
|
utilfeature.DefaultMutableFeatureGate.AddFlag(nfs.FlagSet("feature gate"))
|
||||||
o.Metrics.AddFlags(nfs.FlagSet("metrics"))
|
o.Metrics.AddFlags(nfs.FlagSet("metrics"))
|
||||||
logsapi.AddFlags(o.Logs, nfs.FlagSet("logs"))
|
logsapi.AddFlags(o.Logs, nfs.FlagSet("logs"))
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
|
noopoteltrace "go.opentelemetry.io/otel/trace/noop"
|
||||||
|
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
@ -46,14 +47,15 @@ func TestAddFlags(t *testing.T) {
|
|||||||
fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError)
|
fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError)
|
||||||
featureGate := featuregate.NewFeatureGate()
|
featureGate := featuregate.NewFeatureGate()
|
||||||
effectiveVersion := utilversion.NewEffectiveVersion("1.32")
|
effectiveVersion := utilversion.NewEffectiveVersion("1.32")
|
||||||
|
componentRegistry := utilversion.NewComponentGlobalsRegistry()
|
||||||
|
_ = componentRegistry.Register("test", effectiveVersion, featureGate, true)
|
||||||
s := NewOptions(featureGate, effectiveVersion)
|
s := NewOptions(featureGate, effectiveVersion)
|
||||||
var fss cliflag.NamedFlagSets
|
var fss cliflag.NamedFlagSets
|
||||||
s.AddFlags(&fss)
|
s.AddFlags(&fss)
|
||||||
for _, f := range fss.FlagSets {
|
for _, f := range fss.FlagSets {
|
||||||
fs.AddFlagSet(f)
|
fs.AddFlagSet(f)
|
||||||
}
|
}
|
||||||
featureGate.AddFlag(fs, "")
|
componentRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"--enable-admission-plugins=AlwaysDeny",
|
"--enable-admission-plugins=AlwaysDeny",
|
||||||
@ -114,9 +116,10 @@ func TestAddFlags(t *testing.T) {
|
|||||||
"--request-timeout=2m",
|
"--request-timeout=2m",
|
||||||
"--storage-backend=etcd3",
|
"--storage-backend=etcd3",
|
||||||
"--lease-reuse-duration-seconds=100",
|
"--lease-reuse-duration-seconds=100",
|
||||||
"--emulated-version=1.31",
|
"--emulated-version=test=1.31",
|
||||||
}
|
}
|
||||||
fs.Parse(args)
|
fs.Parse(args)
|
||||||
|
utilruntime.Must(componentRegistry.Set())
|
||||||
|
|
||||||
// This is a snapshot of expected options parsed by args.
|
// This is a snapshot of expected options parsed by args.
|
||||||
expected := &Options{
|
expected := &Options{
|
||||||
|
@ -33,17 +33,16 @@ func NewServerCommand(ctx context.Context, out, errOut io.Writer) *cobra.Command
|
|||||||
// You can also have the flag setting the effectiveVersion of the apiextensions apiserver, and
|
// You can also have the flag setting the effectiveVersion of the apiextensions apiserver, and
|
||||||
// having a mapping from the apiextensions apiserver version to generic apiserver version.
|
// having a mapping from the apiextensions apiserver version to generic apiserver version.
|
||||||
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
||||||
utilversion.ComponentGenericAPIServer, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
utilversion.DefaultKubeComponent, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
||||||
o := options.NewCustomResourceDefinitionsServerOptions(out, errOut, featureGate, effectiveVersion)
|
o := options.NewCustomResourceDefinitionsServerOptions(out, errOut, featureGate, effectiveVersion)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Short: "Launch an API extensions API server",
|
Short: "Launch an API extensions API server",
|
||||||
Long: "Launch an API extensions API server",
|
Long: "Launch an API extensions API server",
|
||||||
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
return utilversion.DefaultComponentGlobalsRegistry.Set()
|
||||||
|
},
|
||||||
RunE: func(c *cobra.Command, args []string) error {
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := o.Complete(); err != nil {
|
if err := o.Complete(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -59,8 +58,7 @@ func NewServerCommand(ctx context.Context, out, errOut io.Writer) *cobra.Command
|
|||||||
cmd.SetContext(ctx)
|
cmd.SetContext(ctx)
|
||||||
|
|
||||||
fs := cmd.Flags()
|
fs := cmd.Flags()
|
||||||
featureGate.AddFlag(fs, "")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
o.AddFlags(fs)
|
o.AddFlags(fs)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -124,11 +124,10 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin
|
|||||||
|
|
||||||
featureGate := utilfeature.DefaultMutableFeatureGate
|
featureGate := utilfeature.DefaultMutableFeatureGate
|
||||||
effectiveVersion := utilversion.DefaultKubeEffectiveVersion()
|
effectiveVersion := utilversion.DefaultKubeEffectiveVersion()
|
||||||
_ = utilversion.DefaultComponentGlobalsRegistry.Register(utilversion.ComponentGenericAPIServer, effectiveVersion, featureGate, true)
|
_ = utilversion.DefaultComponentGlobalsRegistry.Register(utilversion.DefaultKubeComponent, effectiveVersion, featureGate, true)
|
||||||
s := options.NewCustomResourceDefinitionsServerOptions(os.Stdout, os.Stderr, featureGate, effectiveVersion)
|
s := options.NewCustomResourceDefinitionsServerOptions(os.Stdout, os.Stderr, featureGate, effectiveVersion)
|
||||||
|
|
||||||
featureGate.AddFlag(fs, "")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
s.AddFlags(fs)
|
s.AddFlags(fs)
|
||||||
|
|
||||||
s.RecommendedOptions.SecureServing.Listener, s.RecommendedOptions.SecureServing.BindPort, err = createLocalhostListenerOnFreePort()
|
s.RecommendedOptions.SecureServing.Listener, s.RecommendedOptions.SecureServing.BindPort, err = createLocalhostListenerOnFreePort()
|
||||||
@ -151,7 +150,7 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin
|
|||||||
|
|
||||||
fs.Parse(customFlags)
|
fs.Parse(customFlags)
|
||||||
|
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ import (
|
|||||||
// A default version number equal to the current Kubernetes major.minor version
|
// A default version number equal to the current Kubernetes major.minor version
|
||||||
// indicates fast forward CEL features that can be used when rollback is no longer needed.
|
// indicates fast forward CEL features that can be used when rollback is no longer needed.
|
||||||
func DefaultCompatibilityVersion() *version.Version {
|
func DefaultCompatibilityVersion() *version.Version {
|
||||||
effectiveVer := utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.ComponentGenericAPIServer)
|
effectiveVer := utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent)
|
||||||
if effectiveVer == nil {
|
if effectiveVer == nil {
|
||||||
effectiveVer = utilversion.DefaultKubeEffectiveVersion()
|
effectiveVer = utilversion.DefaultKubeEffectiveVersion()
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,47 @@ package version
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
"k8s.io/component-base/featuregate"
|
"k8s.io/component-base/featuregate"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultComponentGlobalsRegistry is the global var to store the effective versions and feature gates for all components for easy access.
|
||||||
|
// Example usage:
|
||||||
|
// // register the component effective version and feature gate first
|
||||||
|
// _, _ = utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(utilversion.DefaultKubeComponent, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
||||||
|
// wardleEffectiveVersion := utilversion.NewEffectiveVersion("1.2")
|
||||||
|
// wardleFeatureGate := featuregate.NewFeatureGate()
|
||||||
|
// utilruntime.Must(utilversion.DefaultComponentGlobalsRegistry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, wardleFeatureGate, false))
|
||||||
|
//
|
||||||
|
// cmd := &cobra.Command{
|
||||||
|
// ...
|
||||||
|
// // call DefaultComponentGlobalsRegistry.Set() in PersistentPreRunE
|
||||||
|
// PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
// if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// ...
|
||||||
|
// },
|
||||||
|
// RunE: func(c *cobra.Command, args []string) error {
|
||||||
|
// // call utilversion.DefaultComponentGlobalsRegistry.Validate() somewhere
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// flags := cmd.Flags()
|
||||||
|
// // add flags
|
||||||
|
// utilversion.DefaultComponentGlobalsRegistry.AddFlags(flags)
|
||||||
var DefaultComponentGlobalsRegistry ComponentGlobalsRegistry = NewComponentGlobalsRegistry()
|
var DefaultComponentGlobalsRegistry ComponentGlobalsRegistry = NewComponentGlobalsRegistry()
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ComponentGenericAPIServer = "k8s.io/apiserver"
|
DefaultKubeComponent = "kube"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ComponentGlobals stores the global variables for a component for easy access.
|
// ComponentGlobals stores the global variables for a component for easy access.
|
||||||
@ -50,19 +81,27 @@ type ComponentGlobalsRegistry interface {
|
|||||||
// ComponentGlobalsOrRegister would return the registered global variables for the component if it already exists in the registry.
|
// ComponentGlobalsOrRegister would return the registered global variables for the component if it already exists in the registry.
|
||||||
// Otherwise, the provided variables would be registered under the component, and the same variables would be returned.
|
// Otherwise, the provided variables would be registered under the component, and the same variables would be returned.
|
||||||
ComponentGlobalsOrRegister(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) (MutableEffectiveVersion, featuregate.MutableVersionedFeatureGate)
|
ComponentGlobalsOrRegister(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate) (MutableEffectiveVersion, featuregate.MutableVersionedFeatureGate)
|
||||||
// SetAllComponents sets the emulation version for other global variables for all components registered.
|
// AddFlags adds flags of "--emulated-version" and "--feature-gates"
|
||||||
SetAllComponents() error
|
AddFlags(fs *pflag.FlagSet)
|
||||||
|
// Set sets the flags for all global variables for all components registered.
|
||||||
|
Set() error
|
||||||
// SetAllComponents calls the Validate() function for all the global variables for all components registered.
|
// SetAllComponents calls the Validate() function for all the global variables for all components registered.
|
||||||
ValidateAllComponents() []error
|
Validate() []error
|
||||||
}
|
}
|
||||||
|
|
||||||
type componentGlobalsRegistry struct {
|
type componentGlobalsRegistry struct {
|
||||||
componentGlobals map[string]ComponentGlobals
|
componentGlobals map[string]ComponentGlobals
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
// map of component name to emulation version set from the flag.
|
||||||
|
emulationVersionConfig cliflag.ConfigurationMap
|
||||||
|
// map of component name to the list of feature gates set from the flag.
|
||||||
|
featureGatesConfig map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewComponentGlobalsRegistry() ComponentGlobalsRegistry {
|
func NewComponentGlobalsRegistry() ComponentGlobalsRegistry {
|
||||||
return &componentGlobalsRegistry{componentGlobals: map[string]ComponentGlobals{}}
|
return &componentGlobalsRegistry{
|
||||||
|
componentGlobals: make(map[string]ComponentGlobals),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *componentGlobalsRegistry) EffectiveVersionFor(component string) EffectiveVersion {
|
func (r *componentGlobalsRegistry) EffectiveVersionFor(component string) EffectiveVersion {
|
||||||
@ -90,7 +129,9 @@ func (r *componentGlobalsRegistry) unsafeRegister(component string, effectiveVer
|
|||||||
return fmt.Errorf("component globals of %s already registered", component)
|
return fmt.Errorf("component globals of %s already registered", component)
|
||||||
}
|
}
|
||||||
if featureGate != nil {
|
if featureGate != nil {
|
||||||
featureGate.DeferErrorsToValidation(true)
|
if err := featureGate.SetEmulationVersion(effectiveVersion.EmulationVersion()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c := ComponentGlobals{effectiveVersion: effectiveVersion, featureGate: featureGate}
|
c := ComponentGlobals{effectiveVersion: effectiveVersion, featureGate: featureGate}
|
||||||
r.componentGlobals[component] = c
|
r.componentGlobals[component] = c
|
||||||
@ -98,6 +139,9 @@ func (r *componentGlobalsRegistry) unsafeRegister(component string, effectiveVer
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *componentGlobalsRegistry) Register(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate, override bool) error {
|
func (r *componentGlobalsRegistry) Register(component string, effectiveVersion MutableEffectiveVersion, featureGate featuregate.MutableVersionedFeatureGate, override bool) error {
|
||||||
|
if effectiveVersion == nil {
|
||||||
|
return fmt.Errorf("cannot register nil effectiveVersion")
|
||||||
|
}
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
return r.unsafeRegister(component, effectiveVersion, featureGate, override)
|
return r.unsafeRegister(component, effectiveVersion, featureGate, override)
|
||||||
@ -114,21 +158,112 @@ func (r *componentGlobalsRegistry) ComponentGlobalsOrRegister(component string,
|
|||||||
return effectiveVersion, featureGate
|
return effectiveVersion, featureGate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *componentGlobalsRegistry) SetAllComponents() error {
|
func (r *componentGlobalsRegistry) knownFeatures() []string {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
for _, globals := range r.componentGlobals {
|
var known []string
|
||||||
|
for component, globals := range r.componentGlobals {
|
||||||
if globals.featureGate == nil {
|
if globals.featureGate == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
for _, f := range globals.featureGate.KnownFeatures() {
|
||||||
|
known = append(known, component+":"+f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(known)
|
||||||
|
return known
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *componentGlobalsRegistry) versionFlagOptions(isEmulation bool) []string {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
var vs []string
|
||||||
|
for component, globals := range r.componentGlobals {
|
||||||
|
binaryVer := globals.effectiveVersion.BinaryVersion()
|
||||||
|
if isEmulation {
|
||||||
|
// emulated version could be between binaryMajor.{binaryMinor} and binaryMajor.{binaryMinor}
|
||||||
|
// TODO: change to binaryMajor.{binaryMinor-1} and binaryMajor.{binaryMinor} in 1.32
|
||||||
|
vs = append(vs, fmt.Sprintf("%s=%s..%s (default=%s)", component,
|
||||||
|
binaryVer.SubtractMinor(0).String(), binaryVer.String(), globals.effectiveVersion.EmulationVersion().String()))
|
||||||
|
} else {
|
||||||
|
// min compatibility version could be between binaryMajor.{binaryMinor-1} and binaryMajor.{binaryMinor}
|
||||||
|
vs = append(vs, fmt.Sprintf("%s=%s..%s (default=%s)", component,
|
||||||
|
binaryVer.SubtractMinor(1).String(), binaryVer.String(), globals.effectiveVersion.MinCompatibilityVersion().String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(vs)
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *componentGlobalsRegistry) AddFlags(fs *pflag.FlagSet) {
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.mutex.Lock()
|
||||||
|
for _, globals := range r.componentGlobals {
|
||||||
|
if globals.featureGate != nil {
|
||||||
|
globals.featureGate.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.emulationVersionConfig = make(cliflag.ConfigurationMap)
|
||||||
|
r.featureGatesConfig = make(map[string][]string)
|
||||||
|
r.mutex.Unlock()
|
||||||
|
|
||||||
|
fs.Var(&r.emulationVersionConfig, "emulated-version", ""+
|
||||||
|
"The versions different components emulate their capabilities (APIs, features, ...) of.\n"+
|
||||||
|
"If set, the component will emulate the behavior of this version instead of the underlying binary version.\n"+
|
||||||
|
"Version format could only be major.minor, for example: '--emulated-version=wardle=1.2,kube=1.31'. Options are:\n"+strings.Join(r.versionFlagOptions(true), "\n"))
|
||||||
|
|
||||||
|
fs.Var(cliflag.NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey(&r.featureGatesConfig), "feature-gates", "Comma-separated list of component:key=value pairs that describe feature gates for alpha/experimental features of different components.\n"+
|
||||||
|
"If the component is not specified, defaults to \"kube\". This flag can be repeatedly invoked. For example: --feature-gates 'wardle:featureA=true,wardle:featureB=false' --feature-gates 'kube:featureC=true'"+
|
||||||
|
"Options are:\n"+strings.Join(r.knownFeatures(), "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *componentGlobalsRegistry) Set() error {
|
||||||
|
r.mutex.Lock()
|
||||||
|
defer r.mutex.Unlock()
|
||||||
|
for comp, emuVer := range r.emulationVersionConfig {
|
||||||
|
if _, ok := r.componentGlobals[comp]; !ok {
|
||||||
|
return fmt.Errorf("component not registered: %s", comp)
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("setting %s:emulation version to %s\n", comp, emuVer)
|
||||||
|
v, err := version.Parse(emuVer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.componentGlobals[comp].effectiveVersion.SetEmulationVersion(v)
|
||||||
|
}
|
||||||
|
// Set feature gate emulation version before setting feature gate flag values.
|
||||||
|
for comp, globals := range r.componentGlobals {
|
||||||
|
if globals.featureGate == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("setting %s:feature gate emulation version to %s\n", comp, globals.effectiveVersion.EmulationVersion().String())
|
||||||
if err := globals.featureGate.SetEmulationVersion(globals.effectiveVersion.EmulationVersion()); err != nil {
|
if err := globals.featureGate.SetEmulationVersion(globals.effectiveVersion.EmulationVersion()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for comp, fg := range r.featureGatesConfig {
|
||||||
|
if comp == "" {
|
||||||
|
comp = DefaultKubeComponent
|
||||||
|
}
|
||||||
|
if _, ok := r.componentGlobals[comp]; !ok {
|
||||||
|
return fmt.Errorf("component not registered: %s", comp)
|
||||||
|
}
|
||||||
|
featureGate := r.componentGlobals[comp].featureGate
|
||||||
|
if featureGate == nil {
|
||||||
|
return fmt.Errorf("component featureGate not registered: %s", comp)
|
||||||
|
}
|
||||||
|
flagVal := strings.Join(fg, ",")
|
||||||
|
klog.V(2).Infof("setting %s:feature-gates=%s\n", comp, flagVal)
|
||||||
|
if err := featureGate.Set(flagVal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *componentGlobalsRegistry) ValidateAllComponents() []error {
|
func (r *componentGlobalsRegistry) Validate() []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
|
@ -17,12 +17,22 @@ limitations under the License.
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testComponent = "test"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEffectiveVersionRegistry(t *testing.T) {
|
func TestEffectiveVersionRegistry(t *testing.T) {
|
||||||
r := NewComponentGlobalsRegistry()
|
r := NewComponentGlobalsRegistry()
|
||||||
testComponent := "test"
|
|
||||||
ver1 := NewEffectiveVersion("1.31")
|
ver1 := NewEffectiveVersion("1.31")
|
||||||
ver2 := NewEffectiveVersion("1.28")
|
ver2 := NewEffectiveVersion("1.28")
|
||||||
|
|
||||||
@ -46,3 +56,216 @@ func TestEffectiveVersionRegistry(t *testing.T) {
|
|||||||
t.Fatalf("expected EffectiveVersionFor to return the version overridden")
|
t.Fatalf("expected EffectiveVersionFor to return the version overridden")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testRegistry(t *testing.T) *componentGlobalsRegistry {
|
||||||
|
r := componentGlobalsRegistry{
|
||||||
|
componentGlobals: map[string]ComponentGlobals{},
|
||||||
|
emulationVersionConfig: make(cliflag.ConfigurationMap),
|
||||||
|
featureGatesConfig: make(map[string][]string),
|
||||||
|
}
|
||||||
|
verKube := NewEffectiveVersion("1.31")
|
||||||
|
fgKube := featuregate.NewVersionedFeatureGate(version.MustParse("0.0"))
|
||||||
|
err := fgKube.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{
|
||||||
|
"kubeA": {
|
||||||
|
{Version: version.MustParse("1.31"), Default: true, LockToDefault: true, PreRelease: featuregate.GA},
|
||||||
|
{Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Beta},
|
||||||
|
{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
"kubeB": {
|
||||||
|
{Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
"commonC": {
|
||||||
|
{Version: version.MustParse("1.29"), Default: true, PreRelease: featuregate.Beta},
|
||||||
|
{Version: version.MustParse("1.27"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
verTest := NewEffectiveVersion("2.8")
|
||||||
|
fgTest := featuregate.NewVersionedFeatureGate(version.MustParse("0.0"))
|
||||||
|
err = fgTest.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{
|
||||||
|
"testA": {
|
||||||
|
{Version: version.MustParse("2.10"), Default: true, PreRelease: featuregate.GA},
|
||||||
|
{Version: version.MustParse("2.8"), Default: false, PreRelease: featuregate.Beta},
|
||||||
|
{Version: version.MustParse("2.7"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
"testB": {
|
||||||
|
{Version: version.MustParse("2.9"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
"commonC": {
|
||||||
|
{Version: version.MustParse("2.9"), Default: true, PreRelease: featuregate.Beta},
|
||||||
|
{Version: version.MustParse("2.7"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_ = r.Register(DefaultKubeComponent, verKube, fgKube, true)
|
||||||
|
_ = r.Register(testComponent, verTest, fgTest, true)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionFlagOptions(t *testing.T) {
|
||||||
|
r := testRegistry(t)
|
||||||
|
emuVers := strings.Join(r.versionFlagOptions(true), "\n")
|
||||||
|
expectedEmuVers := "kube=1.31..1.31 (default=1.31)\ntest=2.8..2.8 (default=2.8)"
|
||||||
|
if emuVers != expectedEmuVers {
|
||||||
|
t.Errorf("wanted emulation version flag options to be: %s, got %s", expectedEmuVers, emuVers)
|
||||||
|
}
|
||||||
|
minCompVers := strings.Join(r.versionFlagOptions(false), "\n")
|
||||||
|
expectedMinCompVers := "kube=1.30..1.31 (default=1.30)\ntest=2.7..2.8 (default=2.7)"
|
||||||
|
if minCompVers != expectedMinCompVers {
|
||||||
|
t.Errorf("wanted min compatibility version flag options to be: %s, got %s", expectedMinCompVers, minCompVers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionedFeatureGateFlag(t *testing.T) {
|
||||||
|
r := testRegistry(t)
|
||||||
|
known := strings.Join(r.knownFeatures(), "\n")
|
||||||
|
expectedKnown := "kube:AllAlpha=true|false (ALPHA - default=false)\n" +
|
||||||
|
"kube:AllBeta=true|false (BETA - default=false)\n" +
|
||||||
|
"kube:commonC=true|false (BETA - default=true)\n" +
|
||||||
|
"kube:kubeB=true|false (ALPHA - default=false)\n" +
|
||||||
|
"test:AllAlpha=true|false (ALPHA - default=false)\n" +
|
||||||
|
"test:AllBeta=true|false (BETA - default=false)\n" +
|
||||||
|
"test:commonC=true|false (ALPHA - default=false)\n" +
|
||||||
|
"test:testA=true|false (BETA - default=false)"
|
||||||
|
if known != expectedKnown {
|
||||||
|
t.Errorf("wanted min compatibility version flag options to be:\n%s, got:\n%s", expectedKnown, known)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlags(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
emulationVersionFlag string
|
||||||
|
featureGatesFlag string
|
||||||
|
parseError string
|
||||||
|
expectedKubeEmulationVersion *version.Version
|
||||||
|
expectedTestEmulationVersion *version.Version
|
||||||
|
expectedKubeFeatureValues map[featuregate.Feature]bool
|
||||||
|
expectedTestFeatureValues map[featuregate.Feature]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "setting kube emulation version",
|
||||||
|
emulationVersionFlag: "kube=1.30",
|
||||||
|
expectedKubeEmulationVersion: version.MajorMinor(1, 30),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting kube emulation version, prefix v ok",
|
||||||
|
emulationVersionFlag: "kube=v1.30",
|
||||||
|
expectedKubeEmulationVersion: version.MajorMinor(1, 30),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting test emulation version",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
expectedKubeEmulationVersion: version.MajorMinor(1, 31),
|
||||||
|
expectedTestEmulationVersion: version.MajorMinor(2, 7),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version missing component",
|
||||||
|
emulationVersionFlag: "1.31",
|
||||||
|
parseError: "component not registered: 1.31",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version unregistered component",
|
||||||
|
emulationVersionFlag: "test3=1.31",
|
||||||
|
parseError: "component not registered: test3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid version",
|
||||||
|
emulationVersionFlag: "test=1.foo",
|
||||||
|
parseError: "illegal version string \"1.foo\"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting test feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
featureGatesFlag: "test:testA=true",
|
||||||
|
expectedKubeEmulationVersion: version.MajorMinor(1, 31),
|
||||||
|
expectedTestEmulationVersion: version.MajorMinor(2, 7),
|
||||||
|
expectedKubeFeatureValues: map[featuregate.Feature]bool{"kubeA": true, "kubeB": false, "commonC": true},
|
||||||
|
expectedTestFeatureValues: map[featuregate.Feature]bool{"testA": true, "testB": false, "commonC": false},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting future test feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
featureGatesFlag: "test:testA=true,test:testB=true",
|
||||||
|
parseError: "cannot set feature gate testB to true, feature is PreAlpha at emulated version 2.7",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting kube feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7,kube=1.30",
|
||||||
|
featureGatesFlag: "test:commonC=true,commonC=false,kube:kubeB=true",
|
||||||
|
expectedKubeEmulationVersion: version.MajorMinor(1, 30),
|
||||||
|
expectedTestEmulationVersion: version.MajorMinor(2, 7),
|
||||||
|
expectedKubeFeatureValues: map[featuregate.Feature]bool{"kubeA": false, "kubeB": true, "commonC": false},
|
||||||
|
expectedTestFeatureValues: map[featuregate.Feature]bool{"testA": false, "testB": false, "commonC": true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting locked kube feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
featureGatesFlag: "kubeA=false",
|
||||||
|
parseError: "cannot set feature gate kubeA to false, feature is locked to true",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting unknown test feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
featureGatesFlag: "test:testD=true",
|
||||||
|
parseError: "unrecognized feature gate: testD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting unknown component feature flag",
|
||||||
|
emulationVersionFlag: "test=2.7",
|
||||||
|
featureGatesFlag: "test3:commonC=true",
|
||||||
|
parseError: "component not registered: test3",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
fs := pflag.NewFlagSet("testflag", pflag.ContinueOnError)
|
||||||
|
r := testRegistry(t)
|
||||||
|
r.AddFlags(fs)
|
||||||
|
|
||||||
|
err := fs.Parse([]string{fmt.Sprintf("--emulated-version=%s", test.emulationVersionFlag),
|
||||||
|
fmt.Sprintf("--feature-gates=%s", test.featureGatesFlag)})
|
||||||
|
if err == nil {
|
||||||
|
err = r.Set()
|
||||||
|
}
|
||||||
|
if test.parseError != "" {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), test.parseError) {
|
||||||
|
t.Fatalf("%d: Parse() expected: %v, got: %v", i, test.parseError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%d: Parse() expected: nil, got: %v", i, err)
|
||||||
|
}
|
||||||
|
if test.expectedKubeEmulationVersion != nil {
|
||||||
|
v := r.EffectiveVersionFor("kube").EmulationVersion()
|
||||||
|
if !v.EqualTo(test.expectedKubeEmulationVersion) {
|
||||||
|
t.Fatalf("%d: EmulationVersion expected: %s, got: %s", i, test.expectedKubeEmulationVersion.String(), v.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if test.expectedTestEmulationVersion != nil {
|
||||||
|
v := r.EffectiveVersionFor("test").EmulationVersion()
|
||||||
|
if !v.EqualTo(test.expectedTestEmulationVersion) {
|
||||||
|
t.Fatalf("%d: EmulationVersion expected: %s, got: %s", i, test.expectedTestEmulationVersion.String(), v.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for f, v := range test.expectedKubeFeatureValues {
|
||||||
|
if r.FeatureGateFor(DefaultKubeComponent).Enabled(f) != v {
|
||||||
|
t.Errorf("%d: expected kube feature Enabled(%s)=%v", i, f, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for f, v := range test.expectedTestFeatureValues {
|
||||||
|
if r.FeatureGateFor(testComponent).Enabled(f) != v {
|
||||||
|
t.Errorf("%d: expected test feature Enabled(%s)=%v", i, f, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -131,29 +131,29 @@ func TestValidate(t *testing.T) {
|
|||||||
func TestEffectiveVersionsFlag(t *testing.T) {
|
func TestEffectiveVersionsFlag(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
emulationVerson string
|
emulationVersion string
|
||||||
expectedEmulationVersion *version.Version
|
expectedEmulationVersion *version.Version
|
||||||
parseError string
|
parseError string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "major.minor ok",
|
name: "major.minor ok",
|
||||||
emulationVerson: "1.30",
|
emulationVersion: "1.30",
|
||||||
expectedEmulationVersion: version.MajorMinor(1, 30),
|
expectedEmulationVersion: version.MajorMinor(1, 30),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v prefix ok",
|
name: "v prefix ok",
|
||||||
emulationVerson: "v1.30",
|
emulationVersion: "v1.30",
|
||||||
expectedEmulationVersion: version.MajorMinor(1, 30),
|
expectedEmulationVersion: version.MajorMinor(1, 30),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "semantic version not ok",
|
name: "semantic version not ok",
|
||||||
emulationVerson: "1.30.1",
|
emulationVersion: "1.30.1",
|
||||||
parseError: "version 1.30.1 is not in the format of major.minor",
|
parseError: "version 1.30.1 is not in the format of major.minor",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid version",
|
name: "invalid version",
|
||||||
emulationVerson: "1.foo",
|
emulationVersion: "1.foo",
|
||||||
parseError: "illegal version string",
|
parseError: "illegal version string",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
@ -162,7 +162,7 @@ func TestEffectiveVersionsFlag(t *testing.T) {
|
|||||||
effective := NewEffectiveVersion("1.30")
|
effective := NewEffectiveVersion("1.30")
|
||||||
effective.AddFlags(fs, "test")
|
effective.AddFlags(fs, "test")
|
||||||
|
|
||||||
err := fs.Parse([]string{fmt.Sprintf("--test-emulated-version=%s", test.emulationVerson)})
|
err := fs.Parse([]string{fmt.Sprintf("--test-emulated-version=%s", test.emulationVersion)})
|
||||||
if test.parseError != "" {
|
if test.parseError != "" {
|
||||||
if !strings.Contains(err.Error(), test.parseError) {
|
if !strings.Contains(err.Error(), test.parseError) {
|
||||||
t.Fatalf("%d: Parse() Expected %v, Got %v", i, test.parseError, err)
|
t.Fatalf("%d: Parse() Expected %v, Got %v", i, test.parseError, err)
|
||||||
|
@ -164,7 +164,7 @@ func (o *CloudControllerManagerOptions) Flags(allControllers []string, disabledB
|
|||||||
fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
|
||||||
fs.StringVar(&o.Generic.ClientConnection.Kubeconfig, "kubeconfig", o.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).")
|
fs.StringVar(&o.Generic.ClientConnection.Kubeconfig, "kubeconfig", o.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).")
|
||||||
fs.DurationVar(&o.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", o.NodeStatusUpdateFrequency.Duration, "Specifies how often the controller updates nodes' status.")
|
fs.DurationVar(&o.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", o.NodeStatusUpdateFrequency.Duration, "Specifies how often the controller updates nodes' status.")
|
||||||
utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("generic"), "")
|
utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("generic"))
|
||||||
|
|
||||||
return fss
|
return fss
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,9 @@ import (
|
|||||||
// while still allowing the distribution of key-value pairs across multiple flag invocations.
|
// while still allowing the distribution of key-value pairs across multiple flag invocations.
|
||||||
// For example: `--flag "a:hello" --flag "b:again" --flag "b:beautiful" --flag "c:world"` results in `{"a": ["hello"], "b": ["again", "beautiful"], "c": ["world"]}`
|
// For example: `--flag "a:hello" --flag "b:again" --flag "b:beautiful" --flag "c:world"` results in `{"a": ["hello"], "b": ["again", "beautiful"], "c": ["world"]}`
|
||||||
type ColonSeparatedMultimapStringString struct {
|
type ColonSeparatedMultimapStringString struct {
|
||||||
Multimap *map[string][]string
|
Multimap *map[string][]string
|
||||||
initialized bool // set to true after the first Set call
|
initialized bool // set to true after the first Set call
|
||||||
|
allowDefaultEmptyKey bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewColonSeparatedMultimapStringString takes a pointer to a map[string][]string and returns the
|
// NewColonSeparatedMultimapStringString takes a pointer to a map[string][]string and returns the
|
||||||
@ -43,6 +44,12 @@ func NewColonSeparatedMultimapStringString(m *map[string][]string) *ColonSeparat
|
|||||||
return &ColonSeparatedMultimapStringString{Multimap: m}
|
return &ColonSeparatedMultimapStringString{Multimap: m}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey takes a pointer to a map[string][]string and returns the
|
||||||
|
// ColonSeparatedMultimapStringString flag parsing shim for that map. It allows default empty key with no colon in the flag.
|
||||||
|
func NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey(m *map[string][]string) *ColonSeparatedMultimapStringString {
|
||||||
|
return &ColonSeparatedMultimapStringString{Multimap: m, allowDefaultEmptyKey: true}
|
||||||
|
}
|
||||||
|
|
||||||
// Set implements github.com/spf13/pflag.Value
|
// Set implements github.com/spf13/pflag.Value
|
||||||
func (m *ColonSeparatedMultimapStringString) Set(value string) error {
|
func (m *ColonSeparatedMultimapStringString) Set(value string) error {
|
||||||
if m.Multimap == nil {
|
if m.Multimap == nil {
|
||||||
@ -58,11 +65,16 @@ func (m *ColonSeparatedMultimapStringString) Set(value string) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
kv := strings.SplitN(pair, ":", 2)
|
kv := strings.SplitN(pair, ":", 2)
|
||||||
if len(kv) != 2 {
|
var k, v string
|
||||||
return fmt.Errorf("malformed pair, expect string:string")
|
if m.allowDefaultEmptyKey && len(kv) == 1 {
|
||||||
|
v = strings.TrimSpace(kv[0])
|
||||||
|
} else {
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return fmt.Errorf("malformed pair, expect string:string")
|
||||||
|
}
|
||||||
|
k = strings.TrimSpace(kv[0])
|
||||||
|
v = strings.TrimSpace(kv[1])
|
||||||
}
|
}
|
||||||
k := strings.TrimSpace(kv[0])
|
|
||||||
v := strings.TrimSpace(kv[1])
|
|
||||||
(*m.Multimap)[k] = append((*m.Multimap)[k], v)
|
(*m.Multimap)[k] = append((*m.Multimap)[k], v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -98,6 +98,21 @@ func TestSetColonSeparatedMultimapStringString(t *testing.T) {
|
|||||||
&ColonSeparatedMultimapStringString{
|
&ColonSeparatedMultimapStringString{
|
||||||
initialized: true,
|
initialized: true,
|
||||||
Multimap: &map[string][]string{}}, ""},
|
Multimap: &map[string][]string{}}, ""},
|
||||||
|
{"empty key no colon", []string{"foo"},
|
||||||
|
NewColonSeparatedMultimapStringString(&nilMap),
|
||||||
|
&ColonSeparatedMultimapStringString{
|
||||||
|
initialized: true,
|
||||||
|
Multimap: &map[string][]string{
|
||||||
|
"": {"foo"},
|
||||||
|
}}, "malformed pair, expect string:string"},
|
||||||
|
{"empty key no colon allowed", []string{"foo"},
|
||||||
|
NewColonSeparatedMultimapStringStringAllowDefaultEmptyKey(&nilMap),
|
||||||
|
&ColonSeparatedMultimapStringString{
|
||||||
|
initialized: true,
|
||||||
|
allowDefaultEmptyKey: true,
|
||||||
|
Multimap: &map[string][]string{
|
||||||
|
"": {"foo"},
|
||||||
|
}}, ""},
|
||||||
{"empty key", []string{":foo"},
|
{"empty key", []string{":foo"},
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
NewColonSeparatedMultimapStringString(&nilMap),
|
||||||
&ColonSeparatedMultimapStringString{
|
&ColonSeparatedMultimapStringString{
|
||||||
|
@ -115,7 +115,6 @@ type FeatureGate interface {
|
|||||||
// config against potential feature gate changes before committing those changes.
|
// config against potential feature gate changes before committing those changes.
|
||||||
DeepCopy() MutableVersionedFeatureGate
|
DeepCopy() MutableVersionedFeatureGate
|
||||||
// Validate checks if the flag gates are valid at the emulated version.
|
// Validate checks if the flag gates are valid at the emulated version.
|
||||||
// Should always be called after Set when DeferErrorsToValidation is set to true.
|
|
||||||
Validate() []error
|
Validate() []error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +124,9 @@ type MutableFeatureGate interface {
|
|||||||
FeatureGate
|
FeatureGate
|
||||||
|
|
||||||
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
|
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
|
||||||
AddFlag(fs *pflag.FlagSet, prefix string)
|
AddFlag(fs *pflag.FlagSet)
|
||||||
|
// Close sets closed to true, and prevents subsequent calls to Add
|
||||||
|
Close()
|
||||||
// Set parses and stores flag gates for known features
|
// Set parses and stores flag gates for known features
|
||||||
// from a string like feature1=true,feature2=false,...
|
// from a string like feature1=true,feature2=false,...
|
||||||
Set(value string) error
|
Set(value string) error
|
||||||
@ -163,10 +164,6 @@ type MutableVersionedFeatureGate interface {
|
|||||||
// Otherwise, the emulationVersion will be the same as the binary version.
|
// Otherwise, the emulationVersion will be the same as the binary version.
|
||||||
// If set, the feature defaults and availability will be as if the binary is at the emulated version.
|
// If set, the feature defaults and availability will be as if the binary is at the emulated version.
|
||||||
SetEmulationVersion(emulationVersion *version.Version) error
|
SetEmulationVersion(emulationVersion *version.Version) error
|
||||||
// DeferErrorsToValidation defers the errors of Set function to the Validate() function if true.
|
|
||||||
// This is used when the user wants to set the feature gate flag before the emulationVersion is finalized.
|
|
||||||
// Validate() should aways be called later to check for flag errors if deferErrorsToValidation is true.
|
|
||||||
DeferErrorsToValidation(val bool)
|
|
||||||
// GetAll returns a copy of the map of known feature names to versioned feature specs.
|
// GetAll returns a copy of the map of known feature names to versioned feature specs.
|
||||||
GetAllVersioned() map[Feature]VersionedSpecs
|
GetAllVersioned() map[Feature]VersionedSpecs
|
||||||
// AddVersioned adds versioned feature specs to the featureGate.
|
// AddVersioned adds versioned feature specs to the featureGate.
|
||||||
@ -199,12 +196,8 @@ type featureGate struct {
|
|||||||
// while enabled keeps the values of all resolved features.
|
// while enabled keeps the values of all resolved features.
|
||||||
enabledRaw atomic.Value
|
enabledRaw atomic.Value
|
||||||
// closed is set to true when AddFlag is called, and prevents subsequent calls to Add
|
// closed is set to true when AddFlag is called, and prevents subsequent calls to Add
|
||||||
closed bool
|
closed bool
|
||||||
// deferErrorsToValidation could be set to true to defer checking flag setting error,
|
emulationVersion atomic.Pointer[version.Version]
|
||||||
// because the emulationVersion may not be the final emulationVersion when the flag is set.
|
|
||||||
// Validate() should aways be called later to check for flag errors if deferErrorsToValidation is true.
|
|
||||||
deferErrorsToValidation bool
|
|
||||||
emulationVersion atomic.Pointer[version.Version]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUnsetAlphaGates(known map[Feature]VersionedSpecs, enabled map[Feature]bool, val bool, cVer *version.Version) {
|
func setUnsetAlphaGates(known map[Feature]VersionedSpecs, enabled map[Feature]bool, val bool, cVer *version.Version) {
|
||||||
@ -288,17 +281,10 @@ func (f *featureGate) Set(value string) error {
|
|||||||
}
|
}
|
||||||
m[k] = boolValue
|
m[k] = boolValue
|
||||||
}
|
}
|
||||||
err := f.SetFromMap(m)
|
return f.SetFromMap(m)
|
||||||
// ignores SetFromMap error, because the emulationVersion may not be the final emulationVersion when the flag is set.
|
|
||||||
// Validate() should aways be called later to check for flag errors if deferErrorsToValidation is true.
|
|
||||||
if f.deferErrorsToValidation {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if the flag gates are valid at the emulated version.
|
// Validate checks if the flag gates are valid at the emulated version.
|
||||||
// Should always be called after Set when DeferErrorsToValidation is set to true.
|
|
||||||
func (f *featureGate) Validate() []error {
|
func (f *featureGate) Validate() []error {
|
||||||
m, ok := f.enabledRaw.Load().(map[string]bool)
|
m, ok := f.enabledRaw.Load().(map[string]bool)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -502,15 +488,6 @@ func (f *featureGate) GetAllVersioned() map[Feature]VersionedSpecs {
|
|||||||
return retval
|
return retval
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeferErrorsToValidation could be used to defer checking flag setting error,
|
|
||||||
// because the emulationVersion may not be the final emulationVersion when the flag is set.
|
|
||||||
// Validate() should aways be called later to check for flag errors if deferErrorsToValidation is true.
|
|
||||||
func (f *featureGate) DeferErrorsToValidation(val bool) {
|
|
||||||
f.lock.Lock()
|
|
||||||
defer f.lock.Unlock()
|
|
||||||
f.deferErrorsToValidation = val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *featureGate) SetEmulationVersion(emulationVersion *version.Version) error {
|
func (f *featureGate) SetEmulationVersion(emulationVersion *version.Version) error {
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
defer f.lock.Unlock()
|
defer f.lock.Unlock()
|
||||||
@ -577,21 +554,23 @@ func getCurrentVersion(v VersionedSpecs, emulationVersion *version.Version) *Fea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
|
// Close sets closed to true, and prevents subsequent calls to Add
|
||||||
func (f *featureGate) AddFlag(fs *pflag.FlagSet, prefix string) {
|
func (f *featureGate) Close() {
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
|
f.closed = true
|
||||||
|
f.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlag adds a flag for setting global feature gates to the specified FlagSet.
|
||||||
|
func (f *featureGate) AddFlag(fs *pflag.FlagSet) {
|
||||||
// TODO(mtaufen): Shouldn't we just close it on the first Set/SetFromMap instead?
|
// TODO(mtaufen): Shouldn't we just close it on the first Set/SetFromMap instead?
|
||||||
// Not all components expose a feature gates flag using this AddFlag method, and
|
// Not all components expose a feature gates flag using this AddFlag method, and
|
||||||
// in the future, all components will completely stop exposing a feature gates flag,
|
// in the future, all components will completely stop exposing a feature gates flag,
|
||||||
// in favor of componentconfig.
|
// in favor of componentconfig.
|
||||||
f.closed = true
|
f.Close()
|
||||||
f.lock.Unlock()
|
|
||||||
|
|
||||||
known := f.KnownFeatures()
|
known := f.KnownFeatures()
|
||||||
if len(prefix) > 0 && !strings.HasSuffix(prefix, "-") {
|
fs.Var(f, flagName, ""+
|
||||||
prefix += "-"
|
|
||||||
}
|
|
||||||
fs.Var(f, prefix+flagName, ""+
|
|
||||||
"A set of key=value pairs that describe feature gates for alpha/experimental features. "+
|
"A set of key=value pairs that describe feature gates for alpha/experimental features. "+
|
||||||
"Options are:\n"+strings.Join(known, "\n"))
|
"Options are:\n"+strings.Join(known, "\n"))
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ func TestFeatureGateFlag(t *testing.T) {
|
|||||||
testLockedFalseGate: {Default: false, PreRelease: GA, LockToDefault: true},
|
testLockedFalseGate: {Default: false, PreRelease: GA, LockToDefault: true},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
f.AddFlag(fs, "")
|
f.AddFlag(fs)
|
||||||
err = fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)})
|
err = fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)})
|
||||||
if test.parseError != "" {
|
if test.parseError != "" {
|
||||||
if !strings.Contains(err.Error(), test.parseError) {
|
if !strings.Contains(err.Error(), test.parseError) {
|
||||||
@ -679,7 +679,7 @@ func TestFeatureGateOverrideDefault(t *testing.T) {
|
|||||||
t.Run("returns error if already added to flag set", func(t *testing.T) {
|
t.Run("returns error if already added to flag set", func(t *testing.T) {
|
||||||
f := NewFeatureGate()
|
f := NewFeatureGate()
|
||||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||||
f.AddFlag(fs, "")
|
f.AddFlag(fs)
|
||||||
|
|
||||||
if err := f.OverrideDefault("TestFeature", true); err == nil {
|
if err := f.OverrideDefault("TestFeature", true); err == nil {
|
||||||
t.Error("expected a non-nil error to be returned")
|
t.Error("expected a non-nil error to be returned")
|
||||||
@ -986,7 +986,9 @@ func TestVersionedFeatureGateFlag(t *testing.T) {
|
|||||||
t.Run(test.arg, func(t *testing.T) {
|
t.Run(test.arg, func(t *testing.T) {
|
||||||
fs := pflag.NewFlagSet("testfeaturegateflag", pflag.ContinueOnError)
|
fs := pflag.NewFlagSet("testfeaturegateflag", pflag.ContinueOnError)
|
||||||
f := NewVersionedFeatureGate(version.MustParse("1.29"))
|
f := NewVersionedFeatureGate(version.MustParse("1.29"))
|
||||||
f.DeferErrorsToValidation(true)
|
if err := f.SetEmulationVersion(version.MustParse("1.28")); err != nil {
|
||||||
|
t.Fatalf("failed to SetEmulationVersion: %v", err)
|
||||||
|
}
|
||||||
err := f.AddVersioned(map[Feature]VersionedSpecs{
|
err := f.AddVersioned(map[Feature]VersionedSpecs{
|
||||||
testGAGate: {
|
testGAGate: {
|
||||||
{Version: version.MustParse("1.29"), Default: true, PreRelease: GA},
|
{Version: version.MustParse("1.29"), Default: true, PreRelease: GA},
|
||||||
@ -1011,14 +1013,12 @@ func TestVersionedFeatureGateFlag(t *testing.T) {
|
|||||||
testBetaGateNoVersion: {Default: false, PreRelease: Beta},
|
testBetaGateNoVersion: {Default: false, PreRelease: Beta},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
f.AddFlag(fs, "")
|
f.AddFlag(fs)
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
err = fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)})
|
err = fs.Parse([]string{fmt.Sprintf("--%s=%s", flagName, test.arg)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
} else {
|
|
||||||
errs = append(errs, f.SetEmulationVersion(version.MustParse("1.28")))
|
|
||||||
}
|
}
|
||||||
err = utilerrors.NewAggregate(errs)
|
err = utilerrors.NewAggregate(errs)
|
||||||
if test.parseError != "" {
|
if test.parseError != "" {
|
||||||
@ -1425,7 +1425,7 @@ func TestVersionedFeatureGateOverrideDefault(t *testing.T) {
|
|||||||
f := NewVersionedFeatureGate(version.MustParse("1.29"))
|
f := NewVersionedFeatureGate(version.MustParse("1.29"))
|
||||||
require.NoError(t, f.SetEmulationVersion(version.MustParse("1.28")))
|
require.NoError(t, f.SetEmulationVersion(version.MustParse("1.28")))
|
||||||
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||||
f.AddFlag(fs, "")
|
f.AddFlag(fs)
|
||||||
|
|
||||||
if err := f.OverrideDefault("TestFeature", true); err == nil {
|
if err := f.OverrideDefault("TestFeature", true); err == nil {
|
||||||
t.Error("expected a non-nil error to be returned")
|
t.Error("expected a non-nil error to be returned")
|
||||||
|
@ -67,7 +67,7 @@ func NewLoggerCommand() *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
logsapi.AddFeatureGates(featureGate)
|
logsapi.AddFeatureGates(featureGate)
|
||||||
featureGate.AddFlag(cmd.Flags(), "")
|
featureGate.AddFlag(cmd.Flags())
|
||||||
logsapi.AddFlags(c, cmd.Flags())
|
logsapi.AddFlags(c, cmd.Flags())
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func NewLoggerCommand() *cobra.Command {
|
|||||||
// Shouldn't happen.
|
// Shouldn't happen.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
featureGate.AddFlag(cmd.Flags(), "")
|
featureGate.AddFlag(cmd.Flags())
|
||||||
logsapi.AddFlags(c, cmd.Flags())
|
logsapi.AddFlags(c, cmd.Flags())
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import (
|
|||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utilversion "k8s.io/apiserver/pkg/util/version"
|
utilversion "k8s.io/apiserver/pkg/util/version"
|
||||||
"k8s.io/component-base/featuregate"
|
|
||||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
|
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
|
||||||
"k8s.io/kube-aggregator/pkg/apiserver"
|
"k8s.io/kube-aggregator/pkg/apiserver"
|
||||||
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
||||||
@ -61,15 +60,13 @@ type AggregatorOptions struct {
|
|||||||
// with a default AggregatorOptions.
|
// with a default AggregatorOptions.
|
||||||
func NewCommandStartAggregator(ctx context.Context, defaults *AggregatorOptions) *cobra.Command {
|
func NewCommandStartAggregator(ctx context.Context, defaults *AggregatorOptions) *cobra.Command {
|
||||||
o := *defaults
|
o := *defaults
|
||||||
featureGate := o.ServerRunOptions.FeatureGate.(featuregate.MutableVersionedFeatureGate)
|
|
||||||
effectiveVersion := o.ServerRunOptions.EffectiveVersion.(utilversion.MutableEffectiveVersion)
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Short: "Launch a API aggregator and proxy server",
|
Short: "Launch a API aggregator and proxy server",
|
||||||
Long: "Launch a API aggregator and proxy server",
|
Long: "Launch a API aggregator and proxy server",
|
||||||
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
return utilversion.DefaultComponentGlobalsRegistry.Set()
|
||||||
|
},
|
||||||
RunE: func(c *cobra.Command, args []string) error {
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := o.Complete(); err != nil {
|
if err := o.Complete(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -85,8 +82,7 @@ func NewCommandStartAggregator(ctx context.Context, defaults *AggregatorOptions)
|
|||||||
cmd.SetContext(ctx)
|
cmd.SetContext(ctx)
|
||||||
|
|
||||||
fs := cmd.Flags()
|
fs := cmd.Flags()
|
||||||
featureGate.AddFlag(fs, "")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(fs)
|
||||||
effectiveVersion.AddFlags(fs, "")
|
|
||||||
|
|
||||||
o.AddFlags(fs)
|
o.AddFlags(fs)
|
||||||
return cmd
|
return cmd
|
||||||
@ -107,7 +103,7 @@ func NewDefaultOptions(out, err io.Writer) *AggregatorOptions {
|
|||||||
// You can also have the flag setting the effectiveVersion of the aggregator apiserver, and
|
// You can also have the flag setting the effectiveVersion of the aggregator apiserver, and
|
||||||
// having a mapping from the aggregator apiserver version to generic apiserver version.
|
// having a mapping from the aggregator apiserver version to generic apiserver version.
|
||||||
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
effectiveVersion, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
||||||
utilversion.ComponentGenericAPIServer, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
utilversion.DefaultKubeComponent, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
||||||
o := &AggregatorOptions{
|
o := &AggregatorOptions{
|
||||||
ServerRunOptions: genericoptions.NewServerRunOptions(featureGate, effectiveVersion),
|
ServerRunOptions: genericoptions.NewServerRunOptions(featureGate, effectiveVersion),
|
||||||
RecommendedOptions: genericoptions.NewRecommendedOptions(
|
RecommendedOptions: genericoptions.NewRecommendedOptions(
|
||||||
|
@ -41,7 +41,7 @@ var (
|
|||||||
// Codecs provides methods for retrieving codecs and serializers for specific
|
// Codecs provides methods for retrieving codecs and serializers for specific
|
||||||
// versions and content types.
|
// versions and content types.
|
||||||
Codecs = serializer.NewCodecFactory(Scheme)
|
Codecs = serializer.NewCodecFactory(Scheme)
|
||||||
WardleComponentName = "wardle-server"
|
WardleComponentName = "wardle"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -18,7 +18,6 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -36,6 +35,7 @@ import (
|
|||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utilversion "k8s.io/apiserver/pkg/util/version"
|
utilversion "k8s.io/apiserver/pkg/util/version"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
|
"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
|
||||||
"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
|
"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
|
||||||
"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
|
"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
|
||||||
@ -61,7 +61,7 @@ type WardleServerOptions struct {
|
|||||||
|
|
||||||
func mapWardleEffectiveVersionToKubeEffectiveVersion(registry utilversion.ComponentGlobalsRegistry) error {
|
func mapWardleEffectiveVersionToKubeEffectiveVersion(registry utilversion.ComponentGlobalsRegistry) error {
|
||||||
wardleVer := registry.EffectiveVersionFor(apiserver.WardleComponentName)
|
wardleVer := registry.EffectiveVersionFor(apiserver.WardleComponentName)
|
||||||
kubeVer := registry.EffectiveVersionFor(utilversion.ComponentGenericAPIServer).(utilversion.MutableEffectiveVersion)
|
kubeVer := registry.EffectiveVersionFor(utilversion.DefaultKubeComponent).(utilversion.MutableEffectiveVersion)
|
||||||
// map from wardle emulation version to kube emulation version.
|
// map from wardle emulation version to kube emulation version.
|
||||||
emulationVersionMap := map[string]string{
|
emulationVersionMap := map[string]string{
|
||||||
"1.2": kubeVer.BinaryVersion().AddMinor(1).String(),
|
"1.2": kubeVer.BinaryVersion().AddMinor(1).String(),
|
||||||
@ -99,6 +99,13 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti
|
|||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Short: "Launch a wardle API server",
|
Short: "Launch a wardle API server",
|
||||||
Long: "Launch a wardle API server",
|
Long: "Launch a wardle API server",
|
||||||
|
PersistentPreRunE: func(*cobra.Command, []string) error {
|
||||||
|
if err := utilversion.DefaultComponentGlobalsRegistry.Set(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// convert wardle effective version to kube effective version to be used in generic api server, and set the generic api server feature gate.
|
||||||
|
return mapWardleEffectiveVersionToKubeEffectiveVersion(utilversion.DefaultComponentGlobalsRegistry)
|
||||||
|
},
|
||||||
RunE: func(c *cobra.Command, args []string) error {
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
if err := o.Complete(); err != nil {
|
if err := o.Complete(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -118,12 +125,19 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti
|
|||||||
o.RecommendedOptions.AddFlags(flags)
|
o.RecommendedOptions.AddFlags(flags)
|
||||||
|
|
||||||
wardleEffectiveVersion := utilversion.NewEffectiveVersion("1.2")
|
wardleEffectiveVersion := utilversion.NewEffectiveVersion("1.2")
|
||||||
utilruntime.Must(utilversion.DefaultComponentGlobalsRegistry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, nil, false))
|
wardleFeatureGate := featuregate.NewVersionedFeatureGate(version.MustParse("1.2"))
|
||||||
_, featureGate := utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
utilruntime.Must(wardleFeatureGate.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{
|
||||||
utilversion.ComponentGenericAPIServer, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
"BanFlunder": {
|
||||||
|
{Version: version.MustParse("1.2"), Default: true, PreRelease: featuregate.GA},
|
||||||
|
{Version: version.MustParse("1.1"), Default: false, PreRelease: featuregate.Beta},
|
||||||
|
{Version: version.MustParse("1.0"), Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
utilruntime.Must(utilversion.DefaultComponentGlobalsRegistry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, wardleFeatureGate, false))
|
||||||
|
_, _ = utilversion.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
|
||||||
|
utilversion.DefaultKubeComponent, utilversion.DefaultKubeEffectiveVersion(), utilfeature.DefaultMutableFeatureGate)
|
||||||
|
|
||||||
wardleEffectiveVersion.AddFlags(flags, "wardle-")
|
utilversion.DefaultComponentGlobalsRegistry.AddFlags(flags)
|
||||||
featureGate.AddFlag(flags, "")
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -132,29 +146,19 @@ func NewCommandStartWardleServer(ctx context.Context, defaults *WardleServerOpti
|
|||||||
func (o WardleServerOptions) Validate(args []string) error {
|
func (o WardleServerOptions) Validate(args []string) error {
|
||||||
errors := []error{}
|
errors := []error{}
|
||||||
errors = append(errors, o.RecommendedOptions.Validate()...)
|
errors = append(errors, o.RecommendedOptions.Validate()...)
|
||||||
errors = append(errors, utilversion.DefaultComponentGlobalsRegistry.ValidateAllComponents()...)
|
errors = append(errors, utilversion.DefaultComponentGlobalsRegistry.Validate()...)
|
||||||
return utilerrors.NewAggregate(errors)
|
return utilerrors.NewAggregate(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete fills in fields required to have valid data
|
// Complete fills in fields required to have valid data
|
||||||
func (o *WardleServerOptions) Complete() error {
|
func (o *WardleServerOptions) Complete() error {
|
||||||
// register admission plugins
|
if utilversion.DefaultComponentGlobalsRegistry.FeatureGateFor(apiserver.WardleComponentName).Enabled("BanFlunder") {
|
||||||
banflunder.Register(o.RecommendedOptions.Admission.Plugins)
|
// register admission plugins
|
||||||
|
banflunder.Register(o.RecommendedOptions.Admission.Plugins)
|
||||||
|
|
||||||
// add admission plugins to the RecommendedPluginOrder
|
// add admission plugins to the RecommendedPluginOrder
|
||||||
o.RecommendedOptions.Admission.RecommendedPluginOrder = append(o.RecommendedOptions.Admission.RecommendedPluginOrder, "BanFlunder")
|
o.RecommendedOptions.Admission.RecommendedPluginOrder = append(o.RecommendedOptions.Admission.RecommendedPluginOrder, "BanFlunder")
|
||||||
|
|
||||||
// convert wardle effective version to kube effective version to be used in generic api server, and set the generic api server feature gate.
|
|
||||||
if err := mapWardleEffectiveVersionToKubeEffectiveVersion(utilversion.DefaultComponentGlobalsRegistry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
if err := utilversion.DefaultComponentGlobalsRegistry.SetAllComponents(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if errs := utilversion.DefaultComponentGlobalsRegistry.ValidateAllComponents(); len(errs) > 0 {
|
|
||||||
return errors.Join(errs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +189,8 @@ func (o *WardleServerOptions) Config() (*apiserver.Config, error) {
|
|||||||
serverConfig.OpenAPIV3Config.Info.Title = "Wardle"
|
serverConfig.OpenAPIV3Config.Info.Title = "Wardle"
|
||||||
serverConfig.OpenAPIV3Config.Info.Version = "0.1"
|
serverConfig.OpenAPIV3Config.Info.Version = "0.1"
|
||||||
|
|
||||||
serverConfig.FeatureGate = utilversion.DefaultComponentGlobalsRegistry.FeatureGateFor(utilversion.ComponentGenericAPIServer)
|
serverConfig.FeatureGate = utilversion.DefaultComponentGlobalsRegistry.FeatureGateFor(utilversion.DefaultKubeComponent)
|
||||||
serverConfig.EffectiveVersion = utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.ComponentGenericAPIServer)
|
serverConfig.EffectiveVersion = utilversion.DefaultComponentGlobalsRegistry.EffectiveVersionFor(utilversion.DefaultKubeComponent)
|
||||||
|
|
||||||
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
|
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -50,7 +50,7 @@ func TestMapBinaryEffectiveVersionToKubeEffectiveVersion(t *testing.T) {
|
|||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
registry := utilversion.NewComponentGlobalsRegistry()
|
registry := utilversion.NewComponentGlobalsRegistry()
|
||||||
_ = registry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, nil, true)
|
_ = registry.Register(apiserver.WardleComponentName, wardleEffectiveVersion, nil, true)
|
||||||
_ = registry.Register(utilversion.ComponentGenericAPIServer, defaultKubeEffectiveVersion, nil, true)
|
_ = registry.Register(utilversion.DefaultKubeComponent, defaultKubeEffectiveVersion, nil, true)
|
||||||
|
|
||||||
wardleEffectiveVersion.SetEmulationVersion(tc.wardleEmulationVer)
|
wardleEffectiveVersion.SetEmulationVersion(tc.wardleEmulationVer)
|
||||||
err := mapWardleEffectiveVersionToKubeEffectiveVersion(registry)
|
err := mapWardleEffectiveVersionToKubeEffectiveVersion(registry)
|
||||||
@ -59,7 +59,7 @@ func TestMapBinaryEffectiveVersionToKubeEffectiveVersion(t *testing.T) {
|
|||||||
t.Fatal("expected error, no error found")
|
t.Fatal("expected error, no error found")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert.True(t, registry.EffectiveVersionFor(utilversion.ComponentGenericAPIServer).EmulationVersion().EqualTo(tc.expectedKubeEmulationVer))
|
assert.True(t, registry.EffectiveVersionFor(utilversion.DefaultKubeComponent).EmulationVersion().EqualTo(tc.expectedKubeEmulationVer))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func NewCommand() *cobra.Command {
|
|||||||
fs = sharedFlagSets.FlagSet("other")
|
fs = sharedFlagSets.FlagSet("other")
|
||||||
featureGate := featuregate.NewFeatureGate()
|
featureGate := featuregate.NewFeatureGate()
|
||||||
utilruntime.Must(logsapi.AddFeatureGates(featureGate))
|
utilruntime.Must(logsapi.AddFeatureGates(featureGate))
|
||||||
featureGate.AddFlag(fs, "")
|
featureGate.AddFlag(fs)
|
||||||
|
|
||||||
fs = cmd.PersistentFlags()
|
fs = cmd.PersistentFlags()
|
||||||
for _, f := range sharedFlagSets.FlagSets {
|
for _, f := range sharedFlagSets.FlagSets {
|
||||||
|
@ -3008,7 +3008,7 @@ func TestEmulatedStorageVersion(t *testing.T) {
|
|||||||
t.Run(emulatedVersion, func(t *testing.T) {
|
t.Run(emulatedVersion, func(t *testing.T) {
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(
|
server := kubeapiservertesting.StartTestServerOrDie(
|
||||||
t, &kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: emulatedVersion},
|
t, &kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: emulatedVersion},
|
||||||
[]string{"--emulated-version=" + emulatedVersion, `--storage-media-type=application/json`}, framework.SharedEtcd())
|
[]string{"--emulated-version=kube=" + emulatedVersion, `--storage-media-type=application/json`}, framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(server.ClientConfig)
|
client := clientset.NewForConfigOrDie(server.ClientConfig)
|
||||||
@ -3106,7 +3106,7 @@ func TestEmulatedStorageVersion(t *testing.T) {
|
|||||||
func TestEnableEmulationVersion(t *testing.T) {
|
func TestEnableEmulationVersion(t *testing.T) {
|
||||||
server := kubeapiservertesting.StartTestServerOrDie(t,
|
server := kubeapiservertesting.StartTestServerOrDie(t,
|
||||||
&kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: "1.32"},
|
&kubeapiservertesting.TestServerInstanceOptions{BinaryVersion: "1.32"},
|
||||||
[]string{"--emulated-version=1.31"}, framework.SharedEtcd())
|
[]string{"--emulated-version=kube=1.31"}, framework.SharedEtcd())
|
||||||
defer server.TearDownFn()
|
defer server.TearDownFn()
|
||||||
|
|
||||||
rt, err := restclient.TransportFor(server.ClientConfig)
|
rt, err := restclient.TransportFor(server.ClientConfig)
|
||||||
|
@ -292,7 +292,8 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
"--etcd-servers", framework.GetEtcdURL(),
|
"--etcd-servers", framework.GetEtcdURL(),
|
||||||
"--cert-dir", wardleCertDir,
|
"--cert-dir", wardleCertDir,
|
||||||
"--kubeconfig", wardleToKASKubeConfigFile,
|
"--kubeconfig", wardleToKASKubeConfigFile,
|
||||||
"--wardle-emulated-version", "1.1",
|
"--emulated-version", "wardle=1.1",
|
||||||
|
"--feature-gates", "wardle:BanFlunder=true",
|
||||||
})
|
})
|
||||||
if err := wardleCmd.Execute(); err != nil {
|
if err := wardleCmd.Execute(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
@ -387,6 +388,7 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "panda",
|
Name: "panda",
|
||||||
},
|
},
|
||||||
|
DisallowedFlunders: []string{"badname"},
|
||||||
}, metav1.CreateOptions{})
|
}, metav1.CreateOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -402,11 +404,22 @@ func TestAggregatedAPIServer(t *testing.T) {
|
|||||||
t.Error("expected non-empty resource version for fischer list")
|
t.Error("expected non-empty resource version for fischer list")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = wardleClient.Flunders(metav1.NamespaceSystem).Create(ctx, &wardlev1alpha1.Flunder{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "badname",
|
||||||
|
},
|
||||||
|
}, metav1.CreateOptions{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expect flunder:badname not admitted")
|
||||||
|
}
|
||||||
_, err = wardleClient.Flunders(metav1.NamespaceSystem).Create(ctx, &wardlev1alpha1.Flunder{
|
_, err = wardleClient.Flunders(metav1.NamespaceSystem).Create(ctx, &wardlev1alpha1.Flunder{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "panda",
|
Name: "panda",
|
||||||
},
|
},
|
||||||
}, metav1.CreateOptions{})
|
}, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
flunderList, err := wardleClient.Flunders(metav1.NamespaceSystem).List(ctx, metav1.ListOptions{})
|
flunderList, err := wardleClient.Flunders(metav1.NamespaceSystem).List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
Loading…
Reference in New Issue
Block a user