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