github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+incompatible/cmd/helm/helm_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 main
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"math/rand"
    25  	"os"
    26  	"regexp"
    27  	"testing"
    28  
    29  	"github.com/golang/protobuf/ptypes/timestamp"
    30  	"github.com/spf13/cobra"
    31  
    32  	"k8s.io/helm/pkg/helm"
    33  	"k8s.io/helm/pkg/helm/helmpath"
    34  	"k8s.io/helm/pkg/proto/hapi/chart"
    35  	"k8s.io/helm/pkg/proto/hapi/release"
    36  	rls "k8s.io/helm/pkg/proto/hapi/services"
    37  	"k8s.io/helm/pkg/proto/hapi/version"
    38  	"k8s.io/helm/pkg/repo"
    39  )
    40  
    41  var mockHookTemplate = `apiVersion: v1
    42  kind: Job
    43  metadata:
    44    annotations:
    45      "helm.sh/hooks": pre-install
    46  `
    47  
    48  var mockManifest = `apiVersion: v1
    49  kind: Secret
    50  metadata:
    51    name: fixture
    52  `
    53  
    54  type releaseOptions struct {
    55  	name       string
    56  	version    int32
    57  	chart      *chart.Chart
    58  	statusCode release.Status_Code
    59  	namespace  string
    60  }
    61  
    62  func releaseMock(opts *releaseOptions) *release.Release {
    63  	date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
    64  
    65  	name := opts.name
    66  	if name == "" {
    67  		name = "testrelease-" + string(rand.Intn(100))
    68  	}
    69  
    70  	var version int32 = 1
    71  	if opts.version != 0 {
    72  		version = opts.version
    73  	}
    74  
    75  	namespace := opts.namespace
    76  	if namespace == "" {
    77  		namespace = "default"
    78  	}
    79  
    80  	ch := opts.chart
    81  	if opts.chart == nil {
    82  		ch = &chart.Chart{
    83  			Metadata: &chart.Metadata{
    84  				Name:    "foo",
    85  				Version: "0.1.0-beta.1",
    86  			},
    87  			Templates: []*chart.Template{
    88  				{Name: "templates/foo.tpl", Data: []byte(mockManifest)},
    89  			},
    90  		}
    91  	}
    92  
    93  	scode := release.Status_DEPLOYED
    94  	if opts.statusCode > 0 {
    95  		scode = opts.statusCode
    96  	}
    97  
    98  	return &release.Release{
    99  		Name: name,
   100  		Info: &release.Info{
   101  			FirstDeployed: &date,
   102  			LastDeployed:  &date,
   103  			Status:        &release.Status{Code: scode},
   104  			Description:   "Release mock",
   105  		},
   106  		Chart:     ch,
   107  		Config:    &chart.Config{Raw: `name: "value"`},
   108  		Version:   version,
   109  		Namespace: namespace,
   110  		Hooks: []*release.Hook{
   111  			{
   112  				Name:     "pre-install-hook",
   113  				Kind:     "Job",
   114  				Path:     "pre-install-hook.yaml",
   115  				Manifest: mockHookTemplate,
   116  				LastRun:  &date,
   117  				Events:   []release.Hook_Event{release.Hook_PRE_INSTALL},
   118  			},
   119  		},
   120  		Manifest: mockManifest,
   121  	}
   122  }
   123  
   124  type fakeReleaseClient struct {
   125  	rels []*release.Release
   126  	err  error
   127  }
   128  
   129  var _ helm.Interface = &fakeReleaseClient{}
   130  var _ helm.Interface = &helm.Client{}
   131  
   132  func (c *fakeReleaseClient) ListReleases(opts ...helm.ReleaseListOption) (*rls.ListReleasesResponse, error) {
   133  	resp := &rls.ListReleasesResponse{
   134  		Count:    int64(len(c.rels)),
   135  		Releases: c.rels,
   136  	}
   137  	return resp, c.err
   138  }
   139  
   140  func (c *fakeReleaseClient) InstallRelease(chStr, ns string, opts ...helm.InstallOption) (*rls.InstallReleaseResponse, error) {
   141  	return &rls.InstallReleaseResponse{
   142  		Release: c.rels[0],
   143  	}, nil
   144  }
   145  
   146  func (c *fakeReleaseClient) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ...helm.InstallOption) (*rls.InstallReleaseResponse, error) {
   147  	return &rls.InstallReleaseResponse{
   148  		Release: c.rels[0],
   149  	}, nil
   150  }
   151  
   152  func (c *fakeReleaseClient) DeleteRelease(rlsName string, opts ...helm.DeleteOption) (*rls.UninstallReleaseResponse, error) {
   153  	return nil, nil
   154  }
   155  
   156  func (c *fakeReleaseClient) ReleaseStatus(rlsName string, opts ...helm.StatusOption) (*rls.GetReleaseStatusResponse, error) {
   157  	if c.rels[0] != nil {
   158  		return &rls.GetReleaseStatusResponse{
   159  			Name:      c.rels[0].Name,
   160  			Info:      c.rels[0].Info,
   161  			Namespace: c.rels[0].Namespace,
   162  		}, nil
   163  	}
   164  	return nil, fmt.Errorf("No such release: %s", rlsName)
   165  }
   166  
   167  func (c *fakeReleaseClient) GetVersion(opts ...helm.VersionOption) (*rls.GetVersionResponse, error) {
   168  	return &rls.GetVersionResponse{
   169  		Version: &version.Version{
   170  			SemVer: "1.2.3-fakeclient+testonly",
   171  		},
   172  	}, nil
   173  }
   174  
   175  func (c *fakeReleaseClient) UpdateRelease(rlsName string, chStr string, opts ...helm.UpdateOption) (*rls.UpdateReleaseResponse, error) {
   176  	return nil, nil
   177  }
   178  
   179  func (c *fakeReleaseClient) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts ...helm.UpdateOption) (*rls.UpdateReleaseResponse, error) {
   180  	return nil, nil
   181  }
   182  
   183  func (c *fakeReleaseClient) RollbackRelease(rlsName string, opts ...helm.RollbackOption) (*rls.RollbackReleaseResponse, error) {
   184  	return nil, nil
   185  }
   186  
   187  func (c *fakeReleaseClient) ReleaseContent(rlsName string, opts ...helm.ContentOption) (resp *rls.GetReleaseContentResponse, err error) {
   188  	if len(c.rels) > 0 {
   189  		resp = &rls.GetReleaseContentResponse{
   190  			Release: c.rels[0],
   191  		}
   192  	}
   193  	return resp, c.err
   194  }
   195  
   196  func (c *fakeReleaseClient) ReleaseHistory(rlsName string, opts ...helm.HistoryOption) (*rls.GetHistoryResponse, error) {
   197  	return &rls.GetHistoryResponse{Releases: c.rels}, c.err
   198  }
   199  
   200  func (c *fakeReleaseClient) RunReleaseTest(rlsName string, opts ...helm.ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) {
   201  	return nil, nil
   202  }
   203  
   204  func (c *fakeReleaseClient) Option(opt ...helm.Option) helm.Interface {
   205  	return c
   206  }
   207  
   208  // releaseCmd is a command that works with a fakeReleaseClient
   209  type releaseCmd func(c *fakeReleaseClient, out io.Writer) *cobra.Command
   210  
   211  // runReleaseCases runs a set of release cases through the given releaseCmd.
   212  func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) {
   213  	var buf bytes.Buffer
   214  	for _, tt := range tests {
   215  		c := &fakeReleaseClient{
   216  			rels: []*release.Release{tt.resp},
   217  		}
   218  		cmd := rcmd(c, &buf)
   219  		cmd.ParseFlags(tt.flags)
   220  		err := cmd.RunE(cmd, tt.args)
   221  		if (err != nil) != tt.err {
   222  			t.Errorf("%q. expected error, got '%v'", tt.name, err)
   223  		}
   224  		re := regexp.MustCompile(tt.expected)
   225  		if !re.Match(buf.Bytes()) {
   226  			t.Errorf("%q. expected\n%q\ngot\n%q", tt.name, tt.expected, buf.String())
   227  		}
   228  		buf.Reset()
   229  	}
   230  }
   231  
   232  // releaseCase describes a test case that works with releases.
   233  type releaseCase struct {
   234  	name  string
   235  	args  []string
   236  	flags []string
   237  	// expected is the string to be matched. This supports regular expressions.
   238  	expected string
   239  	err      bool
   240  	resp     *release.Release
   241  }
   242  
   243  // tempHelmHome sets up a Helm Home in a temp dir.
   244  //
   245  // This does not clean up the directory. You must do that yourself.
   246  // You  must also set helmHome yourself.
   247  func tempHelmHome(t *testing.T) (string, error) {
   248  	oldhome := helmHome
   249  	dir, err := ioutil.TempDir("", "helm_home-")
   250  	if err != nil {
   251  		return "n/", err
   252  	}
   253  
   254  	helmHome = dir
   255  	if err := ensureTestHome(helmpath.Home(helmHome), t); err != nil {
   256  		return "n/", err
   257  	}
   258  	helmHome = oldhome
   259  	return dir, nil
   260  }
   261  
   262  // ensureTestHome creates a home directory like ensureHome, but without remote references.
   263  //
   264  // t is used only for logging.
   265  func ensureTestHome(home helmpath.Home, t *testing.T) error {
   266  	configDirectories := []string{home.String(), home.Repository(), home.Cache(), home.LocalRepository(), home.Plugins(), home.Starters()}
   267  	for _, p := range configDirectories {
   268  		if fi, err := os.Stat(p); err != nil {
   269  			if err := os.MkdirAll(p, 0755); err != nil {
   270  				return fmt.Errorf("Could not create %s: %s", p, err)
   271  			}
   272  		} else if !fi.IsDir() {
   273  			return fmt.Errorf("%s must be a directory", p)
   274  		}
   275  	}
   276  
   277  	repoFile := home.RepositoryFile()
   278  	if fi, err := os.Stat(repoFile); err != nil {
   279  		rf := repo.NewRepoFile()
   280  		rf.Add(&repo.Entry{
   281  			Name:  "charts",
   282  			URL:   "http://example.com/foo",
   283  			Cache: "charts-index.yaml",
   284  		}, &repo.Entry{
   285  			Name:  "local",
   286  			URL:   "http://localhost.com:7743/foo",
   287  			Cache: "local-index.yaml",
   288  		})
   289  		if err := rf.WriteFile(repoFile, 0644); err != nil {
   290  			return err
   291  		}
   292  	} else if fi.IsDir() {
   293  		return fmt.Errorf("%s must be a file, not a directory", repoFile)
   294  	}
   295  	if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate {
   296  		t.Log("Updating repository file format...")
   297  		if err := r.WriteFile(repoFile, 0644); err != nil {
   298  			return err
   299  		}
   300  	}
   301  
   302  	localRepoIndexFile := home.LocalRepository(localRepoIndexFilePath)
   303  	if fi, err := os.Stat(localRepoIndexFile); err != nil {
   304  		i := repo.NewIndexFile()
   305  		if err := i.WriteFile(localRepoIndexFile, 0644); err != nil {
   306  			return err
   307  		}
   308  
   309  		//TODO: take this out and replace with helm update functionality
   310  		os.Symlink(localRepoIndexFile, home.CacheIndex("local"))
   311  	} else if fi.IsDir() {
   312  		return fmt.Errorf("%s must be a file, not a directory", localRepoIndexFile)
   313  	}
   314  
   315  	t.Logf("$HELM_HOME has been configured at %s.\n", helmHome)
   316  	return nil
   317  }