github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+incompatible/pkg/tiller/release_server_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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 tiller 18 19 import ( 20 "errors" 21 "io" 22 "os" 23 "regexp" 24 "testing" 25 26 "github.com/golang/protobuf/ptypes/timestamp" 27 "golang.org/x/net/context" 28 grpc "google.golang.org/grpc" 29 "google.golang.org/grpc/metadata" 30 "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" 31 32 "k8s.io/helm/pkg/helm" 33 "k8s.io/helm/pkg/proto/hapi/chart" 34 "k8s.io/helm/pkg/proto/hapi/release" 35 "k8s.io/helm/pkg/proto/hapi/services" 36 "k8s.io/helm/pkg/storage" 37 "k8s.io/helm/pkg/storage/driver" 38 "k8s.io/helm/pkg/tiller/environment" 39 ) 40 41 const notesText = "my notes here" 42 43 var manifestWithHook = `apiVersion: v1 44 kind: ConfigMap 45 metadata: 46 name: test-cm 47 annotations: 48 "helm.sh/hook": post-install,pre-delete 49 data: 50 name: value` 51 52 var manifestWithTestHook = ` 53 apiVersion: v1 54 kind: Pod 55 metadata: 56 name: finding-nemo, 57 annotations: 58 "helm.sh/hook": test-success 59 spec: 60 containers: 61 - name: nemo-test 62 image: fake-image 63 cmd: fake-command 64 ` 65 66 var manifestWithKeep = `apiVersion: v1 67 kind: ConfigMap 68 metadata: 69 name: test-cm-keep 70 annotations: 71 "helm.sh/resource-policy": keep 72 data: 73 name: value 74 ` 75 76 var manifestWithUpgradeHooks = `apiVersion: v1 77 kind: ConfigMap 78 metadata: 79 name: test-cm 80 annotations: 81 "helm.sh/hook": post-upgrade,pre-upgrade 82 data: 83 name: value` 84 85 var manifestWithRollbackHooks = `apiVersion: v1 86 kind: ConfigMap 87 metadata: 88 name: test-cm 89 annotations: 90 "helm.sh/hook": post-rollback,pre-rollback 91 data: 92 name: value 93 ` 94 95 func rsFixture() *ReleaseServer { 96 clientset := fake.NewSimpleClientset() 97 return &ReleaseServer{ 98 ReleaseModule: &LocalReleaseModule{ 99 clientset: clientset, 100 }, 101 env: MockEnvironment(), 102 clientset: clientset, 103 Log: func(_ string, _ ...interface{}) {}, 104 } 105 } 106 107 // chartStub creates a fully stubbed out chart. 108 func chartStub() *chart.Chart { 109 return &chart.Chart{ 110 // TODO: This should be more complete. 111 Metadata: &chart.Metadata{ 112 Name: "hello", 113 }, 114 // This adds basic templates, partials, and hooks. 115 Templates: []*chart.Template{ 116 {Name: "templates/hello", Data: []byte("hello: world")}, 117 {Name: "templates/goodbye", Data: []byte("goodbye: world")}, 118 {Name: "templates/empty", Data: []byte("")}, 119 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, 120 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, 121 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 122 }, 123 } 124 } 125 126 // releaseStub creates a release stub, complete with the chartStub as its chart. 127 func releaseStub() *release.Release { 128 return namedReleaseStub("angry-panda", release.Status_DEPLOYED) 129 } 130 131 func namedReleaseStub(name string, status release.Status_Code) *release.Release { 132 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 133 return &release.Release{ 134 Name: name, 135 Info: &release.Info{ 136 FirstDeployed: &date, 137 LastDeployed: &date, 138 Status: &release.Status{Code: status}, 139 Description: "Named Release Stub", 140 }, 141 Chart: chartStub(), 142 Config: &chart.Config{Raw: `name: value`}, 143 Version: 1, 144 Hooks: []*release.Hook{ 145 { 146 Name: "test-cm", 147 Kind: "ConfigMap", 148 Path: "test-cm", 149 Manifest: manifestWithHook, 150 Events: []release.Hook_Event{ 151 release.Hook_POST_INSTALL, 152 release.Hook_PRE_DELETE, 153 }, 154 }, 155 { 156 Name: "finding-nemo", 157 Kind: "Pod", 158 Path: "finding-nemo", 159 Manifest: manifestWithTestHook, 160 Events: []release.Hook_Event{ 161 release.Hook_RELEASE_TEST_SUCCESS, 162 }, 163 }, 164 }, 165 } 166 } 167 168 func upgradeReleaseVersion(rel *release.Release) *release.Release { 169 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 170 171 rel.Info.Status.Code = release.Status_SUPERSEDED 172 return &release.Release{ 173 Name: rel.Name, 174 Info: &release.Info{ 175 FirstDeployed: rel.Info.FirstDeployed, 176 LastDeployed: &date, 177 Status: &release.Status{Code: release.Status_DEPLOYED}, 178 }, 179 Chart: rel.Chart, 180 Config: rel.Config, 181 Version: rel.Version + 1, 182 } 183 } 184 185 func TestValidName(t *testing.T) { 186 for name, valid := range map[string]error{ 187 "nina pinta santa-maria": errInvalidName, 188 "nina-pinta-santa-maria": nil, 189 "-nina": errInvalidName, 190 "pinta-": errInvalidName, 191 "santa-maria": nil, 192 "niƱa": errInvalidName, 193 "...": errInvalidName, 194 "pinta...": errInvalidName, 195 "santa...maria": nil, 196 "": errMissingRelease, 197 " ": errInvalidName, 198 ".nina.": errInvalidName, 199 "nina.pinta": nil, 200 "abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcd": errInvalidName, 201 } { 202 if valid != validateReleaseName(name) { 203 t.Errorf("Expected %q to be %t", name, valid) 204 } 205 } 206 } 207 208 func TestGetVersionSet(t *testing.T) { 209 rs := rsFixture() 210 vs, err := GetVersionSet(rs.clientset.Discovery()) 211 if err != nil { 212 t.Error(err) 213 } 214 if !vs.Has("v1") { 215 t.Errorf("Expected supported versions to at least include v1.") 216 } 217 if vs.Has("nosuchversion/v1") { 218 t.Error("Non-existent version is reported found.") 219 } 220 } 221 222 func TestUniqName(t *testing.T) { 223 rs := rsFixture() 224 225 rel1 := releaseStub() 226 rel2 := releaseStub() 227 rel2.Name = "happy-panda" 228 rel2.Info.Status.Code = release.Status_DELETED 229 230 rs.env.Releases.Create(rel1) 231 rs.env.Releases.Create(rel2) 232 233 tests := []struct { 234 name string 235 expect string 236 reuse bool 237 err bool 238 }{ 239 {"first", "first", false, false}, 240 {"", "[a-z]+-[a-z]+", false, false}, 241 {"angry-panda", "", false, true}, 242 {"happy-panda", "", false, true}, 243 {"happy-panda", "happy-panda", true, false}, 244 {"hungry-hungry-hungry-hungry-hungry-hungry-hungry-hungry-hippos", "", true, true}, // Exceeds max name length 245 } 246 247 for _, tt := range tests { 248 u, err := rs.uniqName(tt.name, tt.reuse) 249 if err != nil { 250 if tt.err { 251 continue 252 } 253 t.Fatal(err) 254 } 255 if tt.err { 256 t.Errorf("Expected an error for %q", tt.name) 257 } 258 if match, err := regexp.MatchString(tt.expect, u); err != nil { 259 t.Fatal(err) 260 } else if !match { 261 t.Errorf("Expected %q to match %q", u, tt.expect) 262 } 263 } 264 } 265 266 func releaseWithKeepStub(rlsName string) *release.Release { 267 ch := &chart.Chart{ 268 Metadata: &chart.Metadata{ 269 Name: "bunnychart", 270 }, 271 Templates: []*chart.Template{ 272 {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, 273 }, 274 } 275 276 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 277 return &release.Release{ 278 Name: rlsName, 279 Info: &release.Info{ 280 FirstDeployed: &date, 281 LastDeployed: &date, 282 Status: &release.Status{Code: release.Status_DEPLOYED}, 283 }, 284 Chart: ch, 285 Config: &chart.Config{Raw: `name: value`}, 286 Version: 1, 287 Manifest: manifestWithKeep, 288 } 289 } 290 291 func MockEnvironment() *environment.Environment { 292 e := environment.New() 293 e.Releases = storage.Init(driver.NewMemory()) 294 e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} 295 return e 296 } 297 298 func newUpdateFailingKubeClient() *updateFailingKubeClient { 299 return &updateFailingKubeClient{ 300 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 301 } 302 303 } 304 305 type updateFailingKubeClient struct { 306 environment.PrintingKubeClient 307 } 308 309 func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { 310 return errors.New("Failed update in kube client") 311 } 312 313 func newHookFailingKubeClient() *hookFailingKubeClient { 314 return &hookFailingKubeClient{ 315 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 316 } 317 } 318 319 type hookFailingKubeClient struct { 320 environment.PrintingKubeClient 321 } 322 323 func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 324 return errors.New("Failed watch") 325 } 326 327 type mockListServer struct { 328 val *services.ListReleasesResponse 329 } 330 331 func (l *mockListServer) Send(res *services.ListReleasesResponse) error { 332 l.val = res 333 return nil 334 } 335 336 func (l *mockListServer) Context() context.Context { return helm.NewContext() } 337 func (l *mockListServer) SendMsg(v interface{}) error { return nil } 338 func (l *mockListServer) RecvMsg(v interface{}) error { return nil } 339 func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } 340 func (l *mockListServer) SetTrailer(m metadata.MD) {} 341 func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } 342 343 type mockRunReleaseTestServer struct { 344 stream grpc.ServerStream 345 } 346 347 func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { 348 return nil 349 } 350 func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } 351 func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } 352 func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} 353 func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } 354 func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } 355 func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() }