mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
Roll back pkg client code for Velero server
Signed-off-by: danfengl <danfengl@vmware.com>
This commit is contained in:
@@ -27,20 +27,15 @@ import (
|
|||||||
"github.com/vmware-tanzu/velero/pkg/buildinfo"
|
"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
|
// Config returns a *rest.Config, using either the kubeconfig (if specified) or an in-cluster
|
||||||
// configuration.
|
// configuration.
|
||||||
func Config(kubeconfig, kubecontext, baseName string, qps float32, burst int) (*rest.Config, error) {
|
func Config(kubeconfig, kubecontext, baseName string, qps float32, burst int) (*rest.Config, error) {
|
||||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||||
loadingRules.ExplicitPath = kubeconfig
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error finding Kubernetes API server config in --kubeconfig, $KUBECONFIG, or in-cluster configuration")
|
return nil, errors.Wrap(err, "error finding Kubernetes API server config in --kubeconfig, $KUBECONFIG, or in-cluster configuration")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,11 +77,10 @@ type factory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory returns a Factory.
|
// NewFactory returns a Factory.
|
||||||
func NewFactory(baseName, kubecontext string, config VeleroConfig) Factory {
|
func NewFactory(baseName string, config VeleroConfig) Factory {
|
||||||
f := &factory{
|
f := &factory{
|
||||||
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
|
flags: pflag.NewFlagSet("", pflag.ContinueOnError),
|
||||||
baseName: baseName,
|
baseName: baseName,
|
||||||
kubecontext: kubecontext,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.namespace = os.Getenv("VELERO_NAMESPACE")
|
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.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.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
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +127,7 @@ func (f *factory) KubeClient() (kubernetes.Interface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kubeClient, err := kubernetes.NewForConfig(clientConfig)
|
kubeClient, err := kubernetes.NewForConfig(clientConfig)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ func TestFactory(t *testing.T) {
|
|||||||
|
|
||||||
// Env variable should set the namespace if no config or argument are used
|
// Env variable should set the namespace if no config or argument are used
|
||||||
os.Setenv("VELERO_NAMESPACE", "env-velero")
|
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())
|
assert.Equal(t, "env-velero", f.Namespace())
|
||||||
|
|
||||||
os.Unsetenv("VELERO_NAMESPACE")
|
os.Unsetenv("VELERO_NAMESPACE")
|
||||||
|
|
||||||
// Argument should change the namespace
|
// Argument should change the namespace
|
||||||
f = NewFactory("velero", "", make(map[string]interface{}))
|
f = NewFactory("velero", make(map[string]interface{}))
|
||||||
s := "flag-velero"
|
s := "flag-velero"
|
||||||
flags := new(pflag.FlagSet)
|
flags := new(pflag.FlagSet)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ func TestFactory(t *testing.T) {
|
|||||||
|
|
||||||
// An argument overrides the env variable if both are set.
|
// An argument overrides the env variable if both are set.
|
||||||
os.Setenv("VELERO_NAMESPACE", "env-velero")
|
os.Setenv("VELERO_NAMESPACE", "env-velero")
|
||||||
f = NewFactory("velero", "", make(map[string]interface{}))
|
f = NewFactory("velero", make(map[string]interface{}))
|
||||||
flags = new(pflag.FlagSet)
|
flags = new(pflag.FlagSet)
|
||||||
|
|
||||||
f.BindFlags(flags)
|
f.BindFlags(flags)
|
||||||
|
|||||||
@@ -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())
|
f.BindFlags(c.PersistentFlags())
|
||||||
|
|
||||||
// Bind features directly to the root command so it's available to all callers.
|
// Bind features directly to the root command so it's available to all callers.
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ func userPriorityConfigMap() (*corev1.ConfigMap, error) {
|
|||||||
return nil, errors.Wrap(err, "reading client config file")
|
return nil, errors.Wrap(err, "reading client config file")
|
||||||
}
|
}
|
||||||
|
|
||||||
fc := client.NewFactory("APIGroupVersionsRestore", "", cfg)
|
fc := client.NewFactory("APIGroupVersionsRestore", cfg)
|
||||||
|
|
||||||
kc, err := fc.KubeClient()
|
kc, err := fc.KubeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
25
test/e2e/pkg/client/auth_providers.go
Normal file
25
test/e2e/pkg/client/auth_providers.go
Normal 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"
|
||||||
|
)
|
||||||
70
test/e2e/pkg/client/client.go
Normal file
70
test/e2e/pkg/client/client.go
Normal 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)
|
||||||
|
}
|
||||||
51
test/e2e/pkg/client/client_test.go
Normal file
51
test/e2e/pkg/client/client_test.go
Normal 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
151
test/e2e/pkg/client/config.go
Normal file
151
test/e2e/pkg/client/config.go
Normal 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")
|
||||||
|
}
|
||||||
34
test/e2e/pkg/client/config_test.go
Normal file
34
test/e2e/pkg/client/config_test.go
Normal 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())
|
||||||
|
}
|
||||||
141
test/e2e/pkg/client/dynamic.go
Normal file
141
test/e2e/pkg/client/dynamic.go
Normal 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)
|
||||||
|
}
|
||||||
185
test/e2e/pkg/client/factory.go
Normal file
185
test/e2e/pkg/client/factory.go
Normal 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
|
||||||
|
}
|
||||||
61
test/e2e/pkg/client/factory_test.go
Normal file
61
test/e2e/pkg/client/factory_test.go
Normal 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")
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
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
|
// TestClient contains different API clients that are in use throughout
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ func RunKibishiiTests(veleroCfg VeleroConfig, backupName, restoreName, backupLoc
|
|||||||
// return errors.Wrapf(err, "Error get vSphere snapshot uploads")
|
// return errors.Wrapf(err, "Error get vSphere snapshot uploads")
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
|
// wait for a period to confirm no snapshots exist for the backup
|
||||||
|
time.Sleep(5 * time.Minute)
|
||||||
if strings.EqualFold(veleroFeatures, "EnableCSI") {
|
if strings.EqualFold(veleroFeatures, "EnableCSI") {
|
||||||
_, err = GetSnapshotCheckPoint(*veleroCfg.ClientToInstallVelero, veleroCfg, 0,
|
_, err = GetSnapshotCheckPoint(*veleroCfg.ClientToInstallVelero, veleroCfg, 0,
|
||||||
kibishiiNamespace, backupName, KibishiiPodNameList)
|
kibishiiNamespace, backupName, KibishiiPodNameList)
|
||||||
|
|||||||
Reference in New Issue
Block a user