Create fake data for rest mapper running in Kubernetes

Viewed 369

I can run the following code when providing a kubeconfig. Doing so I was able to get the value of gr:

func (o *ApplyOptions) RestMapper() (meta.RESTMapper, error) {
    gr, err := restmapper.GetAPIGroupResources(o.discoveryClient)
    if err != nil {
        return nil, err
    }
    mapper := restmapper.NewDiscoveryRESTMapper(gr)
    return mapper, nil
}

Then I have initiated the following code:

var kubeconfig *string

kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")

flag.Parse()

// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
    panic(err.Error())
}

dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
    panic(err.Error())
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
    panic(err.Error())
}

applyOptions := apply.NewApplyOptions(dynamicClient, discoveryClient)
if err := applyOptions.Apply(context.Background(), []byte(applyStr)); err != nil {
    log.Fatalf("apply error: %v", err)
}

Now I use it for unit test and gr is empty (from restmapper.GetAPIGroupResources(o.discoveryClient)) above when debug it, , how can I add some fake to make it works also from the test ?

var _ = DescribeTable(“test”, func(applyOptions *ApplyOptions, filename string, isExpectedErr bool, expectedErrMsg string) {


    applyOptions = ApplyOptions{
        discoveryClient: clientset.Discovery() ,
        //discoveryClient: &k8sfake.Clientset,
        dynamicClient:   dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()),

    }

   // Here I need to initiate some fake data
    restmapper, err := applyOptions.RestMapper()
    if err != nil {
        Fail(err.Error())
    }

The error which I get is "k8s.io/apimachinery/pkg/api/**meta.NoKindMatchError**"

Update

To make it clear: I am trying to create a unit test for this function with kind: deployment and I got the above error.

1 Answers

One possible approach is to abstract the restmapper away, this will make ApplyOptions to rely on abstractions, thus you can mock it. Assuming you are unit testing.

We can call it RESTMapperDiscovery this type will encapsulate the restmapper capabilities used by ApplyOptions. It is basically a wrapper.

type RESTMapperDiscovery struct {}
func (RESTMapperDiscovery) FromGroupResources(cl discovery.DiscoveryInterface) (meta.RESTMapper, error)

FromGroupResources will concretely implement the code currently part of func (o *ApplyOptions) RestMapper().

To achieve polymorphism we just need an interface.

type DiscoveryRESTMapper interface {
    FromGroupResources(cl discovery.DiscoveryInterface) (meta.RESTMapper, error)
}

At this stage ApplyOptions will also depend on DiscoveryRESTMapper.

type ApplyOptions struct {
    //... other fields
    restMapper DiscoveryRESTMapper
}
func (o *ApplyOptions) RestMapper() (meta.RESTMapper, error) {
    return o.restMapper.FromGroupResources(o.discoveryClient)
}

When testing, you can use a type which fulfils that interface and will return some fake data. meta.RESTMapper is an interface as well, thereof your fake data has to implement.

type mockRESTMapper struct {}
func (mockRESTMapper) FromGroupResources(cl discovery.DiscoveryInterface) (meta.RESTMapper, error) {
    // return the fake meta.RESTMapper
}

applyOptions = &ApplyOptions{
    discoveryClient: clientset.Discovery() ,
    dynamicClient:   dynamicfake.NewSimpleDynamicClient(runtime.NewScheme()),
    restMapper: mockRESTMapper{},

}

// here you have the fake data
restmapper, err := applyOptions.RestMapper()
if err != nil {
    Fail(err.Error())
}

This approach relies on composition and interfaces to decouple external dependencies. k8s apis as restmapper seems to rely on the same mechanism, thus you could also mock all that from the currently dependencies of ApplyOptions. The trade-off here is your solution being tightly coupled with k8s api, this could result in break changes with restmapper versions and/or maintenance burden.

Related