sigs.k8s.io/cluster-api@v1.7.1/internal/runtime/client/fake/fake_client.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package fake is used to help with testing functions that need a fake RuntimeClient.
    18  package fake
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  
    24  	"github.com/pkg/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  
    27  	runtimev1 "sigs.k8s.io/cluster-api/exp/runtime/api/v1alpha1"
    28  	runtimecatalog "sigs.k8s.io/cluster-api/exp/runtime/catalog"
    29  	runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
    30  	runtimeclient "sigs.k8s.io/cluster-api/internal/runtime/client"
    31  )
    32  
    33  // RuntimeClientBuilder is used to build a fake runtime client.
    34  type RuntimeClientBuilder struct {
    35  	ready            bool
    36  	catalog          *runtimecatalog.Catalog
    37  	callAllResponses map[runtimecatalog.GroupVersionHook]runtimehooksv1.ResponseObject
    38  	callResponses    map[string]runtimehooksv1.ResponseObject
    39  }
    40  
    41  // NewRuntimeClientBuilder returns a new builder for the fake runtime client.
    42  func NewRuntimeClientBuilder() *RuntimeClientBuilder {
    43  	return &RuntimeClientBuilder{}
    44  }
    45  
    46  // WithCatalog can be use the provided catalog in the fake runtime client.
    47  func (f *RuntimeClientBuilder) WithCatalog(catalog *runtimecatalog.Catalog) *RuntimeClientBuilder {
    48  	f.catalog = catalog
    49  	return f
    50  }
    51  
    52  // WithCallAllExtensionResponses can be used to dictate the responses for CallAllExtensions.
    53  func (f *RuntimeClientBuilder) WithCallAllExtensionResponses(responses map[runtimecatalog.GroupVersionHook]runtimehooksv1.ResponseObject) *RuntimeClientBuilder {
    54  	f.callAllResponses = responses
    55  	return f
    56  }
    57  
    58  // WithCallExtensionResponses can be used to dictate the responses for CallExtension.
    59  func (f *RuntimeClientBuilder) WithCallExtensionResponses(responses map[string]runtimehooksv1.ResponseObject) *RuntimeClientBuilder {
    60  	f.callResponses = responses
    61  	return f
    62  }
    63  
    64  // MarkReady can be used to mark the fake runtime client as either ready or not ready.
    65  func (f *RuntimeClientBuilder) MarkReady(ready bool) *RuntimeClientBuilder {
    66  	f.ready = ready
    67  	return f
    68  }
    69  
    70  // Build returns the fake runtime client.
    71  func (f *RuntimeClientBuilder) Build() *RuntimeClient {
    72  	return &RuntimeClient{
    73  		isReady:          f.ready,
    74  		callAllResponses: f.callAllResponses,
    75  		callResponses:    f.callResponses,
    76  		catalog:          f.catalog,
    77  		callAllTracker:   map[string]int{},
    78  	}
    79  }
    80  
    81  var _ runtimeclient.Client = &RuntimeClient{}
    82  
    83  // RuntimeClient is a fake implementation of runtimeclient.Client.
    84  type RuntimeClient struct {
    85  	isReady          bool
    86  	catalog          *runtimecatalog.Catalog
    87  	callAllResponses map[runtimecatalog.GroupVersionHook]runtimehooksv1.ResponseObject
    88  	callResponses    map[string]runtimehooksv1.ResponseObject
    89  
    90  	callAllTracker map[string]int
    91  }
    92  
    93  // CallAllExtensions implements Client.
    94  func (fc *RuntimeClient) CallAllExtensions(ctx context.Context, hook runtimecatalog.Hook, _ metav1.Object, _ runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error {
    95  	defer func() {
    96  		fc.callAllTracker[runtimecatalog.HookName(hook)]++
    97  	}()
    98  
    99  	gvh, err := fc.catalog.GroupVersionHook(hook)
   100  	if err != nil {
   101  		return errors.Wrap(err, "failed to compute GVH")
   102  	}
   103  
   104  	expectedResponse, ok := fc.callAllResponses[gvh]
   105  	if !ok {
   106  		// This should actually panic because an error here would mean a mistake in the test setup.
   107  		panic(fmt.Sprintf("test response not available hook for %q", gvh))
   108  	}
   109  
   110  	if err := fc.catalog.Convert(expectedResponse, response, ctx); err != nil {
   111  		// This should actually panic because an error here would mean a mistake in the test setup.
   112  		panic("cannot update response")
   113  	}
   114  
   115  	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
   116  		return errors.Errorf("runtime hook %q failed", gvh)
   117  	}
   118  	return nil
   119  }
   120  
   121  // CallExtension implements Client.
   122  func (fc *RuntimeClient) CallExtension(ctx context.Context, _ runtimecatalog.Hook, _ metav1.Object, name string, _ runtimehooksv1.RequestObject, response runtimehooksv1.ResponseObject) error {
   123  	expectedResponse, ok := fc.callResponses[name]
   124  	if !ok {
   125  		// This should actually panic because an error here would mean a mistake in the test setup.
   126  		panic(fmt.Sprintf("test response not available for extension %q", name))
   127  	}
   128  
   129  	if err := fc.catalog.Convert(expectedResponse, response, ctx); err != nil {
   130  		// This should actually panic because an error here would mean a mistake in the test setup.
   131  		panic("cannot update response")
   132  	}
   133  
   134  	// If the received response is a failure then return an error.
   135  	if response.GetStatus() == runtimehooksv1.ResponseStatusFailure {
   136  		return errors.Errorf("ExtensionHandler %s failed with message %s", name, response.GetMessage())
   137  	}
   138  	return nil
   139  }
   140  
   141  // Discover implements Client.
   142  func (fc *RuntimeClient) Discover(context.Context, *runtimev1.ExtensionConfig) (*runtimev1.ExtensionConfig, error) {
   143  	panic("unimplemented")
   144  }
   145  
   146  // IsReady implements Client.
   147  func (fc *RuntimeClient) IsReady() bool {
   148  	return fc.isReady
   149  }
   150  
   151  // Register implements Client.
   152  func (fc *RuntimeClient) Register(_ *runtimev1.ExtensionConfig) error {
   153  	panic("unimplemented")
   154  }
   155  
   156  // Unregister implements Client.
   157  func (fc *RuntimeClient) Unregister(_ *runtimev1.ExtensionConfig) error {
   158  	panic("unimplemented")
   159  }
   160  
   161  // WarmUp implements Client.
   162  func (fc *RuntimeClient) WarmUp(_ *runtimev1.ExtensionConfigList) error {
   163  	panic("unimplemented")
   164  }
   165  
   166  // CallAllCount return the number of times a hook was called.
   167  func (fc *RuntimeClient) CallAllCount(hook runtimecatalog.Hook) int {
   168  	return fc.callAllTracker[runtimecatalog.HookName(hook)]
   169  }