Moved flag and globalflag
Moved all flag code from `staging/src/k8s.io/apiserver/pkg/util/[flag|globalflag]` to `component-base/cli/[flag|globalflag]` except for the term function because of unwanted dependencies. Kubernetes-commit: 7744f908306e5131be5a94815ac76a7cba6454f2
This commit is contained in:
parent
c8e68b89a1
commit
c7299d9da0
|
|
@ -25,18 +25,18 @@ import (
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/apiserver/pkg/server/resourceconfig"
|
"k8s.io/apiserver/pkg/server/resourceconfig"
|
||||||
serverstore "k8s.io/apiserver/pkg/server/storage"
|
serverstore "k8s.io/apiserver/pkg/server/storage"
|
||||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIEnablementOptions contains the options for which resources to turn on and off.
|
// APIEnablementOptions contains the options for which resources to turn on and off.
|
||||||
// Given small aggregated API servers, this option isn't required for "normal" API servers
|
// Given small aggregated API servers, this option isn't required for "normal" API servers
|
||||||
type APIEnablementOptions struct {
|
type APIEnablementOptions struct {
|
||||||
RuntimeConfig utilflag.ConfigurationMap
|
RuntimeConfig cliflag.ConfigurationMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIEnablementOptions() *APIEnablementOptions {
|
func NewAPIEnablementOptions() *APIEnablementOptions {
|
||||||
return &APIEnablementOptions{
|
return &APIEnablementOptions{
|
||||||
RuntimeConfig: make(utilflag.ConfigurationMap),
|
RuntimeConfig: make(cliflag.ConfigurationMap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeGroupRegisty struct{}
|
type fakeGroupRegisty struct{}
|
||||||
|
|
@ -42,28 +42,28 @@ func TestAPIEnablementOptionsValidate(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "test when invalid key with only api/all=false",
|
name: "test when invalid key with only api/all=false",
|
||||||
testOptions: &APIEnablementOptions{
|
testOptions: &APIEnablementOptions{
|
||||||
RuntimeConfig: utilflag.ConfigurationMap{"api/all": "false"},
|
RuntimeConfig: cliflag.ConfigurationMap{"api/all": "false"},
|
||||||
},
|
},
|
||||||
expectErr: "invalid key with only api/all=false",
|
expectErr: "invalid key with only api/all=false",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test when ConfigurationMap key is invalid",
|
name: "test when ConfigurationMap key is invalid",
|
||||||
testOptions: &APIEnablementOptions{
|
testOptions: &APIEnablementOptions{
|
||||||
RuntimeConfig: utilflag.ConfigurationMap{"apiall": "false"},
|
RuntimeConfig: cliflag.ConfigurationMap{"apiall": "false"},
|
||||||
},
|
},
|
||||||
expectErr: "runtime-config invalid key",
|
expectErr: "runtime-config invalid key",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test when unknown api groups",
|
name: "test when unknown api groups",
|
||||||
testOptions: &APIEnablementOptions{
|
testOptions: &APIEnablementOptions{
|
||||||
RuntimeConfig: utilflag.ConfigurationMap{"api/v1": "true"},
|
RuntimeConfig: cliflag.ConfigurationMap{"api/v1": "true"},
|
||||||
},
|
},
|
||||||
expectErr: "unknown api groups",
|
expectErr: "unknown api groups",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test when valid api groups",
|
name: "test when valid api groups",
|
||||||
testOptions: &APIEnablementOptions{
|
testOptions: &APIEnablementOptions{
|
||||||
RuntimeConfig: utilflag.ConfigurationMap{"apiregistration.k8s.io/v1beta1": "true"},
|
RuntimeConfig: cliflag.ConfigurationMap{"apiregistration.k8s.io/v1beta1": "true"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ import (
|
||||||
|
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SecureServingOptions struct {
|
type SecureServingOptions struct {
|
||||||
|
|
@ -54,7 +54,7 @@ type SecureServingOptions struct {
|
||||||
// ServerCert is the TLS cert info for serving secure traffic
|
// ServerCert is the TLS cert info for serving secure traffic
|
||||||
ServerCert GeneratableKeyCert
|
ServerCert GeneratableKeyCert
|
||||||
// SNICertKeys are named CertKeys for serving secure traffic with SNI support.
|
// SNICertKeys are named CertKeys for serving secure traffic with SNI support.
|
||||||
SNICertKeys []utilflag.NamedCertKey
|
SNICertKeys []cliflag.NamedCertKey
|
||||||
// CipherSuites is the list of allowed cipher suites for the server.
|
// CipherSuites is the list of allowed cipher suites for the server.
|
||||||
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
|
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
|
||||||
CipherSuites []string
|
CipherSuites []string
|
||||||
|
|
@ -165,18 +165,18 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
|
||||||
fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile,
|
fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile,
|
||||||
"File containing the default x509 private key matching --tls-cert-file.")
|
"File containing the default x509 private key matching --tls-cert-file.")
|
||||||
|
|
||||||
tlsCipherPossibleValues := utilflag.TLSCipherPossibleValues()
|
tlsCipherPossibleValues := cliflag.TLSCipherPossibleValues()
|
||||||
fs.StringSliceVar(&s.CipherSuites, "tls-cipher-suites", s.CipherSuites,
|
fs.StringSliceVar(&s.CipherSuites, "tls-cipher-suites", s.CipherSuites,
|
||||||
"Comma-separated list of cipher suites for the server. "+
|
"Comma-separated list of cipher suites for the server. "+
|
||||||
"If omitted, the default Go cipher suites will be use. "+
|
"If omitted, the default Go cipher suites will be use. "+
|
||||||
"Possible values: "+strings.Join(tlsCipherPossibleValues, ","))
|
"Possible values: "+strings.Join(tlsCipherPossibleValues, ","))
|
||||||
|
|
||||||
tlsPossibleVersions := utilflag.TLSPossibleVersions()
|
tlsPossibleVersions := cliflag.TLSPossibleVersions()
|
||||||
fs.StringVar(&s.MinTLSVersion, "tls-min-version", s.MinTLSVersion,
|
fs.StringVar(&s.MinTLSVersion, "tls-min-version", s.MinTLSVersion,
|
||||||
"Minimum TLS version supported. "+
|
"Minimum TLS version supported. "+
|
||||||
"Possible values: "+strings.Join(tlsPossibleVersions, ", "))
|
"Possible values: "+strings.Join(tlsPossibleVersions, ", "))
|
||||||
|
|
||||||
fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+
|
fs.Var(cliflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+
|
||||||
"A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+
|
"A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+
|
||||||
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+
|
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+
|
||||||
"segments. If no domain patterns are provided, the names of the certificate are "+
|
"segments. If no domain patterns are provided, the names of the certificate are "+
|
||||||
|
|
@ -234,7 +234,7 @@ func (s *SecureServingOptions) ApplyTo(config **server.SecureServingInfo) error
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.CipherSuites) != 0 {
|
if len(s.CipherSuites) != 0 {
|
||||||
cipherSuites, err := utilflag.TLSCipherSuites(s.CipherSuites)
|
cipherSuites, err := cliflag.TLSCipherSuites(s.CipherSuites)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +242,7 @@ func (s *SecureServingOptions) ApplyTo(config **server.SecureServingInfo) error
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
c.MinTLSVersion, err = utilflag.TLSVersion(s.MinTLSVersion)
|
c.MinTLSVersion, err = cliflag.TLSVersion(s.MinTLSVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,9 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/version"
|
"k8s.io/apimachinery/pkg/version"
|
||||||
"k8s.io/apiserver/pkg/server"
|
"k8s.io/apiserver/pkg/server"
|
||||||
. "k8s.io/apiserver/pkg/server"
|
. "k8s.io/apiserver/pkg/server"
|
||||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
|
||||||
"k8s.io/client-go/discovery"
|
"k8s.io/client-go/discovery"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setUp(t *testing.T) Config {
|
func setUp(t *testing.T) Config {
|
||||||
|
|
@ -417,7 +417,7 @@ func TestServerRunWithSNI(t *testing.T) {
|
||||||
caCerts := []*x509.Certificate{ca}
|
caCerts := []*x509.Certificate{ca}
|
||||||
|
|
||||||
// create SNI certs
|
// create SNI certs
|
||||||
var namedCertKeys []utilflag.NamedCertKey
|
var namedCertKeys []cliflag.NamedCertKey
|
||||||
serverSig, err := certFileSignature(serverCertBundleFile, serverKeyFile)
|
serverSig, err := certFileSignature(serverCertBundleFile, serverKeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get server cert signature: %v", err)
|
t.Fatalf("failed to get server cert signature: %v", err)
|
||||||
|
|
@ -434,7 +434,7 @@ func TestServerRunWithSNI(t *testing.T) {
|
||||||
t.Fatalf("failed to create SNI cert %d: %v", j, err)
|
t.Fatalf("failed to create SNI cert %d: %v", j, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
namedCertKeys = append(namedCertKeys, utilflag.NamedCertKey{
|
namedCertKeys = append(namedCertKeys, cliflag.NamedCertKey{
|
||||||
KeyFile: keyFile,
|
KeyFile: keyFile,
|
||||||
CertFile: certBundleFile,
|
CertFile: certBundleFile,
|
||||||
Names: c.explicitNames,
|
Names: c.explicitNames,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
serverstore "k8s.io/apiserver/pkg/server/storage"
|
serverstore "k8s.io/apiserver/pkg/server/storage"
|
||||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ func MergeResourceEncodingConfigs(
|
||||||
// not registered in group, then it will fail.
|
// not registered in group, then it will fail.
|
||||||
func MergeAPIResourceConfigs(
|
func MergeAPIResourceConfigs(
|
||||||
defaultAPIResourceConfig *serverstore.ResourceConfig,
|
defaultAPIResourceConfig *serverstore.ResourceConfig,
|
||||||
resourceConfigOverrides utilflag.ConfigurationMap,
|
resourceConfigOverrides cliflag.ConfigurationMap,
|
||||||
registry GroupVersionRegistry,
|
registry GroupVersionRegistry,
|
||||||
) (*serverstore.ResourceConfig, error) {
|
) (*serverstore.ResourceConfig, error) {
|
||||||
resourceConfig := defaultAPIResourceConfig
|
resourceConfig := defaultAPIResourceConfig
|
||||||
|
|
@ -134,7 +134,7 @@ func MergeAPIResourceConfigs(
|
||||||
return resourceConfig, nil
|
return resourceConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRuntimeConfigValue(overrides utilflag.ConfigurationMap, apiKey string, defaultValue bool) (bool, error) {
|
func getRuntimeConfigValue(overrides cliflag.ConfigurationMap, apiKey string, defaultValue bool) (bool, error) {
|
||||||
flagValue, ok := overrides[apiKey]
|
flagValue, ok := overrides[apiKey]
|
||||||
if ok {
|
if ok {
|
||||||
if flagValue == "" {
|
if flagValue == "" {
|
||||||
|
|
@ -150,7 +150,7 @@ func getRuntimeConfigValue(overrides utilflag.ConfigurationMap, apiKey string, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseGroups takes in resourceConfig and returns parsed groups.
|
// ParseGroups takes in resourceConfig and returns parsed groups.
|
||||||
func ParseGroups(resourceConfig utilflag.ConfigurationMap) ([]string, error) {
|
func ParseGroups(resourceConfig cliflag.ConfigurationMap) ([]string, error) {
|
||||||
groups := []string{}
|
groups := []string{}
|
||||||
for key := range resourceConfig {
|
for key := range resourceConfig {
|
||||||
if key == "api/all" {
|
if key == "api/all" {
|
||||||
|
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ciphers maps strings into tls package cipher constants in
|
|
||||||
// https://golang.org/pkg/crypto/tls/#pkg-constants
|
|
||||||
var ciphers = map[string]uint16{
|
|
||||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
|
||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
|
||||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
||||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
||||||
}
|
|
||||||
|
|
||||||
func TLSCipherPossibleValues() []string {
|
|
||||||
cipherKeys := sets.NewString()
|
|
||||||
for key := range ciphers {
|
|
||||||
cipherKeys.Insert(key)
|
|
||||||
}
|
|
||||||
return cipherKeys.List()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TLSCipherSuites(cipherNames []string) ([]uint16, error) {
|
|
||||||
if len(cipherNames) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
ciphersIntSlice := make([]uint16, 0)
|
|
||||||
for _, cipher := range cipherNames {
|
|
||||||
intValue, ok := ciphers[cipher]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Cipher suite %s not supported or doesn't exist", cipher)
|
|
||||||
}
|
|
||||||
ciphersIntSlice = append(ciphersIntSlice, intValue)
|
|
||||||
}
|
|
||||||
return ciphersIntSlice, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var versions = map[string]uint16{
|
|
||||||
"VersionTLS10": tls.VersionTLS10,
|
|
||||||
"VersionTLS11": tls.VersionTLS11,
|
|
||||||
"VersionTLS12": tls.VersionTLS12,
|
|
||||||
}
|
|
||||||
|
|
||||||
func TLSPossibleVersions() []string {
|
|
||||||
versionsKeys := sets.NewString()
|
|
||||||
for key := range versions {
|
|
||||||
versionsKeys.Insert(key)
|
|
||||||
}
|
|
||||||
return versionsKeys.List()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TLSVersion(versionName string) (uint16, error) {
|
|
||||||
if len(versionName) == 0 {
|
|
||||||
return DefaultTLSVersion(), nil
|
|
||||||
}
|
|
||||||
if version, ok := versions[versionName]; ok {
|
|
||||||
return version, nil
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("unknown tls version %q", versionName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultTLSVersion() uint16 {
|
|
||||||
// Can't use SSLv3 because of POODLE and BEAST
|
|
||||||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
|
||||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
|
||||||
return tls.VersionTLS12
|
|
||||||
}
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"go/importer"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStrToUInt16(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
flag []string
|
|
||||||
expected []uint16
|
|
||||||
expected_error bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Happy case
|
|
||||||
flag: []string{"TLS_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"},
|
|
||||||
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
|
|
||||||
expected_error: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// One flag only
|
|
||||||
flag: []string{"TLS_RSA_WITH_RC4_128_SHA"},
|
|
||||||
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA},
|
|
||||||
expected_error: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Empty flag
|
|
||||||
flag: []string{},
|
|
||||||
expected: nil,
|
|
||||||
expected_error: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Duplicated flag
|
|
||||||
flag: []string{"TLS_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_RC4_128_SHA"},
|
|
||||||
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_RC4_128_SHA},
|
|
||||||
expected_error: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Invalid flag
|
|
||||||
flag: []string{"foo"},
|
|
||||||
expected: nil,
|
|
||||||
expected_error: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
uIntFlags, err := TLSCipherSuites(test.flag)
|
|
||||||
if reflect.DeepEqual(uIntFlags, test.expected) == false {
|
|
||||||
t.Errorf("%d: expected %+v, got %+v", i, test.expected, uIntFlags)
|
|
||||||
}
|
|
||||||
if test.expected_error && err == nil {
|
|
||||||
t.Errorf("%d: expecting error, got %+v", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConstantMaps(t *testing.T) {
|
|
||||||
pkg, err := importer.Default().Import("crypto/tls")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("error: %s\n", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
discoveredVersions := map[string]bool{}
|
|
||||||
discoveredCiphers := map[string]bool{}
|
|
||||||
for _, declName := range pkg.Scope().Names() {
|
|
||||||
if strings.HasPrefix(declName, "VersionTLS") {
|
|
||||||
discoveredVersions[declName] = true
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(declName, "TLS_RSA_") || strings.HasPrefix(declName, "TLS_ECDHE_") {
|
|
||||||
discoveredCiphers[declName] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range discoveredCiphers {
|
|
||||||
if _, ok := ciphers[k]; !ok {
|
|
||||||
t.Errorf("discovered cipher tls.%s not in ciphers map", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for k := range ciphers {
|
|
||||||
if _, ok := discoveredCiphers[k]; !ok {
|
|
||||||
t.Errorf("ciphers map has %s not in tls package", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for k := range discoveredVersions {
|
|
||||||
if _, ok := versions[k]; !ok {
|
|
||||||
t.Errorf("discovered version tls.%s not in version map", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for k := range versions {
|
|
||||||
if _, ok := discoveredVersions[k]; !ok {
|
|
||||||
t.Errorf("versions map has %s not in tls package", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ColonSeparatedMultimapStringString supports setting a map[string][]string from an encoding
|
|
||||||
// that separates keys from values with ':' and separates key-value pairs with ','.
|
|
||||||
// A key can be repeated multiple times, in which case the values are appended to a
|
|
||||||
// slice of strings associated with that key. Items in the list associated with a given
|
|
||||||
// key will appear in the order provided.
|
|
||||||
// For example: `a:hello,b:again,c:world,b:beautiful` results in `{"a": ["hello"], "b": ["again", "beautiful"], "c": ["world"]}`
|
|
||||||
// The first call to Set will clear the map before adding entries; subsequent calls will simply append to the map.
|
|
||||||
// This makes it possible to override default values with a command-line option rather than appending to defaults,
|
|
||||||
// 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"]}`
|
|
||||||
type ColonSeparatedMultimapStringString struct {
|
|
||||||
Multimap *map[string][]string
|
|
||||||
initialized bool // set to true after the first Set call
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColonSeparatedMultimapStringString takes a pointer to a map[string][]string and returns the
|
|
||||||
// ColonSeparatedMultimapStringString flag parsing shim for that map.
|
|
||||||
func NewColonSeparatedMultimapStringString(m *map[string][]string) *ColonSeparatedMultimapStringString {
|
|
||||||
return &ColonSeparatedMultimapStringString{Multimap: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements github.com/spf13/pflag.Value
|
|
||||||
func (m *ColonSeparatedMultimapStringString) Set(value string) error {
|
|
||||||
if m.Multimap == nil {
|
|
||||||
return fmt.Errorf("no target (nil pointer to map[string][]string)")
|
|
||||||
}
|
|
||||||
if !m.initialized || *m.Multimap == nil {
|
|
||||||
// clear default values, or allocate if no existing map
|
|
||||||
*m.Multimap = make(map[string][]string)
|
|
||||||
m.initialized = true
|
|
||||||
}
|
|
||||||
for _, pair := range strings.Split(value, ",") {
|
|
||||||
if len(pair) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
kv := strings.SplitN(pair, ":", 2)
|
|
||||||
if len(kv) != 2 {
|
|
||||||
return fmt.Errorf("malformed pair, expect string:string")
|
|
||||||
}
|
|
||||||
k := strings.TrimSpace(kv[0])
|
|
||||||
v := strings.TrimSpace(kv[1])
|
|
||||||
(*m.Multimap)[k] = append((*m.Multimap)[k], v)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements github.com/spf13/pflag.Value
|
|
||||||
func (m *ColonSeparatedMultimapStringString) String() string {
|
|
||||||
type kv struct {
|
|
||||||
k string
|
|
||||||
v string
|
|
||||||
}
|
|
||||||
kvs := make([]kv, 0, len(*m.Multimap))
|
|
||||||
for k, vs := range *m.Multimap {
|
|
||||||
for i := range vs {
|
|
||||||
kvs = append(kvs, kv{k: k, v: vs[i]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stable sort by keys, order of values should be preserved
|
|
||||||
sort.SliceStable(kvs, func(i, j int) bool {
|
|
||||||
return kvs[i].k < kvs[j].k
|
|
||||||
})
|
|
||||||
pairs := make([]string, 0, len(kvs))
|
|
||||||
for i := range kvs {
|
|
||||||
pairs = append(pairs, fmt.Sprintf("%s:%s", kvs[i].k, kvs[i].v))
|
|
||||||
}
|
|
||||||
return strings.Join(pairs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements github.com/spf13/pflag.Value
|
|
||||||
func (m *ColonSeparatedMultimapStringString) Type() string {
|
|
||||||
return "colonSeparatedMultimapStringString"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty implements OmitEmpty
|
|
||||||
func (m *ColonSeparatedMultimapStringString) Empty() bool {
|
|
||||||
return len(*m.Multimap) == 0
|
|
||||||
}
|
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringColonSeparatedMultimapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string][]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
m *ColonSeparatedMultimapStringString
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{"nil", NewColonSeparatedMultimapStringString(&nilMap), ""},
|
|
||||||
{"empty", NewColonSeparatedMultimapStringString(&map[string][]string{}), ""},
|
|
||||||
{"empty key", NewColonSeparatedMultimapStringString(
|
|
||||||
&map[string][]string{
|
|
||||||
"": {"foo"},
|
|
||||||
}),
|
|
||||||
":foo"},
|
|
||||||
{"one key", NewColonSeparatedMultimapStringString(
|
|
||||||
&map[string][]string{
|
|
||||||
"one": {"foo"},
|
|
||||||
}),
|
|
||||||
"one:foo"},
|
|
||||||
{"two keys", NewColonSeparatedMultimapStringString(
|
|
||||||
&map[string][]string{
|
|
||||||
"one": {"foo"},
|
|
||||||
"two": {"bar"},
|
|
||||||
}),
|
|
||||||
"one:foo,two:bar"},
|
|
||||||
{"two keys, multiple items in one key", NewColonSeparatedMultimapStringString(
|
|
||||||
&map[string][]string{
|
|
||||||
"one": {"foo", "baz"},
|
|
||||||
"two": {"bar"},
|
|
||||||
}),
|
|
||||||
"one:foo,one:baz,two:bar"},
|
|
||||||
{"three keys, multiple items in one key", NewColonSeparatedMultimapStringString(
|
|
||||||
&map[string][]string{
|
|
||||||
"a": {"hello"},
|
|
||||||
"b": {"again", "beautiful"},
|
|
||||||
"c": {"world"},
|
|
||||||
}),
|
|
||||||
"a:hello,b:again,b:beautiful,c:world"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
str := c.m.String()
|
|
||||||
if c.expect != str {
|
|
||||||
t.Fatalf("expect %q but got %q", c.expect, str)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetColonSeparatedMultimapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string][]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
vals []string
|
|
||||||
start *ColonSeparatedMultimapStringString
|
|
||||||
expect *ColonSeparatedMultimapStringString
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
// we initialize the map with a default key that should be cleared by Set
|
|
||||||
{"clears defaults", []string{""},
|
|
||||||
NewColonSeparatedMultimapStringString(&map[string][]string{"default": {}}),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{}}, ""},
|
|
||||||
// make sure we still allocate for "initialized" multimaps where Multimap was initially set to a nil map
|
|
||||||
{"allocates map if currently nil", []string{""},
|
|
||||||
&ColonSeparatedMultimapStringString{initialized: true, Multimap: &nilMap},
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{},
|
|
||||||
}, ""},
|
|
||||||
// for most cases, we just reuse nilMap, which should be allocated by Set, and is reset before each test case
|
|
||||||
{"empty", []string{""},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{}}, ""},
|
|
||||||
{"empty key", []string{":foo"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"": {"foo"},
|
|
||||||
}}, ""},
|
|
||||||
{"one key", []string{"one:foo"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"one": {"foo"},
|
|
||||||
}}, ""},
|
|
||||||
{"two keys", []string{"one:foo,two:bar"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"one": {"foo"},
|
|
||||||
"two": {"bar"},
|
|
||||||
}}, ""},
|
|
||||||
{"two keys with space", []string{"one:foo, two:bar"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"one": {"foo"},
|
|
||||||
"two": {"bar"},
|
|
||||||
}}, ""},
|
|
||||||
{"two keys, multiple items in one key", []string{"one: foo, two:bar, one:baz"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"one": {"foo", "baz"},
|
|
||||||
"two": {"bar"},
|
|
||||||
}}, ""},
|
|
||||||
{"three keys, multiple items in one key", []string{"a:hello,b:again,c:world,b:beautiful"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"a": {"hello"},
|
|
||||||
"b": {"again", "beautiful"},
|
|
||||||
"c": {"world"},
|
|
||||||
}}, ""},
|
|
||||||
{"three keys, multiple items in one key, multiple Set invocations", []string{"a:hello,b:again", "c:world", "b:beautiful"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
&ColonSeparatedMultimapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Multimap: &map[string][]string{
|
|
||||||
"a": {"hello"},
|
|
||||||
"b": {"again", "beautiful"},
|
|
||||||
"c": {"world"},
|
|
||||||
}}, ""},
|
|
||||||
{"missing value", []string{"a"},
|
|
||||||
NewColonSeparatedMultimapStringString(&nilMap),
|
|
||||||
nil,
|
|
||||||
"malformed pair, expect string:string"},
|
|
||||||
{"no target", []string{"a:foo"},
|
|
||||||
NewColonSeparatedMultimapStringString(nil),
|
|
||||||
nil,
|
|
||||||
"no target (nil pointer to map[string][]string)"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
nilMap = nil
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
for _, val := range c.vals {
|
|
||||||
err = c.start.Set(val)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.err != "" {
|
|
||||||
if err == nil || err.Error() != c.err {
|
|
||||||
t.Fatalf("expect error %s but got %v", c.err, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(c.expect, c.start) {
|
|
||||||
t.Fatalf("expect %#v but got %#v", c.expect, c.start)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoundTripColonSeparatedMultimapStringString(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
vals []string
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{"empty", []string{""}, ""},
|
|
||||||
{"empty key", []string{":foo"}, ":foo"},
|
|
||||||
{"one key", []string{"one:foo"}, "one:foo"},
|
|
||||||
{"two keys", []string{"one:foo,two:bar"}, "one:foo,two:bar"},
|
|
||||||
{"two keys, multiple items in one key", []string{"one:foo, two:bar, one:baz"}, "one:foo,one:baz,two:bar"},
|
|
||||||
{"three keys, multiple items in one key", []string{"a:hello,b:again,c:world,b:beautiful"}, "a:hello,b:again,b:beautiful,c:world"},
|
|
||||||
{"three keys, multiple items in one key, multiple Set invocations", []string{"a:hello,b:again", "c:world", "b:beautiful"}, "a:hello,b:again,b:beautiful,c:world"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
m := NewColonSeparatedMultimapStringString(&map[string][]string{})
|
|
||||||
for _, val := range c.vals {
|
|
||||||
if err := m.Set(val); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
str := m.String()
|
|
||||||
if c.expect != str {
|
|
||||||
t.Fatalf("expect %q but got %q", c.expect, str)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyColonSeparatedMultimapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string][]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
val *ColonSeparatedMultimapStringString
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{"nil", NewColonSeparatedMultimapStringString(&nilMap), true},
|
|
||||||
{"empty", NewColonSeparatedMultimapStringString(&map[string][]string{}), true},
|
|
||||||
{"populated", NewColonSeparatedMultimapStringString(&map[string][]string{"foo": {}}), false},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
result := c.val.Empty()
|
|
||||||
if result != c.expect {
|
|
||||||
t.Fatalf("expect %t but got %t", c.expect, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2014 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigurationMap map[string]string
|
|
||||||
|
|
||||||
func (m *ConfigurationMap) String() string {
|
|
||||||
pairs := []string{}
|
|
||||||
for k, v := range *m {
|
|
||||||
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
|
|
||||||
}
|
|
||||||
sort.Strings(pairs)
|
|
||||||
return strings.Join(pairs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ConfigurationMap) Set(value string) error {
|
|
||||||
for _, s := range strings.Split(value, ",") {
|
|
||||||
if len(s) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
arr := strings.SplitN(s, "=", 2)
|
|
||||||
if len(arr) == 2 {
|
|
||||||
(*m)[strings.TrimSpace(arr[0])] = strings.TrimSpace(arr[1])
|
|
||||||
} else {
|
|
||||||
(*m)[strings.TrimSpace(arr[0])] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ConfigurationMap) Type() string {
|
|
||||||
return "mapStringString"
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2014 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
goflag "flag"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"k8s.io/klog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WordSepNormalizeFunc changes all flags that contain "_" separators
|
|
||||||
func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
||||||
if strings.Contains(name, "_") {
|
|
||||||
return pflag.NormalizedName(strings.Replace(name, "_", "-", -1))
|
|
||||||
}
|
|
||||||
return pflag.NormalizedName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WarnWordSepNormalizeFunc changes and warns for flags that contain "_" separators
|
|
||||||
func WarnWordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
|
||||||
if strings.Contains(name, "_") {
|
|
||||||
nname := strings.Replace(name, "_", "-", -1)
|
|
||||||
klog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", name, nname)
|
|
||||||
|
|
||||||
return pflag.NormalizedName(nname)
|
|
||||||
}
|
|
||||||
return pflag.NormalizedName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitFlags normalizes, parses, then logs the command line flags
|
|
||||||
func InitFlags() {
|
|
||||||
pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc)
|
|
||||||
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
|
||||||
pflag.Parse()
|
|
||||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
klog.V(2).Infof("FLAG: --%s=%q", flag.Name, flag.Value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LangleSeparatedMapStringString can be set from the command line with the format `--flag "string<string"`.
|
|
||||||
// Multiple comma-separated key-value pairs in a single invocation are supported. For example: `--flag "a<foo,b<bar"`.
|
|
||||||
// Multiple flag invocations are supported. For example: `--flag "a<foo" --flag "b<foo"`.
|
|
||||||
type LangleSeparatedMapStringString struct {
|
|
||||||
Map *map[string]string
|
|
||||||
initialized bool // set to true after first Set call
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLangleSeparatedMapStringString takes a pointer to a map[string]string and returns the
|
|
||||||
// LangleSeparatedMapStringString flag parsing shim for that map
|
|
||||||
func NewLangleSeparatedMapStringString(m *map[string]string) *LangleSeparatedMapStringString {
|
|
||||||
return &LangleSeparatedMapStringString{Map: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements github.com/spf13/pflag.Value
|
|
||||||
func (m *LangleSeparatedMapStringString) String() string {
|
|
||||||
pairs := []string{}
|
|
||||||
for k, v := range *m.Map {
|
|
||||||
pairs = append(pairs, fmt.Sprintf("%s<%s", k, v))
|
|
||||||
}
|
|
||||||
sort.Strings(pairs)
|
|
||||||
return strings.Join(pairs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements github.com/spf13/pflag.Value
|
|
||||||
func (m *LangleSeparatedMapStringString) Set(value string) error {
|
|
||||||
if m.Map == nil {
|
|
||||||
return fmt.Errorf("no target (nil pointer to map[string]string)")
|
|
||||||
}
|
|
||||||
if !m.initialized || *m.Map == nil {
|
|
||||||
// clear default values, or allocate if no existing map
|
|
||||||
*m.Map = make(map[string]string)
|
|
||||||
m.initialized = true
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(value, ",") {
|
|
||||||
if len(s) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
arr := strings.SplitN(s, "<", 2)
|
|
||||||
if len(arr) != 2 {
|
|
||||||
return fmt.Errorf("malformed pair, expect string<string")
|
|
||||||
}
|
|
||||||
k := strings.TrimSpace(arr[0])
|
|
||||||
v := strings.TrimSpace(arr[1])
|
|
||||||
(*m.Map)[k] = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements github.com/spf13/pflag.Value
|
|
||||||
func (*LangleSeparatedMapStringString) Type() string {
|
|
||||||
return "mapStringString"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty implements OmitEmpty
|
|
||||||
func (m *LangleSeparatedMapStringString) Empty() bool {
|
|
||||||
return len(*m.Map) == 0
|
|
||||||
}
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringLangleSeparatedMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
m *LangleSeparatedMapStringString
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{"nil", NewLangleSeparatedMapStringString(&nilMap), ""},
|
|
||||||
{"empty", NewLangleSeparatedMapStringString(&map[string]string{}), ""},
|
|
||||||
{"one key", NewLangleSeparatedMapStringString(&map[string]string{"one": "foo"}), "one<foo"},
|
|
||||||
{"two keys", NewLangleSeparatedMapStringString(&map[string]string{"one": "foo", "two": "bar"}), "one<foo,two<bar"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
str := c.m.String()
|
|
||||||
if c.expect != str {
|
|
||||||
t.Fatalf("expect %q but got %q", c.expect, str)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetLangleSeparatedMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
vals []string
|
|
||||||
start *LangleSeparatedMapStringString
|
|
||||||
expect *LangleSeparatedMapStringString
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
// we initialize the map with a default key that should be cleared by Set
|
|
||||||
{"clears defaults", []string{""},
|
|
||||||
NewLangleSeparatedMapStringString(&map[string]string{"default": ""}),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
}, ""},
|
|
||||||
// make sure we still allocate for "initialized" maps where Map was initially set to a nil map
|
|
||||||
{"allocates map if currently nil", []string{""},
|
|
||||||
&LangleSeparatedMapStringString{initialized: true, Map: &nilMap},
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
}, ""},
|
|
||||||
// for most cases, we just reuse nilMap, which should be allocated by Set, and is reset before each test case
|
|
||||||
{"empty", []string{""},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
}, ""},
|
|
||||||
{"one key", []string{"one<foo"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo"},
|
|
||||||
}, ""},
|
|
||||||
{"two keys", []string{"one<foo,two<bar"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
}, ""},
|
|
||||||
{"two keys, multiple Set invocations", []string{"one<foo", "two<bar"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
}, ""},
|
|
||||||
{"two keys with space", []string{"one<foo, two<bar"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
}, ""},
|
|
||||||
{"empty key", []string{"<foo"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
&LangleSeparatedMapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"": "foo"},
|
|
||||||
}, ""},
|
|
||||||
{"missing value", []string{"one"},
|
|
||||||
NewLangleSeparatedMapStringString(&nilMap),
|
|
||||||
nil,
|
|
||||||
"malformed pair, expect string<string"},
|
|
||||||
{"no target", []string{"a:foo"},
|
|
||||||
NewLangleSeparatedMapStringString(nil),
|
|
||||||
nil,
|
|
||||||
"no target (nil pointer to map[string]string)"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
nilMap = nil
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
for _, val := range c.vals {
|
|
||||||
err = c.start.Set(val)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.err != "" {
|
|
||||||
if err == nil || err.Error() != c.err {
|
|
||||||
t.Fatalf("expect error %s but got %v", c.err, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(c.expect, c.start) {
|
|
||||||
t.Fatalf("expect %#v but got %#v", c.expect, c.start)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyLangleSeparatedMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
val *LangleSeparatedMapStringString
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{"nil", NewLangleSeparatedMapStringString(&nilMap), true},
|
|
||||||
{"empty", NewLangleSeparatedMapStringString(&map[string]string{}), true},
|
|
||||||
{"populated", NewLangleSeparatedMapStringString(&map[string]string{"foo": ""}), false},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
result := c.val.Empty()
|
|
||||||
if result != c.expect {
|
|
||||||
t.Fatalf("expect %t but got %t", c.expect, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MapStringBool can be set from the command line with the format `--flag "string=bool"`.
|
|
||||||
// Multiple comma-separated key-value pairs in a single invocation are supported. For example: `--flag "a=true,b=false"`.
|
|
||||||
// Multiple flag invocations are supported. For example: `--flag "a=true" --flag "b=false"`.
|
|
||||||
type MapStringBool struct {
|
|
||||||
Map *map[string]bool
|
|
||||||
initialized bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMapStringBool takes a pointer to a map[string]string and returns the
|
|
||||||
// MapStringBool flag parsing shim for that map
|
|
||||||
func NewMapStringBool(m *map[string]bool) *MapStringBool {
|
|
||||||
return &MapStringBool{Map: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements github.com/spf13/pflag.Value
|
|
||||||
func (m *MapStringBool) String() string {
|
|
||||||
if m == nil || m.Map == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
pairs := []string{}
|
|
||||||
for k, v := range *m.Map {
|
|
||||||
pairs = append(pairs, fmt.Sprintf("%s=%t", k, v))
|
|
||||||
}
|
|
||||||
sort.Strings(pairs)
|
|
||||||
return strings.Join(pairs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements github.com/spf13/pflag.Value
|
|
||||||
func (m *MapStringBool) Set(value string) error {
|
|
||||||
if m.Map == nil {
|
|
||||||
return fmt.Errorf("no target (nil pointer to map[string]bool)")
|
|
||||||
}
|
|
||||||
if !m.initialized || *m.Map == nil {
|
|
||||||
// clear default values, or allocate if no existing map
|
|
||||||
*m.Map = make(map[string]bool)
|
|
||||||
m.initialized = true
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(value, ",") {
|
|
||||||
if len(s) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
arr := strings.SplitN(s, "=", 2)
|
|
||||||
if len(arr) != 2 {
|
|
||||||
return fmt.Errorf("malformed pair, expect string=bool")
|
|
||||||
}
|
|
||||||
k := strings.TrimSpace(arr[0])
|
|
||||||
v := strings.TrimSpace(arr[1])
|
|
||||||
boolValue, err := strconv.ParseBool(v)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid value of %s: %s, err: %v", k, v, err)
|
|
||||||
}
|
|
||||||
(*m.Map)[k] = boolValue
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements github.com/spf13/pflag.Value
|
|
||||||
func (*MapStringBool) Type() string {
|
|
||||||
return "mapStringBool"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty implements OmitEmpty
|
|
||||||
func (m *MapStringBool) Empty() bool {
|
|
||||||
return len(*m.Map) == 0
|
|
||||||
}
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringMapStringBool(t *testing.T) {
|
|
||||||
var nilMap map[string]bool
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
m *MapStringBool
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{"nil", NewMapStringBool(&nilMap), ""},
|
|
||||||
{"empty", NewMapStringBool(&map[string]bool{}), ""},
|
|
||||||
{"one key", NewMapStringBool(&map[string]bool{"one": true}), "one=true"},
|
|
||||||
{"two keys", NewMapStringBool(&map[string]bool{"one": true, "two": false}), "one=true,two=false"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
str := c.m.String()
|
|
||||||
if c.expect != str {
|
|
||||||
t.Fatalf("expect %q but got %q", c.expect, str)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapStringBool(t *testing.T) {
|
|
||||||
var nilMap map[string]bool
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
vals []string
|
|
||||||
start *MapStringBool
|
|
||||||
expect *MapStringBool
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
// we initialize the map with a default key that should be cleared by Set
|
|
||||||
{"clears defaults", []string{""},
|
|
||||||
NewMapStringBool(&map[string]bool{"default": true}),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{},
|
|
||||||
}, ""},
|
|
||||||
// make sure we still allocate for "initialized" maps where Map was initially set to a nil map
|
|
||||||
{"allocates map if currently nil", []string{""},
|
|
||||||
&MapStringBool{initialized: true, Map: &nilMap},
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{},
|
|
||||||
}, ""},
|
|
||||||
// for most cases, we just reuse nilMap, which should be allocated by Set, and is reset before each test case
|
|
||||||
{"empty", []string{""},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{},
|
|
||||||
}, ""},
|
|
||||||
{"one key", []string{"one=true"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{"one": true},
|
|
||||||
}, ""},
|
|
||||||
{"two keys", []string{"one=true,two=false"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{"one": true, "two": false},
|
|
||||||
}, ""},
|
|
||||||
{"two keys, multiple Set invocations", []string{"one=true", "two=false"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{"one": true, "two": false},
|
|
||||||
}, ""},
|
|
||||||
{"two keys with space", []string{"one=true, two=false"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{"one": true, "two": false},
|
|
||||||
}, ""},
|
|
||||||
{"empty key", []string{"=true"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
&MapStringBool{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]bool{"": true},
|
|
||||||
}, ""},
|
|
||||||
{"missing value", []string{"one"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
nil,
|
|
||||||
"malformed pair, expect string=bool"},
|
|
||||||
{"non-boolean value", []string{"one=foo"},
|
|
||||||
NewMapStringBool(&nilMap),
|
|
||||||
nil,
|
|
||||||
`invalid value of one: foo, err: strconv.ParseBool: parsing "foo": invalid syntax`},
|
|
||||||
{"no target", []string{"one=true"},
|
|
||||||
NewMapStringBool(nil),
|
|
||||||
nil,
|
|
||||||
"no target (nil pointer to map[string]bool)"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
nilMap = nil
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
for _, val := range c.vals {
|
|
||||||
err = c.start.Set(val)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.err != "" {
|
|
||||||
if err == nil || err.Error() != c.err {
|
|
||||||
t.Fatalf("expect error %s but got %v", c.err, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(c.expect, c.start) {
|
|
||||||
t.Fatalf("expect %#v but got %#v", c.expect, c.start)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyMapStringBool(t *testing.T) {
|
|
||||||
var nilMap map[string]bool
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
val *MapStringBool
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{"nil", NewMapStringBool(&nilMap), true},
|
|
||||||
{"empty", NewMapStringBool(&map[string]bool{}), true},
|
|
||||||
{"populated", NewMapStringBool(&map[string]bool{"foo": true}), false},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
result := c.val.Empty()
|
|
||||||
if result != c.expect {
|
|
||||||
t.Fatalf("expect %t but got %t", c.expect, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MapStringString can be set from the command line with the format `--flag "string=string"`.
|
|
||||||
// Multiple flag invocations are supported. For example: `--flag "a=foo" --flag "b=bar"`. If this is desired
|
|
||||||
// to be the only type invocation `NoSplit` should be set to true.
|
|
||||||
// Multiple comma-separated key-value pairs in a single invocation are supported if `NoSplit`
|
|
||||||
// is set to false. For example: `--flag "a=foo,b=bar"`.
|
|
||||||
type MapStringString struct {
|
|
||||||
Map *map[string]string
|
|
||||||
initialized bool
|
|
||||||
NoSplit bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMapStringString takes a pointer to a map[string]string and returns the
|
|
||||||
// MapStringString flag parsing shim for that map
|
|
||||||
func NewMapStringString(m *map[string]string) *MapStringString {
|
|
||||||
return &MapStringString{Map: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMapStringString takes a pointer to a map[string]string and sets `NoSplit`
|
|
||||||
// value to `true` and returns the MapStringString flag parsing shim for that map
|
|
||||||
func NewMapStringStringNoSplit(m *map[string]string) *MapStringString {
|
|
||||||
return &MapStringString{
|
|
||||||
Map: m,
|
|
||||||
NoSplit: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements github.com/spf13/pflag.Value
|
|
||||||
func (m *MapStringString) String() string {
|
|
||||||
if m == nil || m.Map == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
pairs := []string{}
|
|
||||||
for k, v := range *m.Map {
|
|
||||||
pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
|
|
||||||
}
|
|
||||||
sort.Strings(pairs)
|
|
||||||
return strings.Join(pairs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set implements github.com/spf13/pflag.Value
|
|
||||||
func (m *MapStringString) Set(value string) error {
|
|
||||||
if m.Map == nil {
|
|
||||||
return fmt.Errorf("no target (nil pointer to map[string]string)")
|
|
||||||
}
|
|
||||||
if !m.initialized || *m.Map == nil {
|
|
||||||
// clear default values, or allocate if no existing map
|
|
||||||
*m.Map = make(map[string]string)
|
|
||||||
m.initialized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// account for comma-separated key-value pairs in a single invocation
|
|
||||||
if !m.NoSplit {
|
|
||||||
for _, s := range strings.Split(value, ",") {
|
|
||||||
if len(s) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
arr := strings.SplitN(s, "=", 2)
|
|
||||||
if len(arr) != 2 {
|
|
||||||
return fmt.Errorf("malformed pair, expect string=string")
|
|
||||||
}
|
|
||||||
k := strings.TrimSpace(arr[0])
|
|
||||||
v := strings.TrimSpace(arr[1])
|
|
||||||
(*m.Map)[k] = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// account for only one key-value pair in a single invocation
|
|
||||||
arr := strings.SplitN(value, "=", 2)
|
|
||||||
if len(arr) != 2 {
|
|
||||||
return fmt.Errorf("malformed pair, expect string=string")
|
|
||||||
}
|
|
||||||
k := strings.TrimSpace(arr[0])
|
|
||||||
v := strings.TrimSpace(arr[1])
|
|
||||||
(*m.Map)[k] = v
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type implements github.com/spf13/pflag.Value
|
|
||||||
func (*MapStringString) Type() string {
|
|
||||||
return "mapStringString"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty implements OmitEmpty
|
|
||||||
func (m *MapStringString) Empty() bool {
|
|
||||||
return len(*m.Map) == 0
|
|
||||||
}
|
|
||||||
|
|
@ -1,181 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStringMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
m *MapStringString
|
|
||||||
expect string
|
|
||||||
}{
|
|
||||||
{"nil", NewMapStringString(&nilMap), ""},
|
|
||||||
{"empty", NewMapStringString(&map[string]string{}), ""},
|
|
||||||
{"one key", NewMapStringString(&map[string]string{"one": "foo"}), "one=foo"},
|
|
||||||
{"two keys", NewMapStringString(&map[string]string{"one": "foo", "two": "bar"}), "one=foo,two=bar"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
str := c.m.String()
|
|
||||||
if c.expect != str {
|
|
||||||
t.Fatalf("expect %q but got %q", c.expect, str)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
vals []string
|
|
||||||
start *MapStringString
|
|
||||||
expect *MapStringString
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
// we initialize the map with a default key that should be cleared by Set
|
|
||||||
{"clears defaults", []string{""},
|
|
||||||
NewMapStringString(&map[string]string{"default": ""}),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
// make sure we still allocate for "initialized" maps where Map was initially set to a nil map
|
|
||||||
{"allocates map if currently nil", []string{""},
|
|
||||||
&MapStringString{initialized: true, Map: &nilMap},
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
// for most cases, we just reuse nilMap, which should be allocated by Set, and is reset before each test case
|
|
||||||
{"empty", []string{""},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"one key", []string{"one=foo"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo"},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"two keys", []string{"one=foo,two=bar"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"one key, multi flag invocation only", []string{"one=foo,bar"},
|
|
||||||
NewMapStringStringNoSplit(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo,bar"},
|
|
||||||
NoSplit: true,
|
|
||||||
}, ""},
|
|
||||||
{"two keys, multi flag invocation only", []string{"one=foo,bar", "two=foo,bar"},
|
|
||||||
NewMapStringStringNoSplit(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo,bar", "two": "foo,bar"},
|
|
||||||
NoSplit: true,
|
|
||||||
}, ""},
|
|
||||||
{"two keys, multiple Set invocations", []string{"one=foo", "two=bar"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"two keys with space", []string{"one=foo, two=bar"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"one": "foo", "two": "bar"},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"empty key", []string{"=foo"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
&MapStringString{
|
|
||||||
initialized: true,
|
|
||||||
Map: &map[string]string{"": "foo"},
|
|
||||||
NoSplit: false,
|
|
||||||
}, ""},
|
|
||||||
{"missing value", []string{"one"},
|
|
||||||
NewMapStringString(&nilMap),
|
|
||||||
nil,
|
|
||||||
"malformed pair, expect string=string"},
|
|
||||||
{"no target", []string{"a:foo"},
|
|
||||||
NewMapStringString(nil),
|
|
||||||
nil,
|
|
||||||
"no target (nil pointer to map[string]string)"},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
nilMap = nil
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
for _, val := range c.vals {
|
|
||||||
err = c.start.Set(val)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.err != "" {
|
|
||||||
if err == nil || err.Error() != c.err {
|
|
||||||
t.Fatalf("expect error %s but got %v", c.err, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(c.expect, c.start) {
|
|
||||||
t.Fatalf("expect %#v but got %#v", c.expect, c.start)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyMapStringString(t *testing.T) {
|
|
||||||
var nilMap map[string]string
|
|
||||||
cases := []struct {
|
|
||||||
desc string
|
|
||||||
val *MapStringString
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{"nil", NewMapStringString(&nilMap), true},
|
|
||||||
{"empty", NewMapStringString(&map[string]string{}), true},
|
|
||||||
{"populated", NewMapStringString(&map[string]string{"foo": ""}), false},
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.desc, func(t *testing.T) {
|
|
||||||
result := c.val.Empty()
|
|
||||||
if result != c.expect {
|
|
||||||
t.Fatalf("expect %t but got %t", c.expect, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2016 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NamedCertKey is a flag value parsing "certfile,keyfile" and "certfile,keyfile:name,name,name".
|
|
||||||
type NamedCertKey struct {
|
|
||||||
Names []string
|
|
||||||
CertFile, KeyFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ flag.Value = &NamedCertKey{}
|
|
||||||
|
|
||||||
func (nkc *NamedCertKey) String() string {
|
|
||||||
s := nkc.CertFile + "," + nkc.KeyFile
|
|
||||||
if len(nkc.Names) > 0 {
|
|
||||||
s = s + ":" + strings.Join(nkc.Names, ",")
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nkc *NamedCertKey) Set(value string) error {
|
|
||||||
cs := strings.SplitN(value, ":", 2)
|
|
||||||
var keycert string
|
|
||||||
if len(cs) == 2 {
|
|
||||||
var names string
|
|
||||||
keycert, names = strings.TrimSpace(cs[0]), strings.TrimSpace(cs[1])
|
|
||||||
if names == "" {
|
|
||||||
return errors.New("empty names list is not allowed")
|
|
||||||
}
|
|
||||||
nkc.Names = nil
|
|
||||||
for _, name := range strings.Split(names, ",") {
|
|
||||||
nkc.Names = append(nkc.Names, strings.TrimSpace(name))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nkc.Names = nil
|
|
||||||
keycert = strings.TrimSpace(cs[0])
|
|
||||||
}
|
|
||||||
cs = strings.Split(keycert, ",")
|
|
||||||
if len(cs) != 2 {
|
|
||||||
return errors.New("expected comma separated certificate and key file paths")
|
|
||||||
}
|
|
||||||
nkc.CertFile = strings.TrimSpace(cs[0])
|
|
||||||
nkc.KeyFile = strings.TrimSpace(cs[1])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*NamedCertKey) Type() string {
|
|
||||||
return "namedCertKey"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamedCertKeyArray is a flag value parsing NamedCertKeys, each passed with its own
|
|
||||||
// flag instance (in contrast to comma separated slices).
|
|
||||||
type NamedCertKeyArray struct {
|
|
||||||
value *[]NamedCertKey
|
|
||||||
changed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ flag.Value = &NamedCertKey{}
|
|
||||||
|
|
||||||
// NewNamedKeyCertArray creates a new NamedCertKeyArray with the internal value
|
|
||||||
// pointing to p.
|
|
||||||
func NewNamedCertKeyArray(p *[]NamedCertKey) *NamedCertKeyArray {
|
|
||||||
return &NamedCertKeyArray{
|
|
||||||
value: p,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *NamedCertKeyArray) Set(val string) error {
|
|
||||||
nkc := NamedCertKey{}
|
|
||||||
err := nkc.Set(val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !a.changed {
|
|
||||||
*a.value = []NamedCertKey{nkc}
|
|
||||||
a.changed = true
|
|
||||||
} else {
|
|
||||||
*a.value = append(*a.value, nkc)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *NamedCertKeyArray) Type() string {
|
|
||||||
return "namedCertKey"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *NamedCertKeyArray) String() string {
|
|
||||||
nkcs := make([]string, 0, len(*a.value))
|
|
||||||
for i := range *a.value {
|
|
||||||
nkcs = append(nkcs, (*a.value)[i].String())
|
|
||||||
}
|
|
||||||
return "[" + strings.Join(nkcs, ";") + "]"
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2016 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNamedCertKeyArrayFlag(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
args []string
|
|
||||||
def []NamedCertKey
|
|
||||||
expected []NamedCertKey
|
|
||||||
parseError string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
args: []string{},
|
|
||||||
expected: nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key"},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{" foo.crt , foo.key "},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key:abc"},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
Names: []string{"abc"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key: abc "},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
Names: []string{"abc"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key:"},
|
|
||||||
parseError: "empty names list is not allowed",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{""},
|
|
||||||
parseError: "expected comma separated certificate and key file paths",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{" "},
|
|
||||||
parseError: "expected comma separated certificate and key file paths",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"a,b,c"},
|
|
||||||
parseError: "expected comma separated certificate and key file paths",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key:abc,def,ghi"},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
Names: []string{"abc", "def", "ghi"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key:*.*.*"},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
Names: []string{"*.*.*"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: []string{"foo.crt,foo.key", "bar.crt,bar.key"},
|
|
||||||
expected: []NamedCertKey{{
|
|
||||||
KeyFile: "foo.key",
|
|
||||||
CertFile: "foo.crt",
|
|
||||||
}, {
|
|
||||||
KeyFile: "bar.key",
|
|
||||||
CertFile: "bar.crt",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, test := range tests {
|
|
||||||
fs := pflag.NewFlagSet("testNamedCertKeyArray", pflag.ContinueOnError)
|
|
||||||
var nkcs []NamedCertKey
|
|
||||||
nkcs = append(nkcs, test.def...)
|
|
||||||
|
|
||||||
fs.Var(NewNamedCertKeyArray(&nkcs), "tls-sni-cert-key", "usage")
|
|
||||||
|
|
||||||
args := []string{}
|
|
||||||
for _, a := range test.args {
|
|
||||||
args = append(args, fmt.Sprintf("--tls-sni-cert-key=%s", a))
|
|
||||||
}
|
|
||||||
|
|
||||||
err := fs.Parse(args)
|
|
||||||
if test.parseError != "" {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("%d: expected error %q, got nil", i, test.parseError)
|
|
||||||
} else if !strings.Contains(err.Error(), test.parseError) {
|
|
||||||
t.Errorf("%d: expected error %q, got %q", i, test.parseError, err)
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
t.Errorf("%d: expected nil error, got %v", i, err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(nkcs, test.expected) {
|
|
||||||
t.Errorf("%d: expected %+v, got %+v", i, test.expected, nkcs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
goflag "flag"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NoOp implements goflag.Value and plfag.Value,
|
|
||||||
// but has a noop Set implementation
|
|
||||||
type NoOp struct{}
|
|
||||||
|
|
||||||
var _ goflag.Value = NoOp{}
|
|
||||||
var _ pflag.Value = NoOp{}
|
|
||||||
|
|
||||||
func (NoOp) String() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NoOp) Set(val string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (NoOp) Type() string {
|
|
||||||
return "NoOp"
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package flag
|
|
||||||
|
|
||||||
// OmitEmpty is an interface for flags to report whether their underlying value
|
|
||||||
// is "empty." If a flag implements OmitEmpty and returns true for a call to Empty(),
|
|
||||||
// it is assumed that flag may be omitted from the command line.
|
|
||||||
type OmitEmpty interface {
|
|
||||||
Empty() bool
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NamedFlagSets stores named flag sets in the order of calling FlagSet.
|
|
||||||
type NamedFlagSets struct {
|
|
||||||
// Order is an ordered list of flag set names.
|
|
||||||
Order []string
|
|
||||||
// FlagSets stores the flag sets by name.
|
|
||||||
FlagSets map[string]*pflag.FlagSet
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagSet returns the flag set with the given name and adds it to the
|
|
||||||
// ordered name list if it is not in there yet.
|
|
||||||
func (nfs *NamedFlagSets) FlagSet(name string) *pflag.FlagSet {
|
|
||||||
if nfs.FlagSets == nil {
|
|
||||||
nfs.FlagSets = map[string]*pflag.FlagSet{}
|
|
||||||
}
|
|
||||||
if _, ok := nfs.FlagSets[name]; !ok {
|
|
||||||
nfs.FlagSets[name] = pflag.NewFlagSet(name, pflag.ExitOnError)
|
|
||||||
nfs.Order = append(nfs.Order, name)
|
|
||||||
}
|
|
||||||
return nfs.FlagSets[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintSections prints the given names flag sets in sections, with the maximal given column number.
|
|
||||||
// If cols is zero, lines are not wrapped.
|
|
||||||
func PrintSections(w io.Writer, fss NamedFlagSets, cols int) {
|
|
||||||
for _, name := range fss.Order {
|
|
||||||
fs := fss.FlagSets[name]
|
|
||||||
if !fs.HasFlags() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
wideFS := pflag.NewFlagSet("", pflag.ExitOnError)
|
|
||||||
wideFS.AddFlagSet(fs)
|
|
||||||
|
|
||||||
var zzz string
|
|
||||||
if cols > 24 {
|
|
||||||
zzz = strings.Repeat("z", cols-24)
|
|
||||||
wideFS.Int(zzz, 0, strings.Repeat("z", cols-24))
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprintf(&buf, "\n%s flags:\n\n%s", strings.ToUpper(name[:1])+name[1:], wideFS.FlagUsagesWrapped(cols))
|
|
||||||
|
|
||||||
if cols > 24 {
|
|
||||||
i := strings.Index(buf.String(), zzz)
|
|
||||||
lines := strings.Split(buf.String()[:i], "\n")
|
|
||||||
fmt.Fprint(w, strings.Join(lines[:len(lines)-1], "\n"))
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(w, buf.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TerminalSize returns the current width and height of the user's terminal. If it isn't a terminal,
|
|
||||||
// nil is returned. On error, zero values are returned for width and height.
|
|
||||||
// Usually w must be the stdout of the process. Stderr won't work.
|
|
||||||
func TerminalSize(w io.Writer) (int, int, error) {
|
|
||||||
outFd, isTerminal := term.GetFdInfo(w)
|
|
||||||
if !isTerminal {
|
|
||||||
return 0, 0, fmt.Errorf("given writer is no terminal")
|
|
||||||
}
|
|
||||||
winsize, err := term.GetWinsize(outFd)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return int(winsize.Width), int(winsize.Height), nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2014 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 flag
|
|
||||||
|
|
||||||
// StringFlag is a string flag compatible with flags and pflags that keeps track of whether it had a value supplied or not.
|
|
||||||
type StringFlag struct {
|
|
||||||
// If Set has been invoked this value is true
|
|
||||||
provided bool
|
|
||||||
// The exact value provided on the flag
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStringFlag(defaultVal string) StringFlag {
|
|
||||||
return StringFlag{value: defaultVal}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *StringFlag) Default(value string) {
|
|
||||||
f.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StringFlag) String() string {
|
|
||||||
return f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StringFlag) Value() string {
|
|
||||||
return f.value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *StringFlag) Set(value string) error {
|
|
||||||
f.value = value
|
|
||||||
f.provided = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f StringFlag) Provided() bool {
|
|
||||||
return f.provided
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *StringFlag) Type() string {
|
|
||||||
return "string"
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2014 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 flag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tristate is a flag compatible with flags and pflags that
|
|
||||||
// keeps track of whether it had a value supplied or not.
|
|
||||||
type Tristate int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Unset Tristate = iota // 0
|
|
||||||
True
|
|
||||||
False
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f *Tristate) Default(value bool) {
|
|
||||||
*f = triFromBool(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Tristate) String() string {
|
|
||||||
b := boolFromTri(f)
|
|
||||||
return fmt.Sprintf("%t", b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Tristate) Value() bool {
|
|
||||||
b := boolFromTri(f)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Tristate) Set(value string) error {
|
|
||||||
boolVal, err := strconv.ParseBool(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*f = triFromBool(boolVal)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f Tristate) Provided() bool {
|
|
||||||
if f != Unset {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Tristate) Type() string {
|
|
||||||
return "tristate"
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolFromTri(t Tristate) bool {
|
|
||||||
if t == True {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func triFromBool(b bool) Tristate {
|
|
||||||
if b {
|
|
||||||
return True
|
|
||||||
} else {
|
|
||||||
return False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018 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 globalflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
|
|
||||||
"k8s.io/component-base/logs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddGlobalFlags explicitly registers flags that libraries (klog, verflag, etc.) register
|
|
||||||
// against the global flagsets from "flag" and "k8s.io/klog".
|
|
||||||
// We do this in order to prevent unwanted flags from leaking into the component's flagset.
|
|
||||||
func AddGlobalFlags(fs *pflag.FlagSet, name string) {
|
|
||||||
addGlogFlags(fs)
|
|
||||||
logs.AddFlags(fs)
|
|
||||||
|
|
||||||
fs.BoolP("help", "h", false, fmt.Sprintf("help for %s", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// addGlogFlags explicitly registers flags that klog libraries(k8s.io/klog) register.
|
|
||||||
func addGlogFlags(fs *pflag.FlagSet) {
|
|
||||||
// lookup flags of klog libraries in global flag set and re-register the values with our flagset
|
|
||||||
Register(fs, "logtostderr")
|
|
||||||
Register(fs, "alsologtostderr")
|
|
||||||
Register(fs, "v")
|
|
||||||
Register(fs, "skip_headers")
|
|
||||||
Register(fs, "stderrthreshold")
|
|
||||||
Register(fs, "vmodule")
|
|
||||||
Register(fs, "log_backtrace_at")
|
|
||||||
Register(fs, "log_dir")
|
|
||||||
Register(fs, "log_file")
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize replaces underscores with hyphens
|
|
||||||
// we should always use hyphens instead of underscores when registering component flags
|
|
||||||
func normalize(s string) string {
|
|
||||||
return strings.Replace(s, "_", "-", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register adds a flag to local that targets the Value associated with the Flag named globalName in flag.CommandLine.
|
|
||||||
func Register(local *pflag.FlagSet, globalName string) {
|
|
||||||
if f := flag.CommandLine.Lookup(globalName); f != nil {
|
|
||||||
pflagFlag := pflag.PFlagFromGoFlag(f)
|
|
||||||
pflagFlag.Name = normalize(pflagFlag.Name)
|
|
||||||
local.AddFlag(pflagFlag)
|
|
||||||
} else {
|
|
||||||
panic(fmt.Sprintf("failed to find flag in global flagset (flag): %s", globalName))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018 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 globalflag
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
|
|
||||||
apiserverflag "k8s.io/apiserver/pkg/util/flag"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAddGlobalFlags(t *testing.T) {
|
|
||||||
namedFlagSets := &apiserverflag.NamedFlagSets{}
|
|
||||||
nfs := namedFlagSets.FlagSet("global")
|
|
||||||
AddGlobalFlags(nfs, "test-cmd")
|
|
||||||
|
|
||||||
actualFlag := []string{}
|
|
||||||
nfs.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
actualFlag = append(actualFlag, flag.Name)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get all flags from flags.CommandLine, except flag `test.*`.
|
|
||||||
wantedFlag := []string{"help"}
|
|
||||||
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
|
||||||
pflag.VisitAll(func(flag *pflag.Flag) {
|
|
||||||
if !strings.Contains(flag.Name, "test.") {
|
|
||||||
wantedFlag = append(wantedFlag, normalize(flag.Name))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
sort.Strings(wantedFlag)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(wantedFlag, actualFlag) {
|
|
||||||
t.Errorf("[Default]: expected %+v, got %+v", wantedFlag, actualFlag)
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
expectedFlag []string
|
|
||||||
matchExpected bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Happy case
|
|
||||||
expectedFlag: []string{"alsologtostderr", "help", "log-backtrace-at", "log-dir", "log-file", "log-flush-frequency", "logtostderr", "skip-headers", "stderrthreshold", "v", "vmodule"},
|
|
||||||
matchExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Missing flag
|
|
||||||
expectedFlag: []string{"logtostderr", "log-dir"},
|
|
||||||
matchExpected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Empty flag
|
|
||||||
expectedFlag: []string{},
|
|
||||||
matchExpected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Invalid flag
|
|
||||||
expectedFlag: []string{"foo"},
|
|
||||||
matchExpected: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
if reflect.DeepEqual(test.expectedFlag, actualFlag) == test.matchExpected {
|
|
||||||
t.Errorf("[%d]: expected %+v, got %+v", i, test.expectedFlag, actualFlag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
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 term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TerminalSize returns the current width and height of the user's terminal. If it isn't a terminal,
|
||||||
|
// nil is returned. On error, zero values are returned for width and height.
|
||||||
|
// Usually w must be the stdout of the process. Stderr won't work.
|
||||||
|
func TerminalSize(w io.Writer) (int, int, error) {
|
||||||
|
outFd, isTerminal := term.GetFdInfo(w)
|
||||||
|
if !isTerminal {
|
||||||
|
return 0, 0, fmt.Errorf("given writer is no terminal")
|
||||||
|
}
|
||||||
|
winsize, err := term.GetWinsize(outFd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
return int(winsize.Width), int(winsize.Height), nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue