airshipctl/testutil/k8sutils/mock_kubectl_factory.go
Kostiantyn Kalynovskyi d588c73e38 [#20] Add kubectl apply wrapper package
The wrapper is called ApplyAdapter and is a struct, that has Apply(..)
method and some setters that allow to control kubectl apply behaviour

Addapter is expected to be used through Apply(..) function, which takes
slice of document.Document interface objects, writes them out to
temporary file system, from where they are picked up by kubectl Apply
module, and delivered to kubernetes cluster. The decision to use
temporary file system is based on the fact, that in current state
kubectl project currently only works with actual files, and ignores
io.Streams object, that is part of ApplyOptions struct, so it is
currently the only way to use it.

Change-Id: Idc5d79794149c00198f420d76cf9aa3b5264946e
2020-02-20 20:58:31 +00:00

216 lines
8.3 KiB
Go

package k8sutils
import (
"bytes"
"io/ioutil"
"net/http"
"os"
"testing"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/client-go/tools/clientcmd"
kubeconfig "k8s.io/client-go/tools/clientcmd/api"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/openapi"
"k8s.io/kubectl/pkg/validation"
)
// MockKubectlFactory implements Factory interface for testing purposes.
type MockKubectlFactory struct {
MockToDiscoveryClient func() (discovery.CachedDiscoveryInterface, error)
MockDynamicClient func() (dynamic.Interface, error)
MockOpenAPISchema func() (openapi.Resources, error)
MockValidator func() (validation.Schema, error)
MockToRESTMapper func() (meta.RESTMapper, error)
MockToRESTConfig func() (*rest.Config, error)
MockNewBuilder func() *resource.Builder
MockToRawKubeConfigLoader func() clientcmd.ClientConfig
MockClientForMapping func() (resource.RESTClient, error)
KubeConfig kubeconfig.Config
genericclioptions.ConfigFlags
cmdutil.Factory
}
// ToDiscoveryClient implements Factory interface
func (f *MockKubectlFactory) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return f.MockToDiscoveryClient()
}
func (f *MockKubectlFactory) DynamicClient() (dynamic.Interface, error) { return f.MockDynamicClient() }
func (f *MockKubectlFactory) OpenAPISchema() (openapi.Resources, error) { return f.MockOpenAPISchema() }
func (f *MockKubectlFactory) Validator(validate bool) (validation.Schema, error) {
return f.MockValidator()
}
func (f *MockKubectlFactory) ToRESTMapper() (meta.RESTMapper, error) { return f.MockToRESTMapper() }
func (f *MockKubectlFactory) ToRESTConfig() (*rest.Config, error) { return f.MockToRESTConfig() }
func (f *MockKubectlFactory) NewBuilder() *resource.Builder { return f.MockNewBuilder() }
func (f *MockKubectlFactory) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return f.MockToRawKubeConfigLoader()
}
func (f *MockKubectlFactory) ClientForMapping(*meta.RESTMapping) (resource.RESTClient, error) {
return f.MockClientForMapping()
}
func (f *MockKubectlFactory) WithToDiscoveryClientByError(d dynamic.Interface, err error) *MockKubectlFactory {
f.MockDynamicClient = func() (dynamic.Interface, error) { return d, err }
return f
}
func (f *MockKubectlFactory) WithOpenAPISchemaByError(r openapi.Resources, err error) *MockKubectlFactory {
f.MockOpenAPISchema = func() (openapi.Resources, error) { return r, err }
return f
}
func (f *MockKubectlFactory) WithDynamicClientByError(d discovery.CachedDiscoveryInterface,
err error) *MockKubectlFactory {
f.MockToDiscoveryClient = func() (discovery.CachedDiscoveryInterface, error) { return d, err }
return f
}
func (f *MockKubectlFactory) WithValidatorByError(v validation.Schema, err error) *MockKubectlFactory {
f.MockValidator = func() (validation.Schema, error) { return v, err }
return f
}
func (f *MockKubectlFactory) WithToRESTMapperByError(r meta.RESTMapper, err error) *MockKubectlFactory {
f.MockToRESTMapper = func() (meta.RESTMapper, error) { return r, err }
return f
}
func (f *MockKubectlFactory) WithToRESTConfigByError(r *rest.Config, err error) *MockKubectlFactory {
f.MockToRESTConfig = func() (*rest.Config, error) { return r, err }
return f
}
func (f *MockKubectlFactory) WithNewBuilderByError(r *resource.Builder) *MockKubectlFactory {
f.MockNewBuilder = func() *resource.Builder { return r }
return f
}
func (f *MockKubectlFactory) WithToRawKubeConfigLoaderByError(c clientcmd.ClientConfig) *MockKubectlFactory {
f.MockToRawKubeConfigLoader = func() clientcmd.ClientConfig { return c }
return f
}
func (f *MockKubectlFactory) WithClientForMappingByError(r resource.RESTClient, err error) *MockKubectlFactory {
f.MockClientForMapping = func() (resource.RESTClient, error) { return r, err }
return f
}
func NewMockKubectlFactory() *MockKubectlFactory {
return &MockKubectlFactory{MockDynamicClient: func() (dynamic.Interface, error) { return nil, nil },
MockToDiscoveryClient: func() (discovery.CachedDiscoveryInterface, error) { return nil, nil },
MockOpenAPISchema: func() (openapi.Resources, error) { return nil, nil },
MockValidator: func() (validation.Schema, error) { return nil, nil },
MockToRESTMapper: func() (meta.RESTMapper, error) { return nil, nil },
MockToRESTConfig: func() (*rest.Config, error) { return nil, nil },
MockNewBuilder: func() *resource.Builder { return nil },
MockToRawKubeConfigLoader: func() clientcmd.ClientConfig { return nil },
MockClientForMapping: func() (resource.RESTClient, error) { return nil, nil },
}
}
type MockClientConfig struct {
clientcmd.DirectClientConfig
MockNamespace func() (string, bool, error)
}
func (c MockClientConfig) Namespace() (string, bool, error) { return c.MockNamespace() }
func (c *MockClientConfig) WithNamespace(s string, b bool, err error) *MockClientConfig {
c.MockNamespace = func() (string, bool, error) { return s, b, err }
return c
}
func NewMockClientConfig() *MockClientConfig {
return &MockClientConfig{
MockNamespace: func() (string, bool, error) { return "test", false, nil },
}
}
func NewFakeFactoryForRC(t *testing.T, filenameRC string) *cmdtesting.TestFactory {
c := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
f := cmdtesting.NewTestFactory().WithNamespace("test")
f.ClientConfigVal = cmdtesting.DefaultClientConfig()
pathRC := "/namespaces/test/replicationcontrollers/test-rc"
get := "GET"
_, rcBytes := readReplicationController(t, filenameRC, c)
f.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == pathRC && m == get:
bodyRC := ioutil.NopCloser(bytes.NewReader(rcBytes))
return &http.Response{StatusCode: http.StatusOK,
Header: cmdtesting.DefaultHeader(),
Body: bodyRC}, nil
case p == "/namespaces/test/replicationcontrollers" && m == get:
bodyRC := ioutil.NopCloser(bytes.NewReader(rcBytes))
return &http.Response{StatusCode: http.StatusOK,
Header: cmdtesting.DefaultHeader(),
Body: bodyRC}, nil
case p == "/namespaces/test/replicationcontrollers/no-match" && m == get:
return &http.Response{StatusCode: http.StatusNotFound,
Header: cmdtesting.DefaultHeader(),
Body: cmdtesting.ObjBody(c, &corev1.Pod{})}, nil
case p == "/api/v1/namespaces/test" && m == get:
return &http.Response{StatusCode: http.StatusOK,
Header: cmdtesting.DefaultHeader(),
Body: cmdtesting.ObjBody(c, &corev1.Namespace{})}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
return f
}
// Below functions are taken from Kubectl library.
// https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/apply/apply_test.go
func readReplicationController(t *testing.T, filenameRC string, c runtime.Codec) (string, []byte) {
t.Helper()
rcObj := readReplicationControllerFromFile(t, filenameRC, c)
metaAccessor, err := meta.Accessor(rcObj)
require.NoError(t, err, "Could not read replcation controller")
rcBytes, err := runtime.Encode(c, rcObj)
require.NoError(t, err, "Could not read replcation controller")
return metaAccessor.GetName(), rcBytes
}
func readReplicationControllerFromFile(t *testing.T,
filename string, c runtime.Decoder) *corev1.ReplicationController {
data := readBytesFromFile(t, filename)
rc := corev1.ReplicationController{}
require.NoError(t, runtime.DecodeInto(c, data, &rc), "Could not read replcation controller")
return &rc
}
func readBytesFromFile(t *testing.T, filename string) []byte {
file, err := os.Open(filename)
require.NoError(t, err, "Could not read file")
defer file.Close()
data, err := ioutil.ReadAll(file)
require.NoError(t, err, "Could not read file")
return data
}