Merge pull request #5905 from danfengliu/cherrypick-rollback-velero-client-factory-code

[Cherrypick 1.10] rollback velero client factory code
pull/6058/head v1.10.2
lyndon 2023-02-23 15:03:54 +08:00 committed by GitHub
commit 7416504e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 733 additions and 20 deletions

View File

@ -27,20 +27,15 @@ import (
"github.com/vmware-tanzu/velero/pkg/buildinfo"
)
func buildConfigFromFlags(context, kubeconfigPath string, precedence []string) (*rest.Config, error) {
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath, Precedence: precedence},
&clientcmd.ConfigOverrides{
CurrentContext: context,
}).ClientConfig()
}
// Config returns a *rest.Config, using either the kubeconfig (if specified) or an in-cluster
// configuration.
func Config(kubeconfig, kubecontext, baseName string, qps float32, burst int) (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeconfig
clientConfig, err := buildConfigFromFlags(kubecontext, kubeconfig, loadingRules.Precedence)
configOverrides := &clientcmd.ConfigOverrides{CurrentContext: kubecontext}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
clientConfig, err := kubeConfig.ClientConfig()
if err != nil {
return nil, errors.Wrap(err, "error finding Kubernetes API server config in --kubeconfig, $KUBECONFIG, or in-cluster configuration")
}

View File

@ -77,11 +77,10 @@ type factory struct {
}
// NewFactory returns a Factory.
func NewFactory(baseName, kubecontext string, config VeleroConfig) Factory {
func NewFactory(baseName string, config VeleroConfig) Factory {
f := &factory{
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
baseName: baseName,
kubecontext: kubecontext,
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
baseName: baseName,
}
f.namespace = os.Getenv("VELERO_NAMESPACE")
@ -97,7 +96,7 @@ func NewFactory(baseName, kubecontext string, config VeleroConfig) Factory {
f.flags.StringVar(&f.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use to talk to the Kubernetes apiserver. If unset, try the environment variable KUBECONFIG, as well as in-cluster configuration")
f.flags.StringVarP(&f.namespace, "namespace", "n", f.namespace, "The namespace in which Velero should operate")
//f.flags.StringVar(&f.kubecontext, "kubecontext", "", "The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context)")
f.flags.StringVar(&f.kubecontext, "kubecontext", "", "The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context)")
return f
}
@ -128,6 +127,7 @@ func (f *factory) KubeClient() (kubernetes.Interface, error) {
return nil, err
}
kubeClient, err := kubernetes.NewForConfig(clientConfig)
if err != nil {
return nil, errors.WithStack(err)
}

View File

@ -31,14 +31,14 @@ func TestFactory(t *testing.T) {
// Env variable should set the namespace if no config or argument are used
os.Setenv("VELERO_NAMESPACE", "env-velero")
f := NewFactory("velero", "", make(map[string]interface{}))
f := NewFactory("velero", make(map[string]interface{}))
assert.Equal(t, "env-velero", f.Namespace())
os.Unsetenv("VELERO_NAMESPACE")
// Argument should change the namespace
f = NewFactory("velero", "", make(map[string]interface{}))
f = NewFactory("velero", make(map[string]interface{}))
s := "flag-velero"
flags := new(pflag.FlagSet)
@ -50,7 +50,7 @@ func TestFactory(t *testing.T) {
// An argument overrides the env variable if both are set.
os.Setenv("VELERO_NAMESPACE", "env-velero")
f = NewFactory("velero", "", make(map[string]interface{}))
f = NewFactory("velero", make(map[string]interface{}))
flags = new(pflag.FlagSet)
f.BindFlags(flags)

View File

@ -91,7 +91,7 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre
},
}
f := client.NewFactory(name, "", config)
f := client.NewFactory(name, config)
f.BindFlags(c.PersistentFlags())
// Bind features directly to the root command so it's available to all callers.

View File

@ -214,7 +214,7 @@ func userPriorityConfigMap() (*corev1.ConfigMap, error) {
return nil, errors.Wrap(err, "reading client config file")
}
fc := client.NewFactory("APIGroupVersionsRestore", "", cfg)
fc := client.NewFactory("APIGroupVersionsRestore", cfg)
kc, err := fc.KubeClient()
if err != nil {

View File

@ -0,0 +1,25 @@
/*
Copyright 2017 the Velero contributors.
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 client
// Make sure we import the client-go auth provider plugins.
import (
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)

View File

@ -0,0 +1,70 @@
/*
Copyright 2017, 2019 the Velero contributors.
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 client
import (
"fmt"
"runtime"
"github.com/pkg/errors"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
)
func buildConfigFromFlags(context, kubeconfigPath string, precedence []string) (*rest.Config, error) {
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath, Precedence: precedence},
&clientcmd.ConfigOverrides{
CurrentContext: context,
}).ClientConfig()
}
// Config returns a *rest.Config, using either the kubeconfig (if specified) or an in-cluster
// configuration.
func Config(kubeconfig, kubecontext, baseName string, qps float32, burst int) (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeconfig
clientConfig, err := buildConfigFromFlags(kubecontext, kubeconfig, loadingRules.Precedence)
if err != nil {
return nil, errors.Wrap(err, "error finding Kubernetes API server config in --kubeconfig, $KUBECONFIG, or in-cluster configuration")
}
if qps > 0.0 {
clientConfig.QPS = qps
}
if burst > 0 {
clientConfig.Burst = burst
}
clientConfig.UserAgent = buildUserAgent(
baseName,
buildinfo.Version,
buildinfo.FormattedGitSHA(),
runtime.GOOS,
runtime.GOARCH,
)
return clientConfig, nil
}
// buildUserAgent builds a User-Agent string from given args.
func buildUserAgent(command, version, formattedSha, os, arch string) string {
return fmt.Sprintf(
"%s/%s (%s/%s) %s", command, version, os, arch, formattedSha)
}

View File

@ -0,0 +1,51 @@
/*
Copyright 2018 the Velero contributors.
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 client
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBuildUserAgent(t *testing.T) {
tests := []struct {
name string
command string
os string
arch string
gitSha string
version string
expected string
}{
{
name: "Test general interpolation in correct order",
command: "velero",
os: "darwin",
arch: "amd64",
gitSha: "abc123",
version: "v0.1.1",
expected: "velero/v0.1.1 (darwin/amd64) abc123",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resp := buildUserAgent(test.command, test.version, test.gitSha, test.os, test.arch)
assert.Equal(t, resp, test.expected)
})
}
}

View File

@ -0,0 +1,151 @@
/*
Copyright 2021 the Velero contributors.
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 client
import (
"encoding/json"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/pkg/errors"
)
const (
ConfigKeyNamespace = "namespace"
ConfigKeyFeatures = "features"
ConfigKeyCACert = "cacert"
ConfigKeyColorized = "colorized"
)
// VeleroConfig is a map of strings to interface{} for deserializing Velero client config options.
// The alias is a way to attach type-asserting convenience methods.
type VeleroConfig map[string]interface{}
// LoadConfig loads the Velero client configuration file and returns it as a VeleroConfig. If the
// file does not exist, an empty map is returned.
func LoadConfig() (VeleroConfig, error) {
fileName := configFileName()
_, err := os.Stat(fileName)
if os.IsNotExist(err) {
// If the file isn't there, just return an empty map
return VeleroConfig{}, nil
}
if err != nil {
// For any other Stat() error, return it
return nil, errors.WithStack(err)
}
configFile, err := os.Open(fileName)
if err != nil {
return nil, errors.WithStack(err)
}
defer configFile.Close()
var config VeleroConfig
if err := json.NewDecoder(configFile).Decode(&config); err != nil {
return nil, errors.WithStack(err)
}
return config, nil
}
// SaveConfig saves the passed in config map to the Velero client configuration file.
func SaveConfig(config VeleroConfig) error {
fileName := configFileName()
// Try to make the directory in case it doesn't exist
dir := filepath.Dir(fileName)
if err := os.MkdirAll(dir, 0700); err != nil {
return errors.WithStack(err)
}
configFile, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return errors.WithStack(err)
}
defer configFile.Close()
return json.NewEncoder(configFile).Encode(&config)
}
func (c VeleroConfig) Namespace() string {
val, ok := c[ConfigKeyNamespace]
if !ok {
return ""
}
ns, ok := val.(string)
if !ok {
return ""
}
return ns
}
func (c VeleroConfig) Features() []string {
val, ok := c[ConfigKeyFeatures]
if !ok {
return []string{}
}
features, ok := val.(string)
if !ok {
return []string{}
}
return strings.Split(features, ",")
}
func (c VeleroConfig) Colorized() bool {
val, ok := c[ConfigKeyColorized]
if !ok {
return true
}
valString, ok := val.(string)
if !ok {
return true
}
colorized, err := strconv.ParseBool(valString)
if err != nil {
return true
}
return colorized
}
func (c VeleroConfig) CACertFile() string {
val, ok := c[ConfigKeyCACert]
if !ok {
return ""
}
caCertFile, ok := val.(string)
if !ok {
return ""
}
return caCertFile
}
func configFileName() string {
return filepath.Join(os.Getenv("HOME"), ".config", "velero", "config.json")
}

View File

@ -0,0 +1,34 @@
/*
Copyright 2021 the Velero contributors.
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 client
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestVeleroConfig(t *testing.T) {
c := VeleroConfig{
"namespace": "foo",
"features": "feature1,feature2",
}
assert.Equal(t, "foo", c.Namespace())
assert.Equal(t, []string{"feature1", "feature2"}, c.Features())
assert.Equal(t, true, c.Colorized())
}

View File

@ -0,0 +1,141 @@
/*
Copyright 2017 the Velero contributors.
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 client
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
)
// DynamicFactory contains methods for retrieving dynamic clients for GroupVersionResources and
// GroupVersionKinds.
type DynamicFactory interface {
// ClientForGroupVersionResource returns a Dynamic client for the given group/version
// and resource for the given namespace.
ClientForGroupVersionResource(gv schema.GroupVersion, resource metav1.APIResource, namespace string) (Dynamic, error)
}
// dynamicFactory implements DynamicFactory.
type dynamicFactory struct {
dynamicClient dynamic.Interface
}
// NewDynamicFactory returns a new ClientPool-based dynamic factory.
func NewDynamicFactory(dynamicClient dynamic.Interface) DynamicFactory {
return &dynamicFactory{dynamicClient: dynamicClient}
}
func (f *dynamicFactory) ClientForGroupVersionResource(gv schema.GroupVersion, resource metav1.APIResource, namespace string) (Dynamic, error) {
return &dynamicResourceClient{
resourceClient: f.dynamicClient.Resource(gv.WithResource(resource.Name)).Namespace(namespace),
}, nil
}
// Creator creates an object.
type Creator interface {
// Create creates an object.
Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
}
// Lister lists objects.
type Lister interface {
// List lists all the objects of a given resource.
List(metav1.ListOptions) (*unstructured.UnstructuredList, error)
}
// Watcher watches objects.
type Watcher interface {
// Watch watches for changes to objects of a given resource.
Watch(metav1.ListOptions) (watch.Interface, error)
}
// Getter gets an object.
type Getter interface {
// Get fetches an object by name.
Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error)
}
// Patcher patches an object.
type Patcher interface {
//Patch patches the named object using the provided patch bytes, which are expected to be in JSON merge patch format. The patched object is returned.
Patch(name string, data []byte) (*unstructured.Unstructured, error)
}
// Deletor deletes an object.
type Deletor interface {
//Patch patches the named object using the provided patch bytes, which are expected to be in JSON merge patch format. The patched object is returned.
Delete(name string, opts metav1.DeleteOptions) error
}
// StatusUpdater updates status field of a object
type StatusUpdater interface {
UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error)
}
// Dynamic contains client methods that Velero needs for backing up and restoring resources.
type Dynamic interface {
Creator
Lister
Watcher
Getter
Patcher
Deletor
StatusUpdater
}
// dynamicResourceClient implements Dynamic.
type dynamicResourceClient struct {
resourceClient dynamic.ResourceInterface
}
var _ Dynamic = &dynamicResourceClient{}
func (d *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
return d.resourceClient.Create(context.TODO(), obj, metav1.CreateOptions{})
}
func (d *dynamicResourceClient) List(options metav1.ListOptions) (*unstructured.UnstructuredList, error) {
return d.resourceClient.List(context.TODO(), options)
}
func (d *dynamicResourceClient) Watch(options metav1.ListOptions) (watch.Interface, error) {
return d.resourceClient.Watch(context.TODO(), options)
}
func (d *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) {
return d.resourceClient.Get(context.TODO(), name, opts)
}
func (d *dynamicResourceClient) Patch(name string, data []byte) (*unstructured.Unstructured, error) {
return d.resourceClient.Patch(context.TODO(), name, types.MergePatchType, data, metav1.PatchOptions{})
}
func (d *dynamicResourceClient) Delete(name string, opts metav1.DeleteOptions) error {
return d.resourceClient.Delete(context.TODO(), name, opts)
}
func (d *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
return d.resourceClient.UpdateStatus(context.TODO(), obj, opts)
}

View File

@ -0,0 +1,185 @@
/*
Copyright 2017, 2019 the Velero contributors.
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 client
import (
"os"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
k8scheme "k8s.io/client-go/kubernetes/scheme"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
)
// Factory knows how to create a VeleroClient and Kubernetes client.
type Factory interface {
// BindFlags binds common flags (--kubeconfig, --namespace) to the passed-in FlagSet.
BindFlags(flags *pflag.FlagSet)
// Client returns a VeleroClient. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
Client() (clientset.Interface, error)
// KubeClient returns a Kubernetes client. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
KubeClient() (kubernetes.Interface, error)
// DynamicClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
DynamicClient() (dynamic.Interface, error)
// KubebuilderClient returns a client for the controller runtime framework. It adds Kubernetes and Velero
// types to its scheme. It uses the following priority to specify the cluster
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
KubebuilderClient() (kbclient.Client, error)
// SetBasename changes the basename for an already-constructed client.
// This is useful for generating clients that require a different user-agent string below the root `velero`
// command, such as the server subcommand.
SetBasename(string)
// SetClientQPS sets the Queries Per Second for a client.
SetClientQPS(float32)
// SetClientBurst sets the Burst for a client.
SetClientBurst(int)
// ClientConfig returns a rest.Config struct used for client-go clients.
ClientConfig() (*rest.Config, error)
// Namespace returns the namespace which the Factory will create clients for.
Namespace() string
}
type factory struct {
flags *pflag.FlagSet
kubeconfig string
kubecontext string
baseName string
namespace string
clientQPS float32
clientBurst int
}
// NewFactory returns a Factory.
func NewFactory(baseName, kubecontext string, config VeleroConfig) Factory {
f := &factory{
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
baseName: baseName,
kubecontext: kubecontext,
}
f.namespace = os.Getenv("VELERO_NAMESPACE")
if config.Namespace() != "" {
f.namespace = config.Namespace()
}
// We didn't get the namespace via env var or config file, so use the default.
// Command line flags will override when BindFlags is called.
if f.namespace == "" {
f.namespace = velerov1api.DefaultNamespace
}
f.flags.StringVar(&f.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use to talk to the Kubernetes apiserver. If unset, try the environment variable KUBECONFIG, as well as in-cluster configuration")
f.flags.StringVarP(&f.namespace, "namespace", "n", f.namespace, "The namespace in which Velero should operate")
//f.flags.StringVar(&f.kubecontext, "kubecontext", "", "The context to use to talk to the Kubernetes apiserver. If unset defaults to whatever your current-context is (kubectl config current-context)")
return f
}
func (f *factory) BindFlags(flags *pflag.FlagSet) {
flags.AddFlagSet(f.flags)
}
func (f *factory) ClientConfig() (*rest.Config, error) {
return Config(f.kubeconfig, f.kubecontext, f.baseName, f.clientQPS, f.clientBurst)
}
func (f *factory) Client() (clientset.Interface, error) {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
veleroClient, err := clientset.NewForConfig(clientConfig)
if err != nil {
return nil, errors.WithStack(err)
}
return veleroClient, nil
}
func (f *factory) KubeClient() (kubernetes.Interface, error) {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
kubeClient, err := kubernetes.NewForConfig(clientConfig)
if err != nil {
return nil, errors.WithStack(err)
}
return kubeClient, nil
}
func (f *factory) DynamicClient() (dynamic.Interface, error) {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
dynamicClient, err := dynamic.NewForConfig(clientConfig)
if err != nil {
return nil, errors.WithStack(err)
}
return dynamicClient, nil
}
func (f *factory) KubebuilderClient() (kbclient.Client, error) {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
scheme := runtime.NewScheme()
velerov1api.AddToScheme(scheme)
k8scheme.AddToScheme(scheme)
apiextv1beta1.AddToScheme(scheme)
apiextv1.AddToScheme(scheme)
kubebuilderClient, err := kbclient.New(clientConfig, kbclient.Options{
Scheme: scheme,
})
if err != nil {
return nil, err
}
return kubebuilderClient, nil
}
func (f *factory) SetBasename(name string) {
f.baseName = name
}
func (f *factory) SetClientQPS(qps float32) {
f.clientQPS = qps
}
func (f *factory) SetClientBurst(burst int) {
f.clientBurst = burst
}
func (f *factory) Namespace() string {
return f.namespace
}

View File

@ -0,0 +1,61 @@
/*
Copyright 2019 the Velero contributors.
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 client
import (
"os"
"testing"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
)
// TestFactory tests the client.Factory interface.
func TestFactory(t *testing.T) {
// Velero client configuration is currently omitted due to requiring a
// test filesystem in pkg/test. This causes an import cycle as pkg/test
// uses pkg/client's interfaces to implement fakes
// Env variable should set the namespace if no config or argument are used
os.Setenv("VELERO_NAMESPACE", "env-velero")
f := NewFactory("velero", "", make(map[string]interface{}))
assert.Equal(t, "env-velero", f.Namespace())
os.Unsetenv("VELERO_NAMESPACE")
// Argument should change the namespace
f = NewFactory("velero", "", make(map[string]interface{}))
s := "flag-velero"
flags := new(pflag.FlagSet)
f.BindFlags(flags)
flags.Parse([]string{"--namespace", s})
assert.Equal(t, s, f.Namespace())
// An argument overrides the env variable if both are set.
os.Setenv("VELERO_NAMESPACE", "env-velero")
f = NewFactory("velero", "", make(map[string]interface{}))
flags = new(pflag.FlagSet)
f.BindFlags(flags)
flags.Parse([]string{"--namespace", s})
assert.Equal(t, s, f.Namespace())
os.Unsetenv("VELERO_NAMESPACE")
}

View File

@ -20,7 +20,7 @@ import (
"k8s.io/client-go/kubernetes"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/test/e2e/pkg/client"
)
// TestClient contains different API clients that are in use throughout