mirror of
https://github.com/TECHNOFAB11/zfs-localpv.git
synced 2025-12-12 06:20:11 +01:00
refact(csi): use common lib-csi imports (#263)
Signed-off-by: shubham <shubham.bajpai@mayadata.io>
This commit is contained in:
parent
48e6a19d7c
commit
2906d39d94
55 changed files with 271 additions and 611 deletions
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 btrfs
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
/*
|
||||
* We have to generate a new UUID for the cloned volumes with btrfs filesystem
|
||||
* otherwise system will mount the same volume if UUID is same. Here, since cloned
|
||||
* volume refers to the same block because of the way ZFS clone works, it will
|
||||
* also have the same UUID.
|
||||
*/
|
||||
|
||||
// GenerateUUID generates a new btrfs UUID for the given device
|
||||
func GenerateUUID(device string) error {
|
||||
// for mounting the cloned volume for btrfs, a new UUID has to be generated
|
||||
cmd := exec.Command("btrfstune", "-f", "-u", device)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
klog.Errorf("btrfs: uuid generate failed for device %s error: %s", device, string(out))
|
||||
return err
|
||||
}
|
||||
klog.Infof("btrfs: generated UUID for the device %s \n %v", device, string(out))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package bkpbuilder
|
||||
|
||||
import (
|
||||
"github.com/openebs/lib-csi/pkg/common/errors"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
)
|
||||
|
||||
// Builder is the builder object for ZFSBackup
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ package bkpbuilder
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
client "github.com/openebs/lib-csi/pkg/common/kubernetes/client"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
client "github.com/openebs/zfs-localpv/pkg/common/kubernetes/client"
|
||||
clientset "github.com/openebs/zfs-localpv/pkg/generated/clientset/internalclientset"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package restorebuilder
|
||||
|
||||
import (
|
||||
"github.com/openebs/lib-csi/pkg/common/errors"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
)
|
||||
|
||||
// Builder is the builder object for ZFSRestore
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ package restorebuilder
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
client "github.com/openebs/lib-csi/pkg/common/kubernetes/client"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
client "github.com/openebs/zfs-localpv/pkg/common/kubernetes/client"
|
||||
clientset "github.com/openebs/zfs-localpv/pkg/generated/clientset/internalclientset"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package snapbuilder
|
||||
|
||||
import (
|
||||
"github.com/openebs/lib-csi/pkg/common/errors"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
)
|
||||
|
||||
// Builder is the builder object for ZFSSnapshot
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ package snapbuilder
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
client "github.com/openebs/lib-csi/pkg/common/kubernetes/client"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
client "github.com/openebs/zfs-localpv/pkg/common/kubernetes/client"
|
||||
clientset "github.com/openebs/zfs-localpv/pkg/generated/clientset/internalclientset"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package volbuilder
|
||||
|
||||
import (
|
||||
"github.com/openebs/lib-csi/pkg/common/errors"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
)
|
||||
|
||||
// Builder is the builder object for ZFSVolume
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ package volbuilder
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
client "github.com/openebs/lib-csi/pkg/common/kubernetes/client"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
client "github.com/openebs/zfs-localpv/pkg/common/kubernetes/client"
|
||||
clientset "github.com/openebs/zfs-localpv/pkg/generated/clientset/internalclientset"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// ClientsetGetter abstracts fetching of kubernetes clientset
|
||||
type ClientsetGetter interface {
|
||||
Get() (*kubernetes.Clientset, error)
|
||||
}
|
||||
|
||||
// ClientsetStruct is used to export a kuberneter Clientset
|
||||
type ClientsetStruct struct{}
|
||||
|
||||
// Clientset returns a pointer to clientset struct
|
||||
func Clientset() *ClientsetStruct {
|
||||
return &ClientsetStruct{}
|
||||
}
|
||||
|
||||
// Get returns a new instance of kubernetes clientset
|
||||
func (c *ClientsetStruct) Get() (*kubernetes.Clientset, error) {
|
||||
config, err := Config().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get kubernetes clientset")
|
||||
}
|
||||
return kubernetes.NewForConfig(config)
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClientsetGet(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
iserr bool
|
||||
}{
|
||||
"101": {true},
|
||||
}
|
||||
|
||||
for name, mock := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, err := Clientset().Get()
|
||||
if !mock.iserr && err != nil {
|
||||
t.Fatalf("Test '%s' failed: expected no error: actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/openebs/zfs-localpv/pkg/common/env"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConfigGetter abstracts fetching of kubernetes client config
|
||||
type ConfigGetter interface {
|
||||
Get() (*rest.Config, error)
|
||||
Name() string
|
||||
}
|
||||
|
||||
// configFromENV is an implementation of ConfigGetter
|
||||
type configFromENV struct{}
|
||||
|
||||
// Name returns the name of this config getter instance
|
||||
func (c *configFromENV) Name() string {
|
||||
return "k8s-config-from-env"
|
||||
}
|
||||
|
||||
// Get returns kubernetes rest config based on kubernetes environment values
|
||||
func (c *configFromENV) Get() (*rest.Config, error) {
|
||||
k8sMaster := env.Get(env.KubeMaster)
|
||||
kubeConfig := env.Get(env.KubeConfig)
|
||||
|
||||
if len(strings.TrimSpace(k8sMaster)) == 0 && len(strings.TrimSpace(kubeConfig)) == 0 {
|
||||
return nil, errors.New("missing kubernetes master as well as kubeconfig: failed to get kubernetes client config")
|
||||
}
|
||||
|
||||
return clientcmd.BuildConfigFromFlags(k8sMaster, kubeConfig)
|
||||
}
|
||||
|
||||
// configFromREST is an implementation of ConfigGetter
|
||||
type configFromREST struct{}
|
||||
|
||||
// Name returns the name of this config getter instance
|
||||
func (c *configFromREST) Name() string {
|
||||
return "k8s-config-from-rest"
|
||||
}
|
||||
|
||||
// Get returns kubernetes rest config based on in-cluster config implementation
|
||||
func (c *configFromREST) Get() (*rest.Config, error) {
|
||||
return rest.InClusterConfig()
|
||||
}
|
||||
|
||||
// ConfigGetters holds a list of ConfigGetter instances
|
||||
//
|
||||
// NOTE:
|
||||
// This is an implementation of ConfigGetter
|
||||
type ConfigGetters []ConfigGetter
|
||||
|
||||
// Name returns the name of this config getter instance
|
||||
func (c ConfigGetters) Name() string {
|
||||
return "list-of-k8s-config-getter"
|
||||
}
|
||||
|
||||
// Get fetches the kubernetes client config that is used to make kubernetes API
|
||||
// calls. It makes use of its list of getter instances to fetch kubernetes
|
||||
// config.
|
||||
func (c ConfigGetters) Get() (config *rest.Config, err error) {
|
||||
var errs []error
|
||||
for _, g := range c {
|
||||
config, err = g.Get()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
errs = append(errs, errors.Wrapf(err, "failed to get kubernetes client config via %s", g.Name()))
|
||||
}
|
||||
// at this point; all getters have failed
|
||||
err = errors.Errorf("%+v", errs)
|
||||
err = errors.Wrap(err, "failed to get kubernetes client config")
|
||||
return
|
||||
}
|
||||
|
||||
// Config provides appropriate config getter instances that help in fetching
|
||||
// kubernetes client config to invoke kubernetes API calls
|
||||
func Config() ConfigGetter {
|
||||
return ConfigGetters{&configFromENV{}, &configFromREST{}}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/openebs/zfs-localpv/pkg/common/env"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// test if configFromENV implements ConfigGetter interface
|
||||
var _ ConfigGetter = &configFromENV{}
|
||||
|
||||
// test if configFromREST implements ConfigGetter interface
|
||||
var _ ConfigGetter = &configFromREST{}
|
||||
|
||||
// test if ConfigGetters implements ConfigGetter interface
|
||||
var _ ConfigGetter = ConfigGetters{}
|
||||
|
||||
func TestConfigFromENV(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
masterip string
|
||||
kubeconfig string
|
||||
iserr bool
|
||||
}{
|
||||
"101": {"", "", true},
|
||||
"102": {"", "/etc/config/kubeconfig", true},
|
||||
"103": {"0.0.0.0", "", false},
|
||||
"104": {"0.0.0.0", "/etc/config/config", true},
|
||||
}
|
||||
|
||||
// Sub tests is not used here as env key is set & unset to test. Since env
|
||||
// is a global setting, the tests should run serially
|
||||
for name, mock := range tests {
|
||||
masterip := os.Getenv(string(env.KubeMaster))
|
||||
defer os.Setenv(string(env.KubeMaster), masterip)
|
||||
|
||||
kubeconfig := os.Getenv(string(env.KubeConfig))
|
||||
defer os.Setenv(string(env.KubeConfig), kubeconfig)
|
||||
|
||||
err := os.Setenv(string(env.KubeMaster), mock.masterip)
|
||||
if err != nil {
|
||||
t.Fatalf("Test '%s' failed: %s", name, err)
|
||||
}
|
||||
err = os.Setenv(string(env.KubeConfig), mock.kubeconfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Test '%s' failed: %s", name, err)
|
||||
}
|
||||
|
||||
c := &configFromENV{}
|
||||
config, err := c.Get()
|
||||
|
||||
if !mock.iserr && config == nil {
|
||||
t.Fatalf("Test '%s' failed: expected config: actual nil config", name)
|
||||
}
|
||||
if !mock.iserr && err != nil {
|
||||
t.Fatalf("Test '%s' failed: expected no error: actual %s", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// ConfigMapGetter abstracts fetching of ConfigMap instance from kubernetes
|
||||
// cluster
|
||||
type ConfigMapGetter interface {
|
||||
Get(options metav1.GetOptions) (*corev1.ConfigMap, error)
|
||||
}
|
||||
|
||||
// Configmap is used to initialise a kubernetes Configmap struct
|
||||
type Configmap struct {
|
||||
namespace string // namespace where this configmap exists
|
||||
name string // name of this configmap
|
||||
}
|
||||
|
||||
// ConfigMap returns a new instance of configmap
|
||||
func ConfigMap(namespace, name string) *Configmap {
|
||||
return &Configmap{namespace: namespace, name: name}
|
||||
}
|
||||
|
||||
// Get returns configmap instance from kubernetes cluster
|
||||
func (c *Configmap) Get(options metav1.GetOptions) (cm *corev1.ConfigMap, err error) {
|
||||
if len(strings.TrimSpace(c.name)) == 0 {
|
||||
return nil, errors.Errorf("missing config map name: failed to get config map from namespace %s", c.namespace)
|
||||
}
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get config map %s %s", c.namespace, c.name)
|
||||
}
|
||||
return cs.CoreV1().ConfigMaps(c.namespace).Get(c.name, options)
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// test if configmap implements ConfigMapGetter interface
|
||||
var _ ConfigMapGetter = &Configmap{}
|
||||
|
||||
func TestConfigMapGet(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
namespace string
|
||||
name string
|
||||
options metav1.GetOptions
|
||||
iserr bool
|
||||
}{
|
||||
"101": {"", "", metav1.GetOptions{}, true},
|
||||
"102": {"default", "", metav1.GetOptions{}, true},
|
||||
"103": {"default", "myconf", metav1.GetOptions{}, true},
|
||||
}
|
||||
|
||||
for name, mock := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, err := ConfigMap(mock.namespace, mock.name).Get(mock.options)
|
||||
if !mock.iserr && err != nil {
|
||||
t.Fatalf("Test '%s' failed: expected no error: actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
k8sdynamic "k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// DynamicProvider abstracts providing kubernetes dynamic client interface
|
||||
type DynamicProvider interface {
|
||||
Provide() (k8sdynamic.Interface, error)
|
||||
}
|
||||
|
||||
//DynamicStruct is used to initialise a kuberenets dynamic interface
|
||||
type DynamicStruct struct{}
|
||||
|
||||
// Dynamic returns a new instance of dynamic
|
||||
func Dynamic() *DynamicStruct {
|
||||
return &DynamicStruct{}
|
||||
}
|
||||
|
||||
// Provide provides a kubernetes dynamic client capable of invoking operations
|
||||
// against kubernetes resources
|
||||
func (d *DynamicStruct) Provide() (k8sdynamic.Interface, error) {
|
||||
config, err := Config().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to provide dynamic client")
|
||||
}
|
||||
return k8sdynamic.NewForConfig(config)
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// test if dynamic implements DynamicProvider interface
|
||||
var _ DynamicProvider = &DynamicStruct{}
|
||||
|
||||
func TestDynamicProvider(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
iserr bool
|
||||
}{
|
||||
"101": {true},
|
||||
}
|
||||
|
||||
for name, mock := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, err := Dynamic().Provide()
|
||||
if !mock.iserr && err != nil {
|
||||
t.Fatalf("Test '%s' failed: expected no error: actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
)
|
||||
|
||||
// GetServerVersion uses the client-go Discovery client to get the
|
||||
// kubernetes version struct
|
||||
func GetServerVersion() (*version.Info, error) {
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get apiserver version")
|
||||
}
|
||||
return cs.Discovery().ServerVersion()
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NamespaceGetter abstracts fetching of Namespace from kubernetes cluster
|
||||
type NamespaceGetter interface {
|
||||
Get(name string, options metav1.GetOptions) (*corev1.Namespace, error)
|
||||
}
|
||||
|
||||
// NamespaceLister abstracts fetching of a list of namespaces from kubernetes cluster
|
||||
type NamespaceLister interface {
|
||||
List(options metav1.ListOptions) (*corev1.NamespaceList, error)
|
||||
}
|
||||
|
||||
// NamespaceStruct is used to initialise kubernetes namespace instnaces
|
||||
type NamespaceStruct struct{}
|
||||
|
||||
// Namespace returns a pointer to the namespace struct
|
||||
func Namespace() *NamespaceStruct {
|
||||
return &NamespaceStruct{}
|
||||
}
|
||||
|
||||
// Get returns a namespace instance from kubernetes cluster
|
||||
func (ns *NamespaceStruct) Get(name string, options metav1.GetOptions) (*corev1.Namespace, error) {
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get namespace: %s", name)
|
||||
}
|
||||
return cs.CoreV1().Namespaces().Get(name, options)
|
||||
}
|
||||
|
||||
// List returns a slice of namespaces defined in a Kubernetes cluster
|
||||
func (ns *NamespaceStruct) List(options metav1.ListOptions) (*corev1.NamespaceList, error) {
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get namespaces")
|
||||
}
|
||||
return cs.CoreV1().Namespaces().List(options)
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NodeGetter abstracts fetching of Node details from kubernetes cluster
|
||||
type NodeGetter interface {
|
||||
Get(name string, options metav1.GetOptions) (*corev1.Node, error)
|
||||
}
|
||||
|
||||
// NodeLister abstracts fetching of Nodes from kubernetes cluster
|
||||
type NodeLister interface {
|
||||
List(options metav1.ListOptions) (*corev1.NodeList, error)
|
||||
}
|
||||
|
||||
//NodeStruct returns a struct used to instantiate a kubernetes Node
|
||||
type NodeStruct struct{}
|
||||
|
||||
// Node returnd a pointer to the node struct
|
||||
func Node() *NodeStruct {
|
||||
return &NodeStruct{}
|
||||
}
|
||||
|
||||
// Get returns a node instance from kubernetes cluster
|
||||
func (n *NodeStruct) Get(name string, options metav1.GetOptions) (*corev1.Node, error) {
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get node: %s", name)
|
||||
}
|
||||
return cs.CoreV1().Nodes().Get(name, options)
|
||||
}
|
||||
|
||||
// List returns a slice of Nodes registered in a Kubernetes cluster
|
||||
func (n *NodeStruct) List(options metav1.ListOptions) (*corev1.NodeList, error) {
|
||||
cs, err := Clientset().Get()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get nodes")
|
||||
}
|
||||
return cs.CoreV1().Nodes().List(options)
|
||||
}
|
||||
|
||||
// NumberOfNodes returns the number of nodes registered in a Kubernetes cluster
|
||||
func NumberOfNodes() (int, error) {
|
||||
n := Node()
|
||||
nodes, err := n.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "failed to get the number of nodes")
|
||||
}
|
||||
return len(nodes.Items), nil
|
||||
}
|
||||
|
||||
// GetNode returns a node instance from kubernetes cluster
|
||||
func GetNode(name string) (*corev1.Node, error) {
|
||||
n := Node()
|
||||
node, err := n.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get node")
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// ListNodes returns list of node instance from kubernetes cluster
|
||||
func ListNodes(options metav1.ListOptions) (*corev1.NodeList, error) {
|
||||
n := Node()
|
||||
nodelist, err := n.List(options)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list node")
|
||||
}
|
||||
return nodelist, nil
|
||||
}
|
||||
|
||||
// GetOSAndKernelVersion gets us the OS,Kernel version
|
||||
func GetOSAndKernelVersion() (string, error) {
|
||||
nodes := Node()
|
||||
// get a single node
|
||||
firstNode, err := nodes.List(metav1.ListOptions{Limit: 1})
|
||||
if err != nil {
|
||||
return "unknown, unknown", errors.Wrapf(err, "failed to get the os kernel/arch")
|
||||
}
|
||||
nodedetails := firstNode.Items[0].Status.NodeInfo
|
||||
return nodedetails.OSImage + ", " + nodedetails.KernelVersion, nil
|
||||
}
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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.
|
||||
*/
|
||||
|
||||
// TODO
|
||||
// Move this file to pkg/k8sresource/v1alpha1
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// ResourceCreator abstracts creating an unstructured instance in kubernetes
|
||||
// cluster
|
||||
type ResourceCreator interface {
|
||||
Create(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
||||
// ResourceGetter abstracts fetching an unstructured instance from kubernetes
|
||||
// cluster
|
||||
type ResourceGetter interface {
|
||||
Get(name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
||||
// ResourceLister abstracts fetching an unstructured list of instance from kubernetes
|
||||
// cluster
|
||||
type ResourceLister interface {
|
||||
List(options metav1.ListOptions) (*unstructured.UnstructuredList, error)
|
||||
}
|
||||
|
||||
// ResourceUpdater abstracts updating an unstructured instance found in
|
||||
// kubernetes cluster
|
||||
type ResourceUpdater interface {
|
||||
Update(oldobj, newobj *unstructured.Unstructured, subresources ...string) (u *unstructured.Unstructured, err error)
|
||||
}
|
||||
|
||||
// ResourceApplier abstracts applying an unstructured instance that may or may
|
||||
// not be available in kubernetes cluster
|
||||
type ResourceApplier interface {
|
||||
Apply(obj *unstructured.Unstructured, subresources ...string) (*unstructured.Unstructured, error)
|
||||
}
|
||||
|
||||
// ResourceDeleter abstracts deletes an unstructured instance that is available in kubernetes cluster
|
||||
type ResourceDeleter interface {
|
||||
Delete(obj *unstructured.Unstructured, subresources ...string) error
|
||||
}
|
||||
|
||||
// ResourceStruct is used to abstract a kubernetes struct
|
||||
type ResourceStruct struct {
|
||||
gvr schema.GroupVersionResource // identify a resource
|
||||
namespace string // namespace where this resource is to be operated at
|
||||
}
|
||||
|
||||
// String implements Stringer interface
|
||||
func (r *ResourceStruct) String() string {
|
||||
return r.gvr.String()
|
||||
}
|
||||
|
||||
// Resource returns a new resource instance
|
||||
func Resource(gvr schema.GroupVersionResource, namespace string) *ResourceStruct {
|
||||
return &ResourceStruct{gvr: gvr, namespace: namespace}
|
||||
}
|
||||
|
||||
// Create creates a new resource in kubernetes cluster
|
||||
func (r *ResourceStruct) Create(obj *unstructured.Unstructured, subresources ...string) (u *unstructured.Unstructured, err error) {
|
||||
if obj == nil {
|
||||
err = errors.Errorf("nil resource instance: failed to create resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
dynamic, err := Dynamic().Provide()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to create resource '%s' '%s' at '%s'", r.gvr, obj.GetName(), r.namespace)
|
||||
return
|
||||
}
|
||||
u, err = dynamic.Resource(r.gvr).Namespace(r.namespace).Create(obj, metav1.CreateOptions{}, subresources...)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to create resource '%s' '%s' at '%s'", r.gvr, obj.GetName(), r.namespace)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes a existing resource in kubernetes cluster
|
||||
func (r *ResourceStruct) Delete(obj *unstructured.Unstructured, subresources ...string) error {
|
||||
if obj == nil {
|
||||
return errors.Errorf("nil resource instance: failed to delete resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
}
|
||||
dynamic, err := Dynamic().Provide()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete resource '%s' '%s' at '%s'", r.gvr, obj.GetName(), r.namespace)
|
||||
}
|
||||
err = dynamic.Resource(r.gvr).Namespace(r.namespace).Delete(obj.GetName(), &metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete resource '%s' '%s' at '%s'", r.gvr, obj.GetName(), r.namespace)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns a specific resource from kubernetes cluster
|
||||
func (r *ResourceStruct) Get(name string, opts metav1.GetOptions, subresources ...string) (u *unstructured.Unstructured, err error) {
|
||||
if len(strings.TrimSpace(name)) == 0 {
|
||||
err = errors.Errorf("missing resource name: failed to get resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
dynamic, err := Dynamic().Provide()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to get resource '%s' '%s' at '%s'", r.gvr, name, r.namespace)
|
||||
return
|
||||
}
|
||||
u, err = dynamic.Resource(r.gvr).Namespace(r.namespace).Get(name, opts, subresources...)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to get resource '%s' '%s' at '%s'", r.gvr, name, r.namespace)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Update updates the resource at kubernetes cluster
|
||||
func (r *ResourceStruct) Update(oldobj, newobj *unstructured.Unstructured, subresources ...string) (u *unstructured.Unstructured, err error) {
|
||||
if oldobj == nil {
|
||||
err = errors.Errorf("nil old resource instance: failed to update resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
if newobj == nil {
|
||||
err = errors.Errorf("nil new resource instance: failed to update resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
dynamic, err := Dynamic().Provide()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to update resource '%s' '%s' at '%s'", r.gvr, oldobj.GetName(), r.namespace)
|
||||
return
|
||||
}
|
||||
|
||||
resourceVersion := oldobj.GetResourceVersion()
|
||||
newobj.SetResourceVersion(resourceVersion)
|
||||
|
||||
u, err = dynamic.Resource(r.gvr).Namespace(r.namespace).Update(newobj, metav1.UpdateOptions{}, subresources...)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to update resource '%s' '%s' at '%s'", r.gvr, oldobj.GetName(), r.namespace)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List returns a list of specific resource at kubernetes cluster
|
||||
func (r *ResourceStruct) List(opts metav1.ListOptions) (u *unstructured.UnstructuredList, err error) {
|
||||
dynamic, err := Dynamic().Provide()
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to list resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
u, err = dynamic.Resource(r.gvr).Namespace(r.namespace).List(opts)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to list resource '%s' at '%s'", r.gvr, r.namespace)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResourceCreateOrUpdater as the name suggests manages to either
|
||||
// create or update a given resource. It does so by implementing
|
||||
// ResourceApplier interface
|
||||
type ResourceCreateOrUpdater struct {
|
||||
*ResourceStruct
|
||||
|
||||
// Various executors required to perform Apply
|
||||
// This is how this instance decouples its dependencies
|
||||
Getter ResourceGetter
|
||||
Creator ResourceCreator
|
||||
Updater ResourceUpdater
|
||||
|
||||
// IsSkipUpdate will not update this resource if set to true.
|
||||
// In other words, enabling this flag can only create the
|
||||
// resource in the cluster if not created previously
|
||||
IsSkipUpdate bool
|
||||
}
|
||||
|
||||
// ResourceCreateOrUpdaterOption is a typed function used to
|
||||
// build an instance of ResourceCreateOrUpdater
|
||||
//
|
||||
// NOTE:
|
||||
// This follows the pattern known as "functional options". It
|
||||
// is a function that operates on a given structure as a value
|
||||
// to build (initialise, configure, sensible defaults, etc) this
|
||||
// same structure.
|
||||
type ResourceCreateOrUpdaterOption func(*ResourceCreateOrUpdater)
|
||||
|
||||
// ResourceCreateOrUpdaterSkipUpdate sets IsSkipUpdate based
|
||||
// on the provided flag
|
||||
func ResourceCreateOrUpdaterSkipUpdate(skip bool) ResourceCreateOrUpdaterOption {
|
||||
return func(r *ResourceCreateOrUpdater) {
|
||||
r.IsSkipUpdate = skip
|
||||
}
|
||||
}
|
||||
|
||||
// NewResourceCreateOrUpdater returns a new instance of
|
||||
// ResourceCreateOrUpdater
|
||||
func NewResourceCreateOrUpdater(
|
||||
gvr schema.GroupVersionResource,
|
||||
namespace string,
|
||||
options ...ResourceCreateOrUpdaterOption,
|
||||
) *ResourceCreateOrUpdater {
|
||||
resource := Resource(gvr, namespace)
|
||||
t := &ResourceCreateOrUpdater{
|
||||
ResourceStruct: resource,
|
||||
Getter: resource,
|
||||
Creator: resource,
|
||||
Updater: resource,
|
||||
}
|
||||
for _, o := range options {
|
||||
o(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// String implements Stringer interface
|
||||
func (r *ResourceCreateOrUpdater) String() string {
|
||||
if r.ResourceStruct == nil {
|
||||
return fmt.Sprint("ResourceCreateOrUpdater")
|
||||
}
|
||||
return fmt.Sprintf("ResourceCreateOrUpdater %s", r.ResourceStruct)
|
||||
}
|
||||
|
||||
// Apply applies a resource to the kubernetes cluster. In other words, it
|
||||
// creates a new resource if it does not exist or updates the existing
|
||||
// resource.
|
||||
func (r *ResourceCreateOrUpdater) Apply(
|
||||
obj *unstructured.Unstructured,
|
||||
subresources ...string,
|
||||
) (resource *unstructured.Unstructured, err error) {
|
||||
if r.Getter == nil {
|
||||
err = errors.Errorf("%s: Apply failed: Nil getter", r)
|
||||
return
|
||||
}
|
||||
if r.Creator == nil {
|
||||
err = errors.Errorf("%s: Apply failed: Nil creator", r)
|
||||
return
|
||||
}
|
||||
if r.Updater == nil {
|
||||
err = errors.Errorf("%s: Apply failed: Nil updater", r)
|
||||
return
|
||||
}
|
||||
if obj == nil {
|
||||
err = errors.Errorf("%s: Apply failed: Nil resource", r)
|
||||
return
|
||||
}
|
||||
resource, err = r.Getter.Get(obj.GetName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(errors.Cause(err)) {
|
||||
return r.Creator.Create(obj, subresources...)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if r.IsSkipUpdate {
|
||||
klog.V(2).Infof("%s: Skipping update", r)
|
||||
return resource, nil
|
||||
}
|
||||
return r.Updater.Update(resource, obj, subresources...)
|
||||
}
|
||||
|
||||
// ResourceDeleteOptions is a utility instance used during the resource's delete operations
|
||||
type ResourceDeleteOptions struct {
|
||||
Deleter ResourceDeleter
|
||||
}
|
||||
|
||||
// Delete is a resource that is suitable to be executed as a Delete operation
|
||||
type Delete struct {
|
||||
*ResourceStruct
|
||||
options ResourceDeleteOptions
|
||||
}
|
||||
|
||||
// DeleteResource returns a new instance of delete resource
|
||||
func DeleteResource(gvr schema.GroupVersionResource, namespace string) *Delete {
|
||||
resource := Resource(gvr, namespace)
|
||||
options := ResourceDeleteOptions{Deleter: resource}
|
||||
return &Delete{ResourceStruct: resource, options: options}
|
||||
}
|
||||
|
||||
// Delete deletes a resource from a kubernetes cluster
|
||||
func (d *Delete) Delete(obj *unstructured.Unstructured, subresources ...string) error {
|
||||
if d.options.Deleter == nil {
|
||||
return errors.New("nil resource deleter instance: failed to delete resource")
|
||||
} else if obj == nil {
|
||||
return errors.New("nil resource instance: failed to delete resource")
|
||||
}
|
||||
return d.options.Deleter.Delete(obj, subresources...)
|
||||
}
|
||||
|
||||
// ResourceListOptions is a utility instance used during the resource's list operations
|
||||
type ResourceListOptions struct {
|
||||
Lister ResourceLister
|
||||
}
|
||||
|
||||
// List is a resource resource that is suitable to be executed as a List operation
|
||||
type List struct {
|
||||
*ResourceStruct
|
||||
options ResourceListOptions
|
||||
}
|
||||
|
||||
// ListResource returns a new instance of list resource
|
||||
func ListResource(gvr schema.GroupVersionResource, namespace string) *List {
|
||||
resource := Resource(gvr, namespace)
|
||||
options := ResourceListOptions{Lister: resource}
|
||||
return &List{ResourceStruct: resource, options: options}
|
||||
}
|
||||
|
||||
// List lists a resource from a kubernetes cluster
|
||||
func (l *List) List(options metav1.ListOptions) (u *unstructured.UnstructuredList, err error) {
|
||||
if l.options.Lister == nil {
|
||||
err = errors.New("nil resource lister instance: failed to list resource")
|
||||
return
|
||||
}
|
||||
return l.options.Lister.List(options)
|
||||
}
|
||||
|
||||
// ResourceGetOptions is a utility instance used during the resource's get operations
|
||||
type ResourceGetOptions struct {
|
||||
Getter ResourceGetter
|
||||
}
|
||||
|
||||
// Get is resource that is suitable to be executed as Get operation
|
||||
type Get struct {
|
||||
*ResourceStruct
|
||||
options ResourceGetOptions
|
||||
}
|
||||
|
||||
// GetResource returns a new instance of get resource
|
||||
func GetResource(gvr schema.GroupVersionResource, namespace string) *Get {
|
||||
resource := Resource(gvr, namespace)
|
||||
options := ResourceGetOptions{Getter: resource}
|
||||
return &Get{ResourceStruct: resource, options: options}
|
||||
}
|
||||
|
||||
// Get gets a resource from a kubernetes cluster
|
||||
func (g *Get) Get(name string, opts metav1.GetOptions, subresources ...string) (u *unstructured.Unstructured, err error) {
|
||||
if g.options.Getter == nil {
|
||||
err = errors.New("nil resource getter instance: failed to get resource")
|
||||
return
|
||||
}
|
||||
return g.options.Getter.Get(name, opts, subresources...)
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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.
|
||||
*/
|
||||
|
||||
// TODO
|
||||
// Move this file to pkg/k8sresource/v1alpha1
|
||||
package v1alpha1
|
||||
|
||||
// verify if resource struct is an implementation of ResourceGetter
|
||||
var _ ResourceGetter = &ResourceStruct{}
|
||||
|
||||
// verify if resource struct is an implementation of ResourceCreator
|
||||
var _ ResourceCreator = &ResourceStruct{}
|
||||
|
||||
// verify if resource struct is an implementation of ResourceUpdater
|
||||
var _ ResourceUpdater = &ResourceStruct{}
|
||||
|
||||
// verify if createOrUpdate struct is an implementation of ResourceApplier
|
||||
var _ ResourceApplier = &ResourceCreateOrUpdater{}
|
||||
113
pkg/common/env/env.go
vendored
113
pkg/common/env/env.go
vendored
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// KubeConfig is the ENV variable to fetch kubernetes kubeconfig
|
||||
KubeConfig = "OPENEBS_IO_KUBE_CONFIG"
|
||||
|
||||
// KubeMaster is the ENV variable to fetch kubernetes master's address
|
||||
KubeMaster = "OPENEBS_IO_K8S_MASTER"
|
||||
)
|
||||
|
||||
// EnvironmentSetter abstracts setting of environment variable
|
||||
type EnvironmentSetter func(envKey string, value string) (err error)
|
||||
|
||||
// EnvironmentGetter abstracts fetching value from an environment variable
|
||||
type EnvironmentGetter func(envKey string) (value string)
|
||||
|
||||
// EnvironmentLookup abstracts looking up an environment variable
|
||||
type EnvironmentLookup func(envKey string) (value string, present bool)
|
||||
|
||||
// Set sets the provided environment variable
|
||||
//
|
||||
// NOTE:
|
||||
// This is an implementation of EnvironmentSetter
|
||||
func Set(envKey string, value string) (err error) {
|
||||
return os.Setenv(string(envKey), value)
|
||||
}
|
||||
|
||||
// Get fetches value from the provided environment variable
|
||||
//
|
||||
// NOTE:
|
||||
// This is an implementation of EnvironmentGetter
|
||||
func Get(envKey string) (value string) {
|
||||
return getEnv(string(envKey))
|
||||
}
|
||||
|
||||
// GetOrDefault fetches value from the provided environment variable
|
||||
// which on empty returns the defaultValue
|
||||
// NOTE: os.Getenv is used here instead of os.LookupEnv because it is
|
||||
// not required to know if the environment variable is defined on the system
|
||||
func GetOrDefault(e string, defaultValue string) (value string) {
|
||||
envValue := Get(e)
|
||||
if len(envValue) == 0 {
|
||||
// ENV not defined or set to ""
|
||||
return defaultValue
|
||||
}
|
||||
return envValue
|
||||
}
|
||||
|
||||
// Lookup looks up an environment variable
|
||||
//
|
||||
// NOTE:
|
||||
// This is an implementation of EnvironmentLookup
|
||||
func Lookup(envKey string) (value string, present bool) {
|
||||
return lookupEnv(string(envKey))
|
||||
}
|
||||
|
||||
// Truthy returns boolean based on the environment variable's value
|
||||
//
|
||||
// The lookup value can be truthy (i.e. 1, t, TRUE, true) or falsy (0, false,
|
||||
// etc) based on strconv.ParseBool logic
|
||||
func Truthy(envKey string) (truth bool) {
|
||||
v, found := Lookup(envKey)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
truth, _ = strconv.ParseBool(v)
|
||||
return
|
||||
}
|
||||
|
||||
// LookupOrFalse looks up an environment variable and returns a string "false"
|
||||
// if environment variable is not present. It returns appropriate values for
|
||||
// other cases.
|
||||
func LookupOrFalse(envKey string) string {
|
||||
val, present := Lookup(envKey)
|
||||
if !present {
|
||||
return "false"
|
||||
}
|
||||
return strings.TrimSpace(val)
|
||||
}
|
||||
|
||||
// getEnv fetches the provided environment variable's value
|
||||
func getEnv(envKey string) (value string) {
|
||||
return strings.TrimSpace(os.Getenv(envKey))
|
||||
}
|
||||
|
||||
// lookupEnv looks up the provided environment variable
|
||||
func lookupEnv(envKey string) (value string, present bool) {
|
||||
value, present = os.LookupEnv(envKey)
|
||||
value = strings.TrimSpace(value)
|
||||
return
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &err{
|
||||
prefix: stackTraceMessagePrefix,
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &err{
|
||||
prefix: stackTraceMessagePrefix,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap annotates err with a new message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &wrapper{wrapErrorMessagePrefix, message, err}
|
||||
}
|
||||
|
||||
// Wrapf annotates err with the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &wrapper{wrapErrorMessagePrefix, fmt.Sprintf(format, args...), err}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the
|
||||
// point WithStack was called. If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
stackTraceMessagePrefix,
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
wrapErrorMessagePrefix string = " -- "
|
||||
listErrorMessagePrefix string = " - "
|
||||
stackTraceMessagePrefix string = " "
|
||||
)
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
// callers returns stack of caller function
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// err implements error interface that has a message and stack
|
||||
type err struct {
|
||||
prefix string
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
// Error is implementation of error interface
|
||||
func (e *err) Error() string { return e.msg }
|
||||
|
||||
// Format is implementation of Formater interface
|
||||
func (e *err) Format(s fmt.State, verb rune) {
|
||||
message := wrapErrorMessagePrefix + e.msg
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprint(s, message)
|
||||
for i, pc := range *e.stack {
|
||||
if i > 0 {
|
||||
return
|
||||
}
|
||||
f := errors.Frame(pc)
|
||||
fmt.Fprintf(s, "\n%s%+v", e.prefix, f)
|
||||
}
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
fmt.Fprint(s, message)
|
||||
}
|
||||
}
|
||||
|
||||
// wrapper implements error interface that has a message and error
|
||||
type wrapper struct {
|
||||
prefix string
|
||||
msg string
|
||||
error
|
||||
}
|
||||
|
||||
// Error is implementation of error interface
|
||||
func (w *wrapper) Error() string { return w.msg }
|
||||
|
||||
// Cause is implementation of causer interface
|
||||
func (w *wrapper) Cause() error { return w.error }
|
||||
|
||||
// Format is implementation of Formater interface
|
||||
func (w *wrapper) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.error)
|
||||
fmt.Fprint(s, w.prefix+w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
fmt.Fprintf(s, "%s\n", w.error)
|
||||
fmt.Fprint(s, w.prefix+w.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// withStack implements error interface that has a stack and error
|
||||
type withStack struct {
|
||||
prefix string
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
// Format is implementation of Formater interface
|
||||
func (ws *withStack) Format(s fmt.State, verb rune) {
|
||||
message := wrapErrorMessagePrefix + fmt.Sprintf("%s", ws.error)
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprint(s, message)
|
||||
for i, pc := range *ws.stack {
|
||||
if i > 0 {
|
||||
return
|
||||
}
|
||||
f := errors.Frame(pc)
|
||||
fmt.Fprintf(s, "\n%s%+v", ws.prefix, f)
|
||||
}
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
fmt.Fprint(s, message)
|
||||
}
|
||||
}
|
||||
|
||||
// Cause is implementation of causer interface
|
||||
func (ws *withStack) Cause() error { return ws.error }
|
||||
|
||||
// ErrorList is a wrapper over list of errors
|
||||
// It implements error interface
|
||||
type ErrorList struct {
|
||||
Errors []error
|
||||
msg string
|
||||
}
|
||||
|
||||
// Error is implementation of error interface
|
||||
func (el *ErrorList) Error() string {
|
||||
message := ""
|
||||
for _, err := range el.Errors {
|
||||
message += err.Error() + ":"
|
||||
}
|
||||
el.msg = message
|
||||
return message
|
||||
}
|
||||
|
||||
// Format is implementation of Formater interface
|
||||
func (el *ErrorList) Format(s fmt.State, verb rune) {
|
||||
message := ""
|
||||
for _, err := range el.Errors {
|
||||
message += "\n" + listErrorMessagePrefix + err.Error()
|
||||
}
|
||||
fmt.Fprint(s, message)
|
||||
|
||||
}
|
||||
|
||||
// WithStack annotates ErrorList with a new message and
|
||||
// stack trace of caller.
|
||||
func (el *ErrorList) WithStack(message string) error {
|
||||
if el == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
stackTraceMessagePrefix,
|
||||
Wrap(el, message),
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithStackf annotates ErrorList with the format specifier
|
||||
// and stack trace of caller.
|
||||
func (el *ErrorList) WithStackf(format string, args ...interface{}) error {
|
||||
if el == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
stackTraceMessagePrefix,
|
||||
Wrapf(el, format, args...),
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 helpers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetCaseInsensitiveMap coercs the map's keys to lower case, which only works
|
||||
// when unicode char is in ASCII subset. May overwrite key-value pairs on
|
||||
// different permutations of key case as in Key and key. DON'T force values to the
|
||||
// lower case unconditionally, because values for keys such as mountpoint or
|
||||
// keylocation are case-sensitive.
|
||||
// Note that although keys such as 'comPREssion' are accepted and processed,
|
||||
// even if they are technically invalid, updates to rectify such typing will be
|
||||
// prohibited as a forbidden update.
|
||||
func GetCaseInsensitiveMap(dict *map[string]string) map[string]string {
|
||||
insensitiveDict := map[string]string{}
|
||||
|
||||
for k, v := range *dict {
|
||||
insensitiveDict[strings.ToLower(k)] = v
|
||||
}
|
||||
return insensitiveDict
|
||||
}
|
||||
|
||||
// GetInsensitiveParameter handles special case ofGetCaseInsensitiveMap looking up one
|
||||
// key-value pair only
|
||||
func GetInsensitiveParameter(dict *map[string]string, key string) string {
|
||||
insensitiveDict := GetCaseInsensitiveMap(dict)
|
||||
return insensitiveDict[strings.ToLower(key)]
|
||||
}
|
||||
|
|
@ -1,250 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
env "github.com/openebs/zfs-localpv/pkg/common/env"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// getInClusterConfigFunc abstracts the logic to get
|
||||
// kubernetes incluster config
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type getInClusterConfigFunc func() (*rest.Config, error)
|
||||
|
||||
// buildConfigFromFlagsFunc provides the abstraction to get
|
||||
// kubernetes config from provided flags
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type buildConfigFromFlagsFunc func(string, string) (*rest.Config, error)
|
||||
|
||||
// GetConfigFunc provides the abstraction to get
|
||||
// kubernetes config from provided client instance
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type GetConfigFunc func(*Client) (*rest.Config, error)
|
||||
|
||||
// GetConfig returns kubernetes config instance
|
||||
//
|
||||
// NOTE:
|
||||
// This is an implementation of GetConfigFunc
|
||||
func GetConfig(c *Client) (*rest.Config, error) {
|
||||
if c == nil {
|
||||
return nil, errors.New("failed to get kubernetes config: nil client was provided")
|
||||
}
|
||||
return c.GetConfigForPathOrDirect()
|
||||
}
|
||||
|
||||
// getKubeMasterIPFunc provides the abstraction to get
|
||||
// kubernetes master IP address
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type getKubeMasterIPFunc func(string) string
|
||||
|
||||
// getKubeConfigPathFunc provides the abstraction to get
|
||||
// kubernetes config path
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type getKubeConfigPathFunc func(string) string
|
||||
|
||||
// getKubernetesDynamicClientFunc provides the abstraction to get
|
||||
// dynamic kubernetes clientset
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type getKubernetesDynamicClientFunc func(*rest.Config) (dynamic.Interface, error)
|
||||
|
||||
// getKubernetesClientsetFunc provides the abstraction to get
|
||||
// kubernetes clientset
|
||||
//
|
||||
// NOTE:
|
||||
// typed function makes it simple to mock
|
||||
type getKubernetesClientsetFunc func(*rest.Config) (*kubernetes.Clientset, error)
|
||||
|
||||
// Client provides common kuberenetes client operations
|
||||
type Client struct {
|
||||
IsInCluster bool // flag to let client point to its own cluster
|
||||
KubeConfigPath string // kubeconfig path to get kubernetes clientset
|
||||
|
||||
// Below functions are useful during mock
|
||||
|
||||
// handle to get in cluster config
|
||||
getInClusterConfig getInClusterConfigFunc
|
||||
|
||||
// handle to get desired kubernetes config
|
||||
buildConfigFromFlags buildConfigFromFlagsFunc
|
||||
|
||||
// handle to get kubernetes clienset
|
||||
getKubernetesClientset getKubernetesClientsetFunc
|
||||
|
||||
// handle to get dynamic kubernetes clientset
|
||||
getKubernetesDynamicClient getKubernetesDynamicClientFunc
|
||||
|
||||
// handle to get kubernetes master IP
|
||||
getKubeMasterIP getKubeMasterIPFunc
|
||||
|
||||
// handle to get kubernetes config path
|
||||
getKubeConfigPath getKubeConfigPathFunc
|
||||
}
|
||||
|
||||
// OptionFunc is a typed function that abstracts any kind of operation
|
||||
// against the provided client instance
|
||||
//
|
||||
// This is the basic building block to create functional operations
|
||||
// against the client instance
|
||||
type OptionFunc func(*Client)
|
||||
|
||||
// New returns a new instance of client
|
||||
func New(opts ...OptionFunc) *Client {
|
||||
c := &Client{}
|
||||
for _, o := range opts {
|
||||
o(c)
|
||||
}
|
||||
withDefaults(c)
|
||||
return c
|
||||
}
|
||||
|
||||
var (
|
||||
instance *Client
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Instance returns a singleton instance of
|
||||
// this client
|
||||
func Instance(opts ...OptionFunc) *Client {
|
||||
once.Do(func() {
|
||||
instance = New(opts...)
|
||||
})
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
func withDefaults(c *Client) {
|
||||
if c.getInClusterConfig == nil {
|
||||
c.getInClusterConfig = rest.InClusterConfig
|
||||
}
|
||||
if c.buildConfigFromFlags == nil {
|
||||
c.buildConfigFromFlags = clientcmd.BuildConfigFromFlags
|
||||
}
|
||||
if c.getKubernetesClientset == nil {
|
||||
c.getKubernetesClientset = kubernetes.NewForConfig
|
||||
}
|
||||
if c.getKubernetesDynamicClient == nil {
|
||||
c.getKubernetesDynamicClient = dynamic.NewForConfig
|
||||
}
|
||||
if c.getKubeMasterIP == nil {
|
||||
c.getKubeMasterIP = env.Get
|
||||
}
|
||||
if c.getKubeConfigPath == nil {
|
||||
c.getKubeConfigPath = env.Get
|
||||
}
|
||||
}
|
||||
|
||||
// InCluster enables IsInCluster flag
|
||||
func InCluster() OptionFunc {
|
||||
return func(c *Client) {
|
||||
c.IsInCluster = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithKubeConfigPath sets kubeconfig path
|
||||
// against this client instance
|
||||
func WithKubeConfigPath(kubeConfigPath string) OptionFunc {
|
||||
return func(c *Client) {
|
||||
c.KubeConfigPath = kubeConfigPath
|
||||
}
|
||||
}
|
||||
|
||||
// Clientset returns a new instance of kubernetes clientset
|
||||
func (c *Client) Clientset() (*kubernetes.Clientset, error) {
|
||||
config, err := c.GetConfigForPathOrDirect()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err,
|
||||
"failed to get kubernetes clientset: failed to get kubernetes config: IsInCluster {%t}: KubeConfigPath {%s}",
|
||||
c.IsInCluster,
|
||||
c.KubeConfigPath,
|
||||
)
|
||||
}
|
||||
return c.getKubernetesClientset(config)
|
||||
}
|
||||
|
||||
// Config returns the kubernetes config instance based on available criteria
|
||||
func (c *Client) Config() (config *rest.Config, err error) {
|
||||
// IsInCluster flag holds the top most priority
|
||||
if c.IsInCluster {
|
||||
return c.getInClusterConfig()
|
||||
}
|
||||
|
||||
// ENV holds second priority
|
||||
if strings.TrimSpace(c.getKubeMasterIP(env.KubeMaster)) != "" ||
|
||||
strings.TrimSpace(c.getKubeConfigPath(env.KubeConfig)) != "" {
|
||||
return c.getConfigFromENV()
|
||||
}
|
||||
|
||||
// Defaults to InClusterConfig
|
||||
return c.getInClusterConfig()
|
||||
}
|
||||
|
||||
// ConfigForPath returns the kubernetes config instance based on KubeConfig path
|
||||
func (c *Client) ConfigForPath(kubeConfigPath string) (config *rest.Config, err error) {
|
||||
return c.buildConfigFromFlags("", kubeConfigPath)
|
||||
}
|
||||
|
||||
// GetConfigForPathOrDirect returns the kubernetes config instance based on direct KubeConfig
|
||||
func (c *Client) GetConfigForPathOrDirect() (config *rest.Config, err error) {
|
||||
if c.KubeConfigPath != "" {
|
||||
return c.ConfigForPath(c.KubeConfigPath)
|
||||
}
|
||||
return c.Config()
|
||||
}
|
||||
|
||||
func (c *Client) getConfigFromENV() (config *rest.Config, err error) {
|
||||
k8sMaster := c.getKubeMasterIP(env.KubeMaster)
|
||||
kubeConfig := c.getKubeConfigPath(env.KubeConfig)
|
||||
if strings.TrimSpace(k8sMaster) == "" &&
|
||||
strings.TrimSpace(kubeConfig) == "" {
|
||||
return nil, errors.Errorf(
|
||||
"failed to get kubernetes config: missing ENV: atleast one should be set: {%s} or {%s}",
|
||||
env.KubeMaster,
|
||||
env.KubeConfig,
|
||||
)
|
||||
}
|
||||
return c.buildConfigFromFlags(k8sMaster, kubeConfig)
|
||||
}
|
||||
|
||||
// Dynamic returns a kubernetes dynamic client capable of invoking operations
|
||||
// against kubernetes resources
|
||||
func (c *Client) Dynamic() (dynamic.Interface, error) {
|
||||
config, err := c.GetConfigForPathOrDirect()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get dynamic client")
|
||||
}
|
||||
return c.getKubernetesDynamicClient(config)
|
||||
}
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 The OpenEBS 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 v1alpha1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func fakeGetClientsetOk(c *rest.Config) (*kubernetes.Clientset, error) {
|
||||
return &kubernetes.Clientset{}, nil
|
||||
}
|
||||
|
||||
func fakeGetClientsetErr(c *rest.Config) (*kubernetes.Clientset, error) {
|
||||
return nil, errors.New("fake error")
|
||||
}
|
||||
|
||||
func fakeInClusterConfigOk() (*rest.Config, error) {
|
||||
return &rest.Config{}, nil
|
||||
}
|
||||
|
||||
func fakeInClusterConfigErr() (*rest.Config, error) {
|
||||
return nil, errors.New("fake error")
|
||||
}
|
||||
|
||||
func fakeBuildConfigFromFlagsOk(kubemaster string, kubeconfig string) (*rest.Config, error) {
|
||||
return &rest.Config{}, nil
|
||||
}
|
||||
|
||||
func fakeBuildConfigFromFlagsErr(kubemaster string, kubeconfig string) (*rest.Config, error) {
|
||||
return nil, errors.New("fake error")
|
||||
}
|
||||
|
||||
func fakeGetKubeConfigPathOk(e string) string {
|
||||
return "fake"
|
||||
}
|
||||
|
||||
func fakeGetKubeConfigPathNil(e string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func fakeGetKubeMasterIPOk(e string) string {
|
||||
return "fake"
|
||||
}
|
||||
|
||||
func fakeGetKubeMasterIPNil(e string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func fakeGetDynamicClientSetOk(c *rest.Config) (dynamic.Interface, error) {
|
||||
return dynamic.NewForConfig(c)
|
||||
}
|
||||
|
||||
func fakeGetDynamicClientSetNil(c *rest.Config) (dynamic.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func fakeGetDynamicClientSetErr(c *rest.Config) (dynamic.Interface, error) {
|
||||
return nil, errors.New("fake error")
|
||||
}
|
||||
|
||||
func TestNewInCluster(t *testing.T) {
|
||||
c := New(InCluster())
|
||||
if !c.IsInCluster {
|
||||
t.Fatalf("test failed: expected IsInCluster as 'true' actual '%t'", c.IsInCluster)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
isInCluster bool
|
||||
kubeConfigPath string
|
||||
getInClusterConfig getInClusterConfigFunc
|
||||
getKubeMasterIP getKubeMasterIPFunc
|
||||
getKubeConfigPath getKubeConfigPathFunc
|
||||
getConfigFromENV buildConfigFromFlagsFunc
|
||||
isErr bool
|
||||
}{
|
||||
"t1": {true, "", fakeInClusterConfigOk, nil, nil, nil, false},
|
||||
"t2": {true, "", fakeInClusterConfigErr, nil, nil, nil, true},
|
||||
"t3": {false, "", fakeInClusterConfigErr, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, true},
|
||||
"t4": {false, "", fakeInClusterConfigOk, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, false},
|
||||
"t5": {false, "fakeKubeConfigPath", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathNil, fakeBuildConfigFromFlagsOk, false},
|
||||
"t6": {false, "", nil, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, false},
|
||||
"t7": {false, "", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, false},
|
||||
"t8": {false, "fakeKubeConfigPath", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsErr, true},
|
||||
"t9": {false, "fakeKubeConfigpath", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, false},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
IsInCluster: mock.isInCluster,
|
||||
KubeConfigPath: mock.kubeConfigPath,
|
||||
getInClusterConfig: mock.getInClusterConfig,
|
||||
getKubeMasterIP: mock.getKubeMasterIP,
|
||||
getKubeConfigPath: mock.getKubeConfigPath,
|
||||
buildConfigFromFlags: mock.getConfigFromENV,
|
||||
}
|
||||
_, err := c.Config()
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected no error actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigFromENV(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
getKubeMasterIP getKubeMasterIPFunc
|
||||
getKubeConfigPath getKubeConfigPathFunc
|
||||
getConfigFromENV buildConfigFromFlagsFunc
|
||||
isErr bool
|
||||
}{
|
||||
"t1": {fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, true},
|
||||
"t2": {fakeGetKubeMasterIPNil, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, false},
|
||||
"t3": {fakeGetKubeMasterIPOk, fakeGetKubeConfigPathNil, fakeBuildConfigFromFlagsOk, false},
|
||||
"t4": {fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, false},
|
||||
"t5": {fakeGetKubeMasterIPNil, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsErr, true},
|
||||
"t6": {fakeGetKubeMasterIPOk, fakeGetKubeConfigPathNil, fakeBuildConfigFromFlagsErr, true},
|
||||
"t7": {fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsErr, true},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
getKubeMasterIP: mock.getKubeMasterIP,
|
||||
getKubeConfigPath: mock.getKubeConfigPath,
|
||||
buildConfigFromFlags: mock.getConfigFromENV,
|
||||
}
|
||||
_, err := c.getConfigFromENV()
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected error actual no error", name)
|
||||
}
|
||||
if !mock.isErr && err != nil {
|
||||
t.Fatalf("test '%s' failed: expected no error actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigFromPathOrDirect(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
kubeConfigPath string
|
||||
getConfigFromFlags buildConfigFromFlagsFunc
|
||||
getInClusterConfig getInClusterConfigFunc
|
||||
isErr bool
|
||||
}{
|
||||
"T1": {"", fakeBuildConfigFromFlagsErr, fakeInClusterConfigOk, false},
|
||||
"T2": {"fake-path", fakeBuildConfigFromFlagsOk, fakeInClusterConfigErr, false},
|
||||
"T3": {"fake-path", fakeBuildConfigFromFlagsErr, fakeInClusterConfigOk, true},
|
||||
"T4": {"", fakeBuildConfigFromFlagsOk, fakeInClusterConfigErr, true},
|
||||
"T5": {"fake-path", fakeBuildConfigFromFlagsErr, fakeInClusterConfigErr, true},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
KubeConfigPath: mock.kubeConfigPath,
|
||||
buildConfigFromFlags: mock.getConfigFromFlags,
|
||||
getInClusterConfig: mock.getInClusterConfig,
|
||||
getKubeMasterIP: fakeGetKubeMasterIPNil,
|
||||
getKubeConfigPath: fakeGetKubeConfigPathNil,
|
||||
}
|
||||
_, err := c.GetConfigForPathOrDirect()
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected error actual no error", name)
|
||||
}
|
||||
if !mock.isErr && err != nil {
|
||||
t.Fatalf("test '%s' failed: expected no error actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientset(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
isInCluster bool
|
||||
kubeConfigPath string
|
||||
getInClusterConfig getInClusterConfigFunc
|
||||
getKubeMasterIP getKubeMasterIPFunc
|
||||
getKubeConfigPath getKubeConfigPathFunc
|
||||
getConfigFromENV buildConfigFromFlagsFunc
|
||||
getKubernetesClientset getKubernetesClientsetFunc
|
||||
isErr bool
|
||||
}{
|
||||
"t10": {true, "", fakeInClusterConfigOk, nil, nil, nil, fakeGetClientsetOk, false},
|
||||
"t11": {true, "", fakeInClusterConfigOk, nil, nil, nil, fakeGetClientsetErr, true},
|
||||
"t12": {true, "", fakeInClusterConfigErr, nil, nil, nil, fakeGetClientsetOk, true},
|
||||
|
||||
"t21": {false, "", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathNil, fakeBuildConfigFromFlagsOk, fakeGetClientsetOk, false},
|
||||
"t22": {false, "", nil, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, fakeGetClientsetOk, false},
|
||||
"t23": {false, "", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, fakeGetClientsetOk, false},
|
||||
"t24": {false, "fake-path", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsErr, fakeGetClientsetOk, true},
|
||||
"t25": {false, "", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, fakeGetClientsetErr, true},
|
||||
"t26": {false, "fakePath", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsErr, fakeGetClientsetOk, true},
|
||||
|
||||
"t30": {false, "", fakeInClusterConfigOk, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, fakeGetClientsetOk, false},
|
||||
"t31": {false, "", fakeInClusterConfigOk, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, fakeGetClientsetErr, true},
|
||||
"t32": {false, "", fakeInClusterConfigErr, fakeGetKubeMasterIPNil, fakeGetKubeConfigPathNil, nil, nil, true},
|
||||
"t33": {false, "fakePath", nil, fakeGetKubeMasterIPOk, fakeGetKubeConfigPathOk, fakeBuildConfigFromFlagsOk, fakeGetClientsetOk, false},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
IsInCluster: mock.isInCluster,
|
||||
KubeConfigPath: mock.kubeConfigPath,
|
||||
getInClusterConfig: mock.getInClusterConfig,
|
||||
getKubeMasterIP: mock.getKubeMasterIP,
|
||||
getKubeConfigPath: mock.getKubeConfigPath,
|
||||
buildConfigFromFlags: mock.getConfigFromENV,
|
||||
getKubernetesClientset: mock.getKubernetesClientset,
|
||||
}
|
||||
_, err := c.Clientset()
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected error actual no error", name)
|
||||
}
|
||||
if !mock.isErr && err != nil {
|
||||
t.Fatalf("test '%s' failed: expected no error actual '%s'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamic(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
getKubeMasterIP getKubeMasterIPFunc
|
||||
getInClusterConfig getInClusterConfigFunc
|
||||
getKubernetesDynamicClientSet getKubernetesDynamicClientFunc
|
||||
kubeConfigPath string
|
||||
getConfigFromENV buildConfigFromFlagsFunc
|
||||
getKubeConfigPath getKubeConfigPathFunc
|
||||
isErr bool
|
||||
}{
|
||||
"t1": {fakeGetKubeMasterIPNil, fakeInClusterConfigErr, fakeGetDynamicClientSetOk, "fake-path", fakeBuildConfigFromFlagsOk, fakeGetKubeConfigPathNil, false},
|
||||
"t2": {fakeGetKubeMasterIPNil, fakeInClusterConfigErr, fakeGetDynamicClientSetErr, "fake-path", fakeBuildConfigFromFlagsOk, fakeGetKubeConfigPathOk, true},
|
||||
"t3": {fakeGetKubeMasterIPNil, fakeInClusterConfigErr, fakeGetDynamicClientSetOk, "fake-path", fakeBuildConfigFromFlagsErr, fakeGetKubeConfigPathOk, true},
|
||||
"t4": {fakeGetKubeMasterIPOk, fakeInClusterConfigOk, fakeGetDynamicClientSetOk, "", fakeBuildConfigFromFlagsOk, fakeGetKubeConfigPathOk, false},
|
||||
"t5": {fakeGetKubeMasterIPOk, fakeInClusterConfigErr, fakeGetDynamicClientSetErr, "", fakeBuildConfigFromFlagsOk, fakeGetKubeConfigPathOk, true},
|
||||
"t6": {fakeGetKubeMasterIPNil, fakeInClusterConfigOk, fakeGetDynamicClientSetErr, "", fakeBuildConfigFromFlagsErr, fakeGetKubeConfigPathNil, true},
|
||||
"t7": {fakeGetKubeMasterIPNil, fakeInClusterConfigErr, fakeGetDynamicClientSetOk, "", fakeBuildConfigFromFlagsErr, fakeGetKubeConfigPathNil, true},
|
||||
"t8": {fakeGetKubeMasterIPNil, fakeInClusterConfigErr, fakeGetDynamicClientSetErr, "", fakeBuildConfigFromFlagsErr, fakeGetKubeConfigPathNil, true},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
getKubeMasterIP: mock.getKubeMasterIP,
|
||||
KubeConfigPath: mock.kubeConfigPath,
|
||||
getInClusterConfig: mock.getInClusterConfig,
|
||||
buildConfigFromFlags: mock.getConfigFromENV,
|
||||
getKubeConfigPath: mock.getKubeConfigPath,
|
||||
getKubernetesDynamicClient: mock.getKubernetesDynamicClientSet,
|
||||
}
|
||||
_, err := c.Dynamic()
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected error actual no error", name)
|
||||
}
|
||||
if !mock.isErr && err != nil {
|
||||
t.Fatalf("test '%s' failed: expected no error but got '%v'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigForPath(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
kubeConfigPath string
|
||||
getConfigFromPath buildConfigFromFlagsFunc
|
||||
isErr bool
|
||||
}{
|
||||
"T1": {"", fakeBuildConfigFromFlagsErr, true},
|
||||
"T2": {"fake-path", fakeBuildConfigFromFlagsOk, false},
|
||||
}
|
||||
for name, mock := range tests {
|
||||
name, mock := name, mock // pin It
|
||||
t.Run(name, func(t *testing.T) {
|
||||
c := &Client{
|
||||
KubeConfigPath: mock.kubeConfigPath,
|
||||
buildConfigFromFlags: mock.getConfigFromPath,
|
||||
}
|
||||
_, err := c.ConfigForPath(mock.kubeConfigPath)
|
||||
if mock.isErr && err == nil {
|
||||
t.Fatalf("test '%s' failed: expected error actual no error", name)
|
||||
}
|
||||
if !mock.isErr && err != nil {
|
||||
t.Fatalf("test '%s' failed: expected no error but got '%v'", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -21,14 +21,14 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
k8sapi "github.com/openebs/lib-csi/pkg/client/k8s"
|
||||
"github.com/openebs/lib-csi/pkg/mount"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/builder/volbuilder"
|
||||
k8sapi "github.com/openebs/zfs-localpv/pkg/client/k8s/v1alpha1"
|
||||
"github.com/openebs/zfs-localpv/pkg/mgmt/backup"
|
||||
"github.com/openebs/zfs-localpv/pkg/mgmt/restore"
|
||||
"github.com/openebs/zfs-localpv/pkg/mgmt/snapshot"
|
||||
"github.com/openebs/zfs-localpv/pkg/mgmt/volume"
|
||||
"github.com/openebs/zfs-localpv/pkg/mount"
|
||||
"github.com/openebs/zfs-localpv/pkg/zfs"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/sys/unix"
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ import (
|
|||
k8serror "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
errors "github.com/openebs/lib-csi/pkg/common/errors"
|
||||
"github.com/openebs/lib-csi/pkg/common/helpers"
|
||||
schd "github.com/openebs/lib-csi/pkg/scheduler"
|
||||
"github.com/openebs/zfs-localpv/pkg/builder/snapbuilder"
|
||||
"github.com/openebs/zfs-localpv/pkg/builder/volbuilder"
|
||||
errors "github.com/openebs/zfs-localpv/pkg/common/errors"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/helpers"
|
||||
csipayload "github.com/openebs/zfs-localpv/pkg/response"
|
||||
schd "github.com/openebs/zfs-localpv/pkg/scheduler"
|
||||
analytics "github.com/openebs/zfs-localpv/pkg/usage"
|
||||
zfs "github.com/openebs/zfs-localpv/pkg/zfs"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 mount
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
// GetMounts gets mountpoints for the specified volume
|
||||
func GetMounts(dev string) ([]string, error) {
|
||||
|
||||
var (
|
||||
currentMounts []string
|
||||
err error
|
||||
mountList []mount.MountPoint
|
||||
)
|
||||
|
||||
mounter := mount.New("")
|
||||
// Get list of mounted paths present with the node
|
||||
if mountList, err = mounter.List(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, mntInfo := range mountList {
|
||||
if mntInfo.Device == dev {
|
||||
currentMounts = append(currentMounts, mntInfo.Path)
|
||||
}
|
||||
}
|
||||
return currentMounts, nil
|
||||
}
|
||||
|
||||
// IsMountPath returns true if path is a mount path
|
||||
func IsMountPath(path string) bool {
|
||||
|
||||
var (
|
||||
err error
|
||||
mountList []mount.MountPoint
|
||||
)
|
||||
|
||||
mounter := mount.New("")
|
||||
// Get list of mounted paths present with the node
|
||||
if mountList, err = mounter.List(); err != nil {
|
||||
return false
|
||||
}
|
||||
for _, mntInfo := range mountList {
|
||||
if mntInfo.Path == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
Copyright © 2019 The OpenEBS 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 scheduler
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||
k8sapi "github.com/openebs/zfs-localpv/pkg/client/k8s/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// getNodeList gets the nodelist which satisfies the topology info
|
||||
func getNodeList(topo *csi.TopologyRequirement) ([]string, error) {
|
||||
|
||||
var nodelist []string
|
||||
|
||||
list, err := k8sapi.ListNodes(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, node := range list.Items {
|
||||
for _, prf := range topo.Preferred {
|
||||
nodeFiltered := false
|
||||
for key, value := range prf.Segments {
|
||||
if node.Labels[key] != value {
|
||||
nodeFiltered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if nodeFiltered == false {
|
||||
nodelist = append(nodelist, node.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nodelist, nil
|
||||
}
|
||||
|
||||
// runScheduler goes through the node mapping
|
||||
// in the topology and picks the node which is less weighted
|
||||
func runScheduler(nodelist []string, nmap map[string]int64) string {
|
||||
var selected string
|
||||
|
||||
var weight int64 = math.MaxInt64
|
||||
|
||||
// schedule it on the node which has less weight
|
||||
for _, node := range nodelist {
|
||||
if nmap[node] < weight {
|
||||
selected = node
|
||||
weight = nmap[node]
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
|
||||
// Scheduler schedules the PV as per topology constraints for
|
||||
// the given node weight.
|
||||
func Scheduler(req *csi.CreateVolumeRequest, nmap map[string]int64) string {
|
||||
topo := req.AccessibilityRequirements
|
||||
if topo == nil ||
|
||||
len(topo.Preferred) == 0 {
|
||||
klog.Errorf("scheduler: topology information not provided")
|
||||
return ""
|
||||
}
|
||||
|
||||
nodelist, err := getNodeList(topo)
|
||||
if err != nil {
|
||||
klog.Errorf("scheduler: can not get the nodelist err : %v", err.Error())
|
||||
return ""
|
||||
} else if len(nodelist) == 0 {
|
||||
klog.Errorf("scheduler: nodelist is empty")
|
||||
return ""
|
||||
}
|
||||
|
||||
// if there is a single node, schedule it on that
|
||||
if len(nodelist) == 1 {
|
||||
return nodelist[0]
|
||||
}
|
||||
|
||||
return runScheduler(nodelist, nmap)
|
||||
}
|
||||
|
|
@ -18,8 +18,9 @@ package usage
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openebs/zfs-localpv/pkg/common/env"
|
||||
"time"
|
||||
|
||||
"github.com/openebs/lib-csi/pkg/common/env"
|
||||
)
|
||||
|
||||
// OpenEBSPingPeriod ping interval of volume io analytics
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
package usage
|
||||
|
||||
import (
|
||||
k8sapi "github.com/openebs/zfs-localpv/pkg/client/k8s/v1alpha1"
|
||||
k8sapi "github.com/openebs/lib-csi/pkg/client/k8s"
|
||||
)
|
||||
|
||||
// Usage struct represents all information about a usage metric sent to
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ limitations under the License.
|
|||
package usage
|
||||
|
||||
import (
|
||||
k8sapi "github.com/openebs/zfs-localpv/pkg/client/k8s/v1alpha1"
|
||||
env "github.com/openebs/zfs-localpv/pkg/common/env"
|
||||
k8sapi "github.com/openebs/lib-csi/pkg/client/k8s"
|
||||
env "github.com/openebs/lib-csi/pkg/common/env"
|
||||
openebsversion "github.com/openebs/zfs-localpv/pkg/version"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The OpenEBS 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 xfs
|
||||
|
||||
import (
|
||||
"github.com/openebs/zfs-localpv/pkg/mount"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"strings"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func xfsTempMount(device string) error {
|
||||
pvol := strings.Split(device, "/")
|
||||
volname := pvol[len(pvol)-1]
|
||||
|
||||
// evaluate the symlink to get the dev path for volume
|
||||
dev, err := filepath.EvalSymlinks(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create a temporary directory to mount the xfs file system
|
||||
tmpdir := "/tmp/" + volname
|
||||
err = os.Mkdir(tmpdir, 0755)
|
||||
if os.IsNotExist(err) {
|
||||
klog.Errorf("xfs: failed to create tmpdir %s error: %s", tmpdir, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
/*
|
||||
* Device might have already mounted at the tmp path but umount might have failed
|
||||
* in previous attempt. Checking here if device is not mounted then only attempt
|
||||
* to mount it, otherwise proceed with the umount.
|
||||
*/
|
||||
curMounts, err := mount.GetMounts(dev)
|
||||
if err != nil {
|
||||
klog.Errorf("xfs: get mounts failed dev: %s err: %v", device, err.Error())
|
||||
return err
|
||||
} else if len(curMounts) == 0 {
|
||||
// mount with nouuid, so that it can play the log
|
||||
cmd := exec.Command("mount", "-o", "nouuid", device, tmpdir)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
klog.Errorf("xfs: failed to mount device %s => %s error: %s", device, tmpdir, string(out))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
klog.Infof("xfs: device already mounted %s => [%v]", device, curMounts)
|
||||
}
|
||||
|
||||
// log has been replayed, unmount the volume
|
||||
cmd := exec.Command("umount", tmpdir)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
klog.Errorf("xfs: failed to umount tmpdir %s error: %s", tmpdir, string(out))
|
||||
return err
|
||||
}
|
||||
|
||||
// remove the tmp directory
|
||||
err = os.Remove(tmpdir)
|
||||
if err != nil {
|
||||
// don't return error, reconciliation is not needed as umount is done
|
||||
klog.Errorf("xfs: failed to remove tmpdir %s error: %s", tmpdir, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to generate a new UUID for the cloned volumes with xfs filesystem
|
||||
* otherwise system won't let anyone mount it if UUID is same. Here, since cloned
|
||||
* volume refers to the same block because of the way ZFS clone works, it will
|
||||
* also have the same UUID.
|
||||
* There might be something there in the xfs log, we have to clear them
|
||||
* so that filesystem is clean and we can generate the UUID for it.
|
||||
*/
|
||||
|
||||
// GenerateUUID generates a new UUID for the given device
|
||||
func GenerateUUID(device string) error {
|
||||
// temporary mount the volume with nouuid to replay the logs
|
||||
err := xfsTempMount(device)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// for mounting the cloned volume for xfs, a new UUID has to be generated
|
||||
cmd := exec.Command("xfs_admin", "-U", "generate", device)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
klog.Errorf("xfs: uuid generate failed for device %s error: %s", device, string(out))
|
||||
return err
|
||||
}
|
||||
klog.Infof("xfs: generated UUID for the device %s \n %v", device, string(out))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -20,8 +20,8 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
|
||||
mnt "github.com/openebs/lib-csi/pkg/mount"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
mnt "github.com/openebs/zfs-localpv/pkg/mount"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/klog"
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@ import (
|
|||
|
||||
"fmt"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/openebs/lib-csi/pkg/btrfs"
|
||||
"github.com/openebs/lib-csi/pkg/xfs"
|
||||
apis "github.com/openebs/zfs-localpv/pkg/apis/openebs.io/zfs/v1"
|
||||
"github.com/openebs/zfs-localpv/pkg/btrfs"
|
||||
"github.com/openebs/zfs-localpv/pkg/xfs"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// zfs related constants
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue