...

Source file src/k8s.io/cli-runtime/pkg/genericclioptions/command_headers_test.go

Documentation: k8s.io/cli-runtime/pkg/genericclioptions

     1  /*
     2  Copyright 2021 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 genericclioptions
    18  
    19  import (
    20  	"net/http"
    21  	"testing"
    22  
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  var kubectlCmd = &cobra.Command{Use: "kubectl"}
    27  var applyCmd = &cobra.Command{Use: "apply"}
    28  var createCmd = &cobra.Command{Use: "create"}
    29  var secretCmd = &cobra.Command{Use: "secret"}
    30  var genericCmd = &cobra.Command{Use: "generic"}
    31  var authCmd = &cobra.Command{Use: "auth"}
    32  var reconcileCmd = &cobra.Command{Use: "reconcile"}
    33  
    34  func TestParseCommandHeaders(t *testing.T) {
    35  	tests := map[string]struct {
    36  		// Ordering is important; each subsequent command is added as a subcommand
    37  		// of the previous command.
    38  		commands []*cobra.Command
    39  		// Headers which should be present; but other headers may exist
    40  		expectedHeaders map[string]string
    41  	}{
    42  		"Single kubectl command example": {
    43  			commands: []*cobra.Command{kubectlCmd},
    44  			expectedHeaders: map[string]string{
    45  				kubectlCommandHeader: "kubectl",
    46  			},
    47  		},
    48  		"Simple kubectl apply example": {
    49  			commands: []*cobra.Command{kubectlCmd, applyCmd},
    50  			expectedHeaders: map[string]string{
    51  				kubectlCommandHeader: "kubectl apply",
    52  			},
    53  		},
    54  		"Kubectl auth reconcile example": {
    55  			commands: []*cobra.Command{kubectlCmd, authCmd, reconcileCmd},
    56  			expectedHeaders: map[string]string{
    57  				kubectlCommandHeader: "kubectl auth reconcile",
    58  			},
    59  		},
    60  		"Long kubectl create secret generic example": {
    61  			commands: []*cobra.Command{kubectlCmd, createCmd, secretCmd, genericCmd},
    62  			expectedHeaders: map[string]string{
    63  				kubectlCommandHeader: "kubectl create secret generic",
    64  			},
    65  		},
    66  	}
    67  
    68  	for name, tc := range tests {
    69  		t.Run(name, func(t *testing.T) {
    70  			rootCmd := buildCommandChain(tc.commands)
    71  			ch := &CommandHeaderRoundTripper{}
    72  			ch.ParseCommandHeaders(rootCmd, []string{})
    73  			// Unique session ID header should always be present.
    74  			if _, found := ch.Headers[kubectlSessionHeader]; !found {
    75  				t.Errorf("expected kubectl session header (%s) is missing", kubectlSessionHeader)
    76  			}
    77  			// All expected headers must be present; but there may be extras.
    78  			for key, expectedValue := range tc.expectedHeaders {
    79  				actualValue, found := ch.Headers[key]
    80  				if found {
    81  					if expectedValue != actualValue {
    82  						t.Errorf("expected header value (%s), got (%s)", expectedValue, actualValue)
    83  					}
    84  				} else {
    85  					t.Errorf("expected header (%s) not found", key)
    86  				}
    87  			}
    88  		})
    89  	}
    90  }
    91  
    92  // Builds a hierarchy of commands in order from the passed slice of commands,
    93  // by adding each subsequent command as a child of the previous command,
    94  // returning the last leaf command.
    95  func buildCommandChain(commands []*cobra.Command) *cobra.Command {
    96  	var currCmd *cobra.Command
    97  	if len(commands) > 0 {
    98  		currCmd = commands[0]
    99  	}
   100  	for i := 1; i < len(commands); i++ {
   101  		cmd := commands[i]
   102  		currCmd.AddCommand(cmd)
   103  		currCmd = cmd
   104  	}
   105  	return currCmd
   106  }
   107  
   108  // Tests that the CancelRequest function is propogated to the wrapped Delegate
   109  // RoundTripper; but only if the Delegate implements the CancelRequest function.
   110  func TestCancelRequest(t *testing.T) {
   111  	tests := map[string]struct {
   112  		delegate  http.RoundTripper
   113  		cancelled bool
   114  	}{
   115  		"CancelRequest propagated to delegate": {
   116  			delegate:  &cancellableRoundTripper{},
   117  			cancelled: true,
   118  		},
   119  		"CancelRequest not propagated to delegate": {
   120  			delegate:  &nonCancellableRoundTripper{},
   121  			cancelled: false,
   122  		},
   123  	}
   124  
   125  	for name, tc := range tests {
   126  		t.Run(name, func(t *testing.T) {
   127  			rt := &CommandHeaderRoundTripper{
   128  				Delegate: tc.delegate,
   129  			}
   130  			req := http.Request{}
   131  			rt.CancelRequest(&req)
   132  			if tc.cancelled != req.Close {
   133  				t.Errorf("expected RoundTripper cancel (%v), got (%v)", tc.cancelled, req.Close)
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  // Test RoundTripper with CancelRequest function.
   140  type cancellableRoundTripper struct{}
   141  
   142  func (rtc *cancellableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
   143  	return nil, nil
   144  }
   145  
   146  func (rtc *cancellableRoundTripper) CancelRequest(req *http.Request) {
   147  	req.Close = true
   148  }
   149  
   150  // Test RoundTripper without CancelRequest function.
   151  type nonCancellableRoundTripper struct{}
   152  
   153  func (rtc *nonCancellableRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
   154  	return nil, nil
   155  }
   156  

View as plain text