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 }