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