github.com/migueleliasweb/helm@v2.6.1+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  	"path/filepath"
    27  	"regexp"
    28  	"strings"
    29  	"testing"
    30  
    31  	"github.com/golang/protobuf/ptypes/timestamp"
    32  	"github.com/spf13/cobra"
    33  
    34  	"k8s.io/helm/pkg/helm"
    35  	"k8s.io/helm/pkg/helm/helmpath"
    36  	"k8s.io/helm/pkg/proto/hapi/chart"
    37  	"k8s.io/helm/pkg/proto/hapi/release"
    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  // releaseCmd is a command that works with a FakeClient
   125  type releaseCmd func(c *helm.FakeClient, out io.Writer) *cobra.Command
   126  
   127  // runReleaseCases runs a set of release cases through the given releaseCmd.
   128  func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) {
   129  	var buf bytes.Buffer
   130  	for _, tt := range tests {
   131  		c := &helm.FakeClient{
   132  			Rels: []*release.Release{tt.resp},
   133  		}
   134  		cmd := rcmd(c, &buf)
   135  		cmd.ParseFlags(tt.flags)
   136  		err := cmd.RunE(cmd, tt.args)
   137  		if (err != nil) != tt.err {
   138  			t.Errorf("%q. expected error, got '%v'", tt.name, err)
   139  		}
   140  		re := regexp.MustCompile(tt.expected)
   141  		if !re.Match(buf.Bytes()) {
   142  			t.Errorf("%q. expected\n%q\ngot\n%q", tt.name, tt.expected, buf.String())
   143  		}
   144  		buf.Reset()
   145  	}
   146  }
   147  
   148  // releaseCase describes a test case that works with releases.
   149  type releaseCase struct {
   150  	name  string
   151  	args  []string
   152  	flags []string
   153  	// expected is the string to be matched. This supports regular expressions.
   154  	expected string
   155  	err      bool
   156  	resp     *release.Release
   157  }
   158  
   159  // tempHelmHome sets up a Helm Home in a temp dir.
   160  //
   161  // This does not clean up the directory. You must do that yourself.
   162  // You  must also set helmHome yourself.
   163  func tempHelmHome(t *testing.T) (helmpath.Home, error) {
   164  	oldhome := settings.Home
   165  	dir, err := ioutil.TempDir("", "helm_home-")
   166  	if err != nil {
   167  		return helmpath.Home("n/"), err
   168  	}
   169  
   170  	settings.Home = helmpath.Home(dir)
   171  	if err := ensureTestHome(settings.Home, t); err != nil {
   172  		return helmpath.Home("n/"), err
   173  	}
   174  	settings.Home = oldhome
   175  	return helmpath.Home(dir), nil
   176  }
   177  
   178  // ensureTestHome creates a home directory like ensureHome, but without remote references.
   179  //
   180  // t is used only for logging.
   181  func ensureTestHome(home helmpath.Home, t *testing.T) error {
   182  	configDirectories := []string{home.String(), home.Repository(), home.Cache(), home.LocalRepository(), home.Plugins(), home.Starters()}
   183  	for _, p := range configDirectories {
   184  		if fi, err := os.Stat(p); err != nil {
   185  			if err := os.MkdirAll(p, 0755); err != nil {
   186  				return fmt.Errorf("Could not create %s: %s", p, err)
   187  			}
   188  		} else if !fi.IsDir() {
   189  			return fmt.Errorf("%s must be a directory", p)
   190  		}
   191  	}
   192  
   193  	repoFile := home.RepositoryFile()
   194  	if fi, err := os.Stat(repoFile); err != nil {
   195  		rf := repo.NewRepoFile()
   196  		rf.Add(&repo.Entry{
   197  			Name:  "charts",
   198  			URL:   "http://example.com/foo",
   199  			Cache: "charts-index.yaml",
   200  		}, &repo.Entry{
   201  			Name:  "local",
   202  			URL:   "http://localhost.com:7743/foo",
   203  			Cache: "local-index.yaml",
   204  		})
   205  		if err := rf.WriteFile(repoFile, 0644); err != nil {
   206  			return err
   207  		}
   208  	} else if fi.IsDir() {
   209  		return fmt.Errorf("%s must be a file, not a directory", repoFile)
   210  	}
   211  	if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate {
   212  		t.Log("Updating repository file format...")
   213  		if err := r.WriteFile(repoFile, 0644); err != nil {
   214  			return err
   215  		}
   216  	}
   217  
   218  	localRepoIndexFile := home.LocalRepository(localRepositoryIndexFile)
   219  	if fi, err := os.Stat(localRepoIndexFile); err != nil {
   220  		i := repo.NewIndexFile()
   221  		if err := i.WriteFile(localRepoIndexFile, 0644); err != nil {
   222  			return err
   223  		}
   224  
   225  		//TODO: take this out and replace with helm update functionality
   226  		os.Symlink(localRepoIndexFile, home.CacheIndex("local"))
   227  	} else if fi.IsDir() {
   228  		return fmt.Errorf("%s must be a file, not a directory", localRepoIndexFile)
   229  	}
   230  
   231  	t.Logf("$HELM_HOME has been configured at %s.\n", settings.Home.String())
   232  	return nil
   233  }
   234  
   235  func TestRootCmd(t *testing.T) {
   236  	cleanup := resetEnv()
   237  	defer cleanup()
   238  
   239  	tests := []struct {
   240  		name   string
   241  		args   []string
   242  		envars map[string]string
   243  		home   string
   244  	}{
   245  		{
   246  			name: "defaults",
   247  			args: []string{"home"},
   248  			home: filepath.Join(os.Getenv("HOME"), "/.helm"),
   249  		},
   250  		{
   251  			name: "with --home set",
   252  			args: []string{"--home", "/foo"},
   253  			home: "/foo",
   254  		},
   255  		{
   256  			name: "subcommands with --home set",
   257  			args: []string{"home", "--home", "/foo"},
   258  			home: "/foo",
   259  		},
   260  		{
   261  			name:   "with $HELM_HOME set",
   262  			args:   []string{"home"},
   263  			envars: map[string]string{"HELM_HOME": "/bar"},
   264  			home:   "/bar",
   265  		},
   266  		{
   267  			name:   "subcommands with $HELM_HOME set",
   268  			args:   []string{"home"},
   269  			envars: map[string]string{"HELM_HOME": "/bar"},
   270  			home:   "/bar",
   271  		},
   272  		{
   273  			name:   "with $HELM_HOME and --home set",
   274  			args:   []string{"home", "--home", "/foo"},
   275  			envars: map[string]string{"HELM_HOME": "/bar"},
   276  			home:   "/foo",
   277  		},
   278  	}
   279  
   280  	// ensure not set locally
   281  	os.Unsetenv("HELM_HOME")
   282  
   283  	for _, tt := range tests {
   284  		t.Run(tt.name, func(t *testing.T) {
   285  			defer os.Unsetenv("HELM_HOME")
   286  
   287  			for k, v := range tt.envars {
   288  				os.Setenv(k, v)
   289  			}
   290  
   291  			cmd := newRootCmd(tt.args)
   292  			cmd.SetOutput(ioutil.Discard)
   293  			cmd.SetArgs(tt.args)
   294  			cmd.Run = func(*cobra.Command, []string) {}
   295  			if err := cmd.Execute(); err != nil {
   296  				t.Errorf("unexpected error: %s", err)
   297  			}
   298  
   299  			if settings.Home.String() != tt.home {
   300  				t.Errorf("expected home %q, got %q", tt.home, settings.Home)
   301  			}
   302  			homeFlag := cmd.Flag("home").Value.String()
   303  			homeFlag = os.ExpandEnv(homeFlag)
   304  			if homeFlag != tt.home {
   305  				t.Errorf("expected home %q, got %q", tt.home, homeFlag)
   306  			}
   307  		})
   308  	}
   309  }
   310  
   311  func resetEnv() func() {
   312  	origSettings := settings
   313  	origEnv := os.Environ()
   314  	return func() {
   315  		settings = origSettings
   316  		for _, pair := range origEnv {
   317  			kv := strings.SplitN(pair, "=", 2)
   318  			os.Setenv(kv[0], kv[1])
   319  		}
   320  	}
   321  }