...

Source file src/k8s.io/kubectl/pkg/cmd/rollout/rollout_restart_test.go

Documentation: k8s.io/kubectl/pkg/cmd/rollout

     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 rollout
    18  
    19  import (
    20  	"bytes"
    21  	"io"
    22  	"net/http"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	"k8s.io/client-go/rest/fake"
    33  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    34  	"k8s.io/kubectl/pkg/scheme"
    35  )
    36  
    37  var rolloutRestartGroupVersionEncoder = schema.GroupVersion{Group: "apps", Version: "v1"}
    38  
    39  func TestRolloutRestartOne(t *testing.T) {
    40  	deploymentName := "deployment/nginx-deployment"
    41  	ns := scheme.Codecs.WithoutConversion()
    42  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
    43  
    44  	info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
    45  	encoder := ns.EncoderForVersion(info.Serializer, rolloutRestartGroupVersionEncoder)
    46  	tf.Client = &RolloutRestartRESTClient{
    47  		RESTClient: &fake.RESTClient{
    48  			GroupVersion:         rolloutRestartGroupVersionEncoder,
    49  			NegotiatedSerializer: ns,
    50  			Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
    51  				switch p, m := req.URL.Path, req.Method; {
    52  				case p == "/namespaces/test/deployments/nginx-deployment" && (m == "GET" || m == "PATCH"):
    53  					responseDeployment := &appsv1.Deployment{}
    54  					responseDeployment.Name = deploymentName
    55  					body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployment))))
    56  					return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
    57  				default:
    58  					t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
    59  					return nil, nil
    60  				}
    61  			}),
    62  		},
    63  	}
    64  
    65  	streams, _, buf, _ := genericiooptions.NewTestIOStreams()
    66  	cmd := NewCmdRolloutRestart(tf, streams)
    67  
    68  	cmd.Run(cmd, []string{deploymentName})
    69  	expectedOutput := "deployment.apps/" + deploymentName + " restarted\n"
    70  	if buf.String() != expectedOutput {
    71  		t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
    72  	}
    73  }
    74  
    75  func TestRolloutRestartError(t *testing.T) {
    76  	deploymentName := "deployment/nginx-deployment"
    77  	ns := scheme.Codecs.WithoutConversion()
    78  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
    79  
    80  	info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
    81  	encoder := ns.EncoderForVersion(info.Serializer, rolloutRestartGroupVersionEncoder)
    82  	tf.Client = &RolloutRestartRESTClient{
    83  		RESTClient: &fake.RESTClient{
    84  			GroupVersion:         rolloutRestartGroupVersionEncoder,
    85  			NegotiatedSerializer: ns,
    86  			Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
    87  				switch p, m := req.URL.Path, req.Method; {
    88  				case p == "/namespaces/test/deployments/nginx-deployment" && (m == "GET" || m == "PATCH"):
    89  					responseDeployment := &appsv1.Deployment{}
    90  					responseDeployment.Name = deploymentName
    91  					body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployment))))
    92  					return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
    93  				default:
    94  					t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
    95  					return nil, nil
    96  				}
    97  			}),
    98  		},
    99  	}
   100  
   101  	streams, _, bufOut, _ := genericiooptions.NewTestIOStreams()
   102  	opt := NewRolloutRestartOptions(streams)
   103  	err := opt.Complete(tf, nil, []string{deploymentName})
   104  	assert.NoError(t, err)
   105  	err = opt.Validate()
   106  	assert.NoError(t, err)
   107  	opt.Restarter = func(obj runtime.Object) ([]byte, error) {
   108  		return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), obj)
   109  	}
   110  
   111  	expectedErr := "failed to create patch for nginx-deployment: if restart has already been triggered within the past second, please wait before attempting to trigger another"
   112  	err = opt.RunRestart()
   113  	if err == nil {
   114  		t.Errorf("error expected but not fired")
   115  	}
   116  	if err.Error() != expectedErr {
   117  		t.Errorf("unexpected error fired %v", err)
   118  	}
   119  
   120  	if bufOut.String() != "" {
   121  		t.Errorf("unexpected message")
   122  	}
   123  }
   124  
   125  // Tests that giving selectors with no matching objects shows an error
   126  func TestRolloutRestartSelectorNone(t *testing.T) {
   127  	labelSelector := "app=test"
   128  
   129  	ns := scheme.Codecs.WithoutConversion()
   130  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
   131  
   132  	info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
   133  	encoder := ns.EncoderForVersion(info.Serializer, rolloutRestartGroupVersionEncoder)
   134  	tf.Client = &RolloutRestartRESTClient{
   135  		RESTClient: &fake.RESTClient{
   136  			GroupVersion:         rolloutRestartGroupVersionEncoder,
   137  			NegotiatedSerializer: ns,
   138  			Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   139  				switch p, m, q := req.URL.Path, req.Method, req.URL.Query(); {
   140  				case p == "/namespaces/test/deployments" && m == "GET" && q.Get("labelSelector") == labelSelector:
   141  					// Return an empty list
   142  					responseDeployments := &appsv1.DeploymentList{}
   143  					body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployments))))
   144  					return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
   145  				default:
   146  					t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
   147  					return nil, nil
   148  				}
   149  			}),
   150  		},
   151  	}
   152  
   153  	streams, _, outBuf, errBuf := genericiooptions.NewTestIOStreams()
   154  	cmd := NewCmdRolloutRestart(tf, streams)
   155  	cmd.Flags().Set("selector", "app=test")
   156  
   157  	cmd.Run(cmd, []string{"deployment"})
   158  	if len(outBuf.String()) != 0 {
   159  		t.Errorf("expected empty output, but got: %s", outBuf.String())
   160  	}
   161  	expectedError := "No resources found in test namespace.\n"
   162  	if errBuf.String() != expectedError {
   163  		t.Errorf("expected output: %s, but got: %s", expectedError, errBuf.String())
   164  	}
   165  }
   166  
   167  // Tests that giving selectors with no matching objects shows an error
   168  func TestRolloutRestartSelectorMany(t *testing.T) {
   169  	firstDeployment := appsv1.Deployment{}
   170  	firstDeployment.Name = "nginx-deployment-1"
   171  	secondDeployment := appsv1.Deployment{}
   172  	secondDeployment.Name = "nginx-deployment-2"
   173  	labelSelector := "app=test"
   174  
   175  	ns := scheme.Codecs.WithoutConversion()
   176  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
   177  
   178  	info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
   179  	encoder := ns.EncoderForVersion(info.Serializer, rolloutRestartGroupVersionEncoder)
   180  	tf.Client = &RolloutRestartRESTClient{
   181  		RESTClient: &fake.RESTClient{
   182  			GroupVersion:         rolloutRestartGroupVersionEncoder,
   183  			NegotiatedSerializer: ns,
   184  			Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
   185  				switch p, m, q := req.URL.Path, req.Method, req.URL.Query(); {
   186  				case p == "/namespaces/test/deployments" && m == "GET" && q.Get("labelSelector") == labelSelector:
   187  					// Return the list of 2 deployments
   188  					responseDeployments := &appsv1.DeploymentList{}
   189  					responseDeployments.Items = []appsv1.Deployment{firstDeployment, secondDeployment}
   190  					body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployments))))
   191  					return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
   192  				case (p == "/namespaces/test/deployments/nginx-deployment-1" || p == "/namespaces/test/deployments/nginx-deployment-2") && m == "PATCH":
   193  					// Pick deployment based on path
   194  					responseDeployment := firstDeployment
   195  					if strings.HasSuffix(p, "nginx-deployment-2") {
   196  						responseDeployment = secondDeployment
   197  					}
   198  					body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, &responseDeployment))))
   199  					return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
   200  				default:
   201  					t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
   202  					return nil, nil
   203  				}
   204  			}),
   205  		},
   206  	}
   207  
   208  	streams, _, buf, _ := genericiooptions.NewTestIOStreams()
   209  	cmd := NewCmdRolloutRestart(tf, streams)
   210  	cmd.Flags().Set("selector", labelSelector)
   211  
   212  	cmd.Run(cmd, []string{"deployment"})
   213  	expectedOutput := "deployment.apps/" + firstDeployment.Name + " restarted\ndeployment.apps/" + secondDeployment.Name + " restarted\n"
   214  	if buf.String() != expectedOutput {
   215  		t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
   216  	}
   217  }
   218  
   219  type RolloutRestartRESTClient struct {
   220  	*fake.RESTClient
   221  }
   222  

View as plain text