github.com/zoumo/helm@v2.5.0+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]bool{ 187 "nina pinta santa-maria": false, 188 "nina-pinta-santa-maria": true, 189 "-nina": false, 190 "pinta-": false, 191 "santa-maria": true, 192 "niƱa": false, 193 "...": false, 194 "pinta...": false, 195 "santa...maria": true, 196 "": false, 197 " ": false, 198 ".nina.": false, 199 "nina.pinta": true, 200 } { 201 if valid != ValidName.MatchString(name) { 202 t.Errorf("Expected %q to be %t", name, valid) 203 } 204 } 205 } 206 207 func TestGetVersionSet(t *testing.T) { 208 rs := rsFixture() 209 vs, err := GetVersionSet(rs.clientset.Discovery()) 210 if err != nil { 211 t.Error(err) 212 } 213 if !vs.Has("v1") { 214 t.Errorf("Expected supported versions to at least include v1.") 215 } 216 if vs.Has("nosuchversion/v1") { 217 t.Error("Non-existent version is reported found.") 218 } 219 } 220 221 func TestUniqName(t *testing.T) { 222 rs := rsFixture() 223 224 rel1 := releaseStub() 225 rel2 := releaseStub() 226 rel2.Name = "happy-panda" 227 rel2.Info.Status.Code = release.Status_DELETED 228 229 rs.env.Releases.Create(rel1) 230 rs.env.Releases.Create(rel2) 231 232 tests := []struct { 233 name string 234 expect string 235 reuse bool 236 err bool 237 }{ 238 {"first", "first", false, false}, 239 {"", "[a-z]+-[a-z]+", false, false}, 240 {"angry-panda", "", false, true}, 241 {"happy-panda", "", false, true}, 242 {"happy-panda", "happy-panda", true, false}, 243 {"hungry-hungry-hungry-hungry-hungry-hungry-hungry-hungry-hippos", "", true, true}, // Exceeds max name length 244 } 245 246 for _, tt := range tests { 247 u, err := rs.uniqName(tt.name, tt.reuse) 248 if err != nil { 249 if tt.err { 250 continue 251 } 252 t.Fatal(err) 253 } 254 if tt.err { 255 t.Errorf("Expected an error for %q", tt.name) 256 } 257 if match, err := regexp.MatchString(tt.expect, u); err != nil { 258 t.Fatal(err) 259 } else if !match { 260 t.Errorf("Expected %q to match %q", u, tt.expect) 261 } 262 } 263 } 264 265 func releaseWithKeepStub(rlsName string) *release.Release { 266 ch := &chart.Chart{ 267 Metadata: &chart.Metadata{ 268 Name: "bunnychart", 269 }, 270 Templates: []*chart.Template{ 271 {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, 272 }, 273 } 274 275 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 276 return &release.Release{ 277 Name: rlsName, 278 Info: &release.Info{ 279 FirstDeployed: &date, 280 LastDeployed: &date, 281 Status: &release.Status{Code: release.Status_DEPLOYED}, 282 }, 283 Chart: ch, 284 Config: &chart.Config{Raw: `name: value`}, 285 Version: 1, 286 Manifest: manifestWithKeep, 287 } 288 } 289 290 func MockEnvironment() *environment.Environment { 291 e := environment.New() 292 e.Releases = storage.Init(driver.NewMemory()) 293 e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} 294 return e 295 } 296 297 func newUpdateFailingKubeClient() *updateFailingKubeClient { 298 return &updateFailingKubeClient{ 299 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 300 } 301 302 } 303 304 type updateFailingKubeClient struct { 305 environment.PrintingKubeClient 306 } 307 308 func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { 309 return errors.New("Failed update in kube client") 310 } 311 312 func newHookFailingKubeClient() *hookFailingKubeClient { 313 return &hookFailingKubeClient{ 314 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 315 } 316 } 317 318 type hookFailingKubeClient struct { 319 environment.PrintingKubeClient 320 } 321 322 func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 323 return errors.New("Failed watch") 324 } 325 326 type mockListServer struct { 327 val *services.ListReleasesResponse 328 } 329 330 func (l *mockListServer) Send(res *services.ListReleasesResponse) error { 331 l.val = res 332 return nil 333 } 334 335 func (l *mockListServer) Context() context.Context { return helm.NewContext() } 336 func (l *mockListServer) SendMsg(v interface{}) error { return nil } 337 func (l *mockListServer) RecvMsg(v interface{}) error { return nil } 338 func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } 339 func (l *mockListServer) SetTrailer(m metadata.MD) {} 340 func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } 341 342 type mockRunReleaseTestServer struct { 343 stream grpc.ServerStream 344 } 345 346 func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { 347 return nil 348 } 349 func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } 350 func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } 351 func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} 352 func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } 353 func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } 354 func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() }