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