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() }