github.com/strongmonkey/helm@v2.7.2+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 }