diff --git a/pkg/cluster/initinfra/infra_test.go b/pkg/cluster/initinfra/infra_test.go index 5d636c49a..bdd1ccb4f 100644 --- a/pkg/cluster/initinfra/infra_test.go +++ b/pkg/cluster/initinfra/infra_test.go @@ -8,24 +8,15 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "k8s.io/client-go/kubernetes" - "opendev.org/airship/airshipctl/pkg/cluster/initinfra" "opendev.org/airship/airshipctl/pkg/document" "opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/k8s/client" + "opendev.org/airship/airshipctl/pkg/k8s/client/fake" "opendev.org/airship/airshipctl/pkg/k8s/kubectl" "opendev.org/airship/airshipctl/testutil/k8sutils" ) -type TestClient struct { - MockKubectl func() kubectl.Interface - MockClientset func() kubernetes.Interface -} - -func (tc TestClient) ClientSet() kubernetes.Interface { return tc.MockClientset() } -func (tc TestClient) Kubectl() kubectl.Interface { return tc.MockKubectl() } - const ( kubeconfigPath = "testdata/kubeconfig.yaml" filenameRC = "testdata/primary/site/test-site/ephemeral/initinfra/replicationcontroller.yaml" @@ -55,7 +46,7 @@ func TestDeploy(t *testing.T) { infra.FileSystem = document.NewDocumentFs() kctl := kubectl.NewKubectl(tf) - tc := TestClient{ + tc := fake.Client{ MockKubectl: func() kubectl.Interface { return kctl }, } @@ -66,7 +57,7 @@ func TestDeploy(t *testing.T) { expectedError error }{ { - client: TestClient{ + client: fake.Client{ MockKubectl: func() kubectl.Interface { return kubectl.NewKubectl(k8sutils. NewMockKubectlFactory(). diff --git a/pkg/k8s/client/client.go b/pkg/k8s/client/client.go index cb0737002..4d758483b 100644 --- a/pkg/k8s/client/client.go +++ b/pkg/k8s/client/client.go @@ -3,6 +3,7 @@ package client import ( "path/filepath" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "opendev.org/airship/airshipctl/pkg/environment" @@ -15,25 +16,72 @@ const ( buffDir = ".airship" ) -// Interface provides an abstraction layer to interactions -// with kubernetes clusters by getting Clientset which includes -// all kubernetes core objects with standard operations and kubectl -// interface that is built on top of kubectl libraries and implements -// such kubectl subcommands as kubectl apply (more will be added) +// Interface provides an abstraction layer to interactions with kubernetes +// clusters by providing a ClientSet which includes all kubernetes core objects +// with standard operations, a DynamicClient which provides interactions with +// loosely typed kubernetes resources, and a Kubectl interface that is built on +// top of kubectl libraries and implements such kubectl subcommands as kubectl +// apply (more will be added) type Interface interface { ClientSet() kubernetes.Interface + DynamicClient() dynamic.Interface + Kubectl() kubectl.Interface } -// Client is implementation of Cluster interface +// Client is an implementation of Interface type Client struct { - kubectl kubectl.Interface - clientset kubernetes.Interface + clientSet kubernetes.Interface + dynamicClient dynamic.Interface + + kubectl kubectl.Interface } -// ClientSet getter for Clientset interface +// Client implements Interface +var _ Interface = &Client{} + +// NewClient returns Cluster interface with Kubectl +// and ClientSet interfaces initialized +func NewClient(settings *environment.AirshipCTLSettings) (Interface, error) { + client := new(Client) + var err error + + f := k8sutils.FactoryFromKubeconfigPath(settings.KubeConfigPath()) + + pathToBufferDir := filepath.Join(filepath.Dir(settings.AirshipConfigPath()), buffDir) + client.kubectl = kubectl.NewKubectl(f).WithBufferDir(pathToBufferDir) + + client.clientSet, err = f.KubernetesClientSet() + if err != nil { + return nil, err + } + + client.dynamicClient, err = f.DynamicClient() + if err != nil { + return nil, err + } + + return client, nil +} + +// ClientSet getter for ClientSet interface func (c *Client) ClientSet() kubernetes.Interface { - return c.clientset + return c.clientSet +} + +// SetClientSet setter for ClientSet interface +func (c *Client) SetClientSet(clientSet kubernetes.Interface) { + c.clientSet = clientSet +} + +// DynamicClient getter for DynamicClient interface +func (c *Client) DynamicClient() dynamic.Interface { + return c.dynamicClient +} + +// SetDynamicClient setter for DynamicClient interface +func (c *Client) SetDynamicClient(dynamicClient dynamic.Interface) { + c.dynamicClient = dynamicClient } // Kubectl getter for Kubectl interface @@ -41,27 +89,6 @@ func (c *Client) Kubectl() kubectl.Interface { return c.kubectl } -// NewClient returns Cluster interface with Kubectl -// and Clientset interfaces initialized -func NewClient(as *environment.AirshipCTLSettings) (Interface, error) { - f := k8sutils.FactoryFromKubeconfigPath(as.KubeConfigPath()) - kctl := kubectl.NewKubectl(f). - WithBufferDir(filepath.Dir(as.AirshipConfigPath()) + buffDir) - clientSet, err := f.KubernetesClientSet() - if err != nil { - return nil, err - } - client := &Client{} - client.SetClientset(clientSet) - client.SetKubectl(kctl) - return client, nil -} - -// SetClientset setter for Clientset interface -func (c *Client) SetClientset(cs kubernetes.Interface) { - c.clientset = cs -} - // SetKubectl setter for Kubectl interface func (c *Client) SetKubectl(kctl kubectl.Interface) { c.kubectl = kctl diff --git a/pkg/k8s/client/fake/fake.go b/pkg/k8s/client/fake/fake.go new file mode 100644 index 000000000..57bdcf6ba --- /dev/null +++ b/pkg/k8s/client/fake/fake.go @@ -0,0 +1,62 @@ +package fake + +import ( + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + + "opendev.org/airship/airshipctl/pkg/k8s/client" + "opendev.org/airship/airshipctl/pkg/k8s/kubectl" +) + +// Client is an implementation of client.Interface meant for testing purposes. +// Its member methods are intended to be implemented on a case-by-case basis +// per test. Examples of implementations can be found with each interface +// method. +type Client struct { + MockClientSet func() kubernetes.Interface + MockDynamicClient func() dynamic.Interface + MockKubectl func() kubectl.Interface +} + +var _ client.Interface = &Client{} + +// ClientSet is used to get a mocked implementation of a kubernetes clientset. +// To initialize the mocked clientset to be returned, the MockClientSet method +// must be implemented, ideally returning a k8s.io/client-go/kubernetes/fake.Clientset. +// +// Example: +// +// testClient := fake.Client { +// MockClientSet: func() kubernetes.Interface { +// return kubernetes_fake.NewSimpleClientset() +// }, +// } +func (c Client) ClientSet() kubernetes.Interface { + return c.MockClientSet() +} + +// DynamicClient is used to get a mocked implementation of a dynamic client. +// To initialize the mocked client to be returned, the MockDynamicClient method +// must be implemented, ideally returning a k8s.io/client-go/dynamic/fake.FakeDynamicClient. +// +// Example: +// Here, scheme is a k8s.io/apimachinery/pkg/runtime.Scheme, possibly created +// via runtime.NewScheme() +// +// testClient := fake.Client { +// MockDynamicClient: func() dynamic.Interface { +// return dynamic_fake.NewSimpleDynamicClient(scheme) +// }, +// } +func (c Client) DynamicClient() dynamic.Interface { + return c.MockDynamicClient() +} + +// Kubectl is used to get a mocked implementation of a Kubectl client. +// To initialize the mocked client to be returned, the MockKubectl method +// must be implemented. +// +// Example: TODO(howell) +func (c Client) Kubectl() kubectl.Interface { + return c.MockKubectl() +}