github.com/verrazzano/verrazzano@v1.7.1/tools/vz/test/helpers/fake_cmd_context.go (about)

     1  // Copyright (c) 2022, 2024, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package helpers
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"github.com/stretchr/testify/assert"
    12  	"io"
    13  	"k8s.io/client-go/discovery"
    14  	discoveryFake "k8s.io/client-go/discovery/fake"
    15  	"testing"
    16  
    17  	"net/http"
    18  	"os"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/spf13/cobra"
    23  	"github.com/verrazzano/verrazzano/tools/vz/pkg/github"
    24  	"k8s.io/cli-runtime/pkg/genericclioptions"
    25  	"k8s.io/client-go/dynamic"
    26  	"k8s.io/client-go/kubernetes"
    27  	"k8s.io/client-go/kubernetes/fake"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  )
    30  
    31  type FakeRootCmdContext struct {
    32  	client        client.Client
    33  	kubeClient    kubernetes.Interface
    34  	dynamicClient dynamic.Interface
    35  	genericclioptions.IOStreams
    36  }
    37  
    38  type FakeRootCmdContextWithFiles struct {
    39  	FakeRootCmdContext
    40  	Out    *os.File
    41  	ErrOut *os.File
    42  }
    43  
    44  // GetOutputStream - return the output stream
    45  func (rc *FakeRootCmdContext) GetOutputStream() io.Writer {
    46  	return rc.IOStreams.Out
    47  }
    48  
    49  // GetErrorStream - return the error stream
    50  func (rc *FakeRootCmdContext) GetErrorStream() io.Writer {
    51  	return rc.IOStreams.ErrOut
    52  }
    53  
    54  // GetInputStream - return the input stream
    55  func (rc *FakeRootCmdContext) GetInputStream() io.Reader {
    56  	return rc.IOStreams.In
    57  }
    58  
    59  // GetClient - return a controller runtime client that supports the schemes used by the CLI
    60  func (rc *FakeRootCmdContext) GetClient(cmd *cobra.Command) (client.Client, error) {
    61  	return rc.client, nil
    62  }
    63  
    64  // GetKubeClient - return a Kubernetes clientset for use with the fake go-client
    65  func (rc *FakeRootCmdContext) GetKubeClient(cmd *cobra.Command) (kubernetes.Interface, error) {
    66  	return rc.kubeClient, nil
    67  }
    68  
    69  // SetClient - set the client
    70  func (rc *FakeRootCmdContext) SetClient(client client.Client) {
    71  	rc.client = client
    72  }
    73  
    74  // SetKubeClient - set the kubeclient
    75  func (rc *FakeRootCmdContext) SetKubeClient(kubeClient kubernetes.Interface) {
    76  	rc.kubeClient = kubeClient
    77  }
    78  
    79  // RoundTripFunc - define the type for the Transport function
    80  type RoundTripFunc func(req *http.Request) *http.Response
    81  
    82  // RoundTrip - define the implementation for the Transport function
    83  func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
    84  	return f(req), nil
    85  }
    86  
    87  // GetHTTPClient - return an HTTP client for testing that always responds with a 200
    88  // and a pre-defined list of releases
    89  func (rc *FakeRootCmdContext) GetHTTPClient() *http.Client {
    90  	// Predefined response for the list of releases
    91  	releaseResponse := []github.ReleaseAsset{
    92  		{
    93  			TagName: "v1.3.0",
    94  		},
    95  		{
    96  			TagName: "v1.2.0",
    97  		},
    98  		{
    99  			TagName: "v1.3.1",
   100  		},
   101  	}
   102  	jsonResp, _ := json.Marshal(releaseResponse)
   103  
   104  	// Predefined response for getting operator.yaml
   105  	jsonOperResp, err := os.ReadFile("../../test/testdata/operator-file-fake.yaml")
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  
   110  	return &http.Client{
   111  		Timeout: time.Second * 30,
   112  		Transport: RoundTripFunc(func(req *http.Request) *http.Response {
   113  			if strings.Contains(req.URL.Path, "/releases/download") {
   114  				return &http.Response{
   115  					StatusCode: http.StatusOK,
   116  					Body:       io.NopCloser(bytes.NewBuffer(jsonOperResp)),
   117  					Header:     http.Header{"Content-Type": {"application/octet-stream"}},
   118  				}
   119  			}
   120  			return &http.Response{
   121  				StatusCode: http.StatusOK,
   122  				Body:       io.NopCloser(bytes.NewBuffer(jsonResp)),
   123  				Header:     http.Header{"Content-Type": {"application/json"}},
   124  			}
   125  		}),
   126  	}
   127  }
   128  
   129  // GetDynamicClient - return a dynamic client for use with the fake go-client
   130  func (rc *FakeRootCmdContext) GetDynamicClient(cmd *cobra.Command) (dynamic.Interface, error) {
   131  	return rc.dynamicClient, nil
   132  }
   133  
   134  // SetDynamicClient - set a dynamic client for use with the fake go-client (used for testing)
   135  func (rc *FakeRootCmdContext) SetDynamicClient(dynClient dynamic.Interface) {
   136  	rc.dynamicClient = dynClient
   137  }
   138  
   139  func NewFakeRootCmdContext(streams genericclioptions.IOStreams) *FakeRootCmdContext {
   140  	return &FakeRootCmdContext{
   141  		IOStreams:  streams,
   142  		kubeClient: fake.NewSimpleClientset(),
   143  	}
   144  }
   145  
   146  // NewFakeRootCmdContextWithFiles creates a newFakeRootCmdContext with the out stream and the error stream set to two separate files
   147  func NewFakeRootCmdContextWithFiles(t *testing.T) *FakeRootCmdContextWithFiles {
   148  	stdOutFile, stdErrfile, err := createStdTempFiles()
   149  	assert.Nil(t, err)
   150  	return &FakeRootCmdContextWithFiles{
   151  		FakeRootCmdContext: FakeRootCmdContext{
   152  			IOStreams:  genericclioptions.IOStreams{In: os.Stdin, Out: stdOutFile, ErrOut: stdErrfile},
   153  			kubeClient: fake.NewSimpleClientset(),
   154  		},
   155  		Out:    stdOutFile,
   156  		ErrOut: stdErrfile,
   157  	}
   158  }
   159  
   160  // CleanUpFakeRootCmdContextWithFiles removes the standard out and standard error files from the local file system
   161  func CleanUpNewFakeRootCmdContextWithFiles(context *FakeRootCmdContextWithFiles) {
   162  	_, err := os.Stat(context.Out.Name())
   163  	if !(errors.Is(err, os.ErrNotExist)) {
   164  		os.Remove(context.Out.Name())
   165  	}
   166  	_, err = os.Stat(context.ErrOut.Name())
   167  	if !(errors.Is(err, os.ErrNotExist)) {
   168  		os.Remove(context.ErrOut.Name())
   169  	}
   170  }
   171  
   172  // createStdTempFiles creates temporary files for stdout and stderr.
   173  func createStdTempFiles() (*os.File, *os.File, error) {
   174  	stdoutFile, err := os.CreateTemp("", "tmpstdout")
   175  	if err != nil {
   176  		return nil, nil, err
   177  	}
   178  
   179  	stderrFile, err := os.CreateTemp("", "tmpstderr")
   180  	if err != nil {
   181  		return nil, nil, err
   182  	}
   183  
   184  	return stdoutFile, stderrFile, nil
   185  }
   186  
   187  func (rc *FakeRootCmdContext) GetDiscoveryClient(cmd *cobra.Command) (discovery.DiscoveryInterface, error) {
   188  	client := rc.kubeClient
   189  	discoveryClient, ok := client.Discovery().(*discoveryFake.FakeDiscovery)
   190  	if !ok {
   191  		return nil, fmt.Errorf("DiscoveryClient was not successfully created")
   192  	}
   193  	return discoveryClient, nil
   194  }
   195  
   196  func (rc *FakeRootCmdContext) VerifyCLIArgsNil(cmd *cobra.Command) error {
   197  	cmd.Args = func(cmd *cobra.Command, args []string) error {
   198  		// In unit-tests the TestName is an arg. So to avoid tests failing because of the TestName
   199  		// we're now checking that the arg list is greater than 1.
   200  		if len(args) > 1 {
   201  			return fmt.Errorf("invalid arguments specified: %s", args)
   202  		}
   203  		return nil
   204  	}
   205  	return nil
   206  }