1
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
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
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
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
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
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