github.com/helmwave/helmwave@v0.36.4-0.20240509190856-b35563eba4c6/pkg/action/build_internal_test.go (about) 1 package action 2 3 import ( 4 "context" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 10 "github.com/databus23/helm-diff/v3/diff" 11 "github.com/helmwave/helmwave/pkg/cache" 12 "github.com/helmwave/helmwave/pkg/helper" 13 "github.com/helmwave/helmwave/pkg/hooks" 14 "github.com/helmwave/helmwave/pkg/plan" 15 "github.com/helmwave/helmwave/pkg/release" 16 "github.com/helmwave/helmwave/pkg/release/uniqname" 17 "github.com/helmwave/helmwave/pkg/repo" 18 "github.com/helmwave/helmwave/pkg/template" 19 "github.com/helmwave/helmwave/tests" 20 log "github.com/sirupsen/logrus" 21 logTest "github.com/sirupsen/logrus/hooks/test" 22 "github.com/stretchr/testify/suite" 23 "github.com/urfave/cli/v2" 24 ) 25 26 type BuildTestSuite struct { 27 suite.Suite 28 29 ctx context.Context 30 } 31 32 func TestBuildTestSuite(t *testing.T) { 33 t.Parallel() 34 suite.Run(t, new(BuildTestSuite)) 35 } 36 37 func (ts *BuildTestSuite) SetupTest() { 38 ts.ctx = tests.GetContext(ts.T()) 39 } 40 41 func (ts *BuildTestSuite) TestCmd() { 42 s := &Build{} 43 cmd := s.Cmd() 44 45 ts.Require().NotNil(cmd) 46 ts.Require().NotEmpty(cmd.Name) 47 } 48 49 func (ts *BuildTestSuite) TestYmlError() { 50 tmpDir := ts.T().TempDir() 51 y := &Yml{ 52 file: filepath.Join(tests.Root, "helmwave.yml"), 53 templater: template.TemplaterSprig, 54 } 55 56 s := &Build{ 57 plandir: tmpDir, 58 yml: y, 59 tags: cli.StringSlice{}, 60 options: plan.BuildOptions{ 61 MatchAll: true, 62 }, 63 } 64 65 ts.Require().Error(s.Run(ts.ctx)) 66 } 67 68 func (ts *BuildTestSuite) TestManifest() { 69 tmpDir := ts.T().TempDir() 70 y := &Yml{ 71 tpl: filepath.Join(tests.Root, "01_helmwave.yml.tpl"), 72 file: filepath.Join(tests.Root, "02_helmwave.yml"), 73 templater: template.TemplaterSprig, 74 } 75 76 s := &Build{ 77 plandir: tmpDir, 78 yml: y, 79 tags: cli.StringSlice{}, 80 options: plan.BuildOptions{ 81 MatchAll: true, 82 }, 83 } 84 85 ts.Require().NoError(s.Run(ts.ctx)) 86 ts.Require().DirExists(filepath.Join(s.plandir, plan.Manifest)) 87 } 88 89 // func (ts *BuildTestSuite) TestRepositories404() { 90 // s := &Build{ 91 // plandir: tmpDir, 92 // ymlFile: filepath.Join(tests.Root, "04_helmwave.yml"), 93 // tags: cli.StringSlice{}, 94 // options: plan.BuildOptions{ 95 // MatchAll: true, 96 // }, 97 // } 98 // 99 // err := s.Run() 100 // if !errors.Is(err, repo.ErrNotFound) && err != nil { 101 // t.Error("'bitnami' must be not found") 102 // } 103 // } 104 105 func (ts *BuildTestSuite) TestNonUniqueReleases() { 106 tmpDir := ts.T().TempDir() 107 y := &Yml{ 108 tpl: filepath.Join(tests.Root, "14_helmwave.yml"), 109 file: filepath.Join(tmpDir, "14_helmwave.yml"), 110 templater: template.TemplaterSprig, 111 } 112 113 sfail := &Build{ 114 plandir: tmpDir, 115 yml: y, 116 tags: cli.StringSlice{}, 117 options: plan.BuildOptions{ 118 MatchAll: true, 119 }, 120 autoYml: true, 121 } 122 123 sfailByTag := &Build{ 124 plandir: tmpDir, 125 yml: y, 126 tags: cli.StringSlice{}, 127 options: plan.BuildOptions{ 128 MatchAll: true, 129 }, 130 autoYml: true, 131 } 132 err := sfailByTag.tags.Set("nginx") 133 ts.Require().NoError(err) 134 135 sa := &Build{ 136 plandir: tmpDir, 137 yml: y, 138 tags: cli.StringSlice{}, 139 options: plan.BuildOptions{ 140 MatchAll: true, 141 }, 142 autoYml: true, 143 } 144 err = sa.tags.Set("nginx-a") 145 ts.Require().NoError(err) 146 147 sb := &Build{ 148 plandir: tmpDir, 149 yml: y, 150 tags: cli.StringSlice{}, 151 options: plan.BuildOptions{ 152 MatchAll: true, 153 }, 154 autoYml: true, 155 } 156 err = sb.tags.Set("nginx-b") 157 ts.Require().NoError(err) 158 159 var e *release.DuplicateError 160 ts.Require().ErrorAs(sfail.Run(ts.ctx), &e) 161 ts.Equal(uniqname.UniqName("nginx@test"), e.Uniq) 162 163 ts.Require().ErrorAs(sfailByTag.Run(ts.ctx), &e) 164 ts.Equal(uniqname.UniqName("nginx@test"), e.Uniq) 165 166 ts.Require().NoError(sa.Run(ts.ctx)) 167 ts.Require().NoError(sb.Run(ts.ctx)) 168 } 169 170 func (ts *BuildTestSuite) TestRepositories() { 171 tmpDir := ts.T().TempDir() 172 y := &Yml{ 173 tpl: filepath.Join(tests.Root, "01_helmwave.yml.tpl"), 174 file: filepath.Join(tests.Root, "02_helmwave.yml"), 175 templater: template.TemplaterSprig, 176 } 177 178 s := &Build{ 179 plandir: tmpDir, 180 yml: y, 181 tags: cli.StringSlice{}, 182 options: plan.BuildOptions{ 183 MatchAll: true, 184 }, 185 } 186 187 ts.Require().NoError(s.Run(ts.ctx)) 188 189 const rep = "bitnami" 190 b, _ := plan.NewBody(ts.ctx, filepath.Join(s.plandir, plan.File), true) 191 192 if _, found := repo.IndexOfName(b.Repositories, rep); !found { 193 ts.Failf("%q not found", rep) 194 } 195 } 196 197 func (ts *BuildTestSuite) TestReleasesMatchGroup() { 198 tmpDir := ts.T().TempDir() 199 y := &Yml{ 200 tpl: filepath.Join(tests.Root, "01_helmwave.yml.tpl"), 201 file: filepath.Join(tests.Root, "03_helmwave.yml"), 202 templater: template.TemplaterSprig, 203 } 204 205 cases := []struct { 206 tags *cli.StringSlice 207 names []string 208 }{ 209 { 210 tags: cli.NewStringSlice("b"), 211 names: []string{"redis-b", "memcached-b"}, 212 }, 213 { 214 tags: cli.NewStringSlice("b", "redis"), 215 names: []string{"redis-b"}, 216 }, 217 } 218 219 for i := range cases { 220 s := &Build{ 221 plandir: tmpDir, 222 yml: y, 223 tags: *cases[i].tags, 224 options: plan.BuildOptions{ 225 MatchAll: true, 226 }, 227 } 228 229 ts.Require().NoError(s.Run(ts.ctx)) 230 231 b, _ := plan.NewBody(ts.ctx, filepath.Join(s.plandir, plan.File), true) 232 233 names := helper.SlicesMap(b.Releases, func(rel release.Config) string { 234 return rel.Name() 235 }) 236 237 ts.ElementsMatch(cases[i].names, names) 238 } 239 } 240 241 func (ts *BuildTestSuite) TestDiffLocal() { 242 tmpDir := ts.T().TempDir() 243 y := &Yml{ 244 tpl: filepath.Join(tests.Root, "07_helmwave.yml"), 245 file: filepath.Join(tests.Root, "07_helmwave.yml"), 246 templater: template.TemplaterSprig, 247 } 248 249 s := &Build{ 250 plandir: tmpDir, 251 tags: cli.StringSlice{}, 252 options: plan.BuildOptions{ 253 MatchAll: true, 254 }, 255 autoYml: true, 256 yml: y, 257 diff: &Diff{Options: &diff.Options{}}, 258 diffMode: DiffModeLocal, 259 } 260 261 ts.Require().NoError(s.Run(ts.ctx), "build should not fail without diffing") 262 ts.Require().NoError(s.Run(ts.ctx), "build should not fail with diffing with previous plan") 263 } 264 265 func (ts *BuildTestSuite) TestValuesDependency() { 266 tmpDir := ts.T().TempDir() 267 y := &Yml{ 268 tpl: filepath.Join(tests.Root, "19_helmwave.yml"), 269 file: filepath.Join(tests.Root, "19_helmwave.yml"), 270 templater: template.TemplaterSprig, 271 } 272 273 s := &Build{ 274 plandir: tmpDir, 275 tags: cli.StringSlice{}, 276 options: plan.BuildOptions{ 277 MatchAll: true, 278 }, 279 autoYml: true, 280 yml: y, 281 } 282 283 ts.Require().NoError(s.Run(ts.ctx), "build should not fail") 284 } 285 286 type NonParallelBuildTestSuite struct { 287 suite.Suite 288 289 defaultHooks log.LevelHooks 290 logHook *logTest.Hook 291 292 ctx context.Context 293 } 294 295 //nolint:paralleltest // can't parallel because of setenv and uses helm repository.yaml flock 296 func TestNonParallelNonParallelBuildTestSuite(t *testing.T) { 297 // t.Parallel() 298 suite.Run(t, new(NonParallelBuildTestSuite)) 299 } 300 301 func (ts *NonParallelBuildTestSuite) SetupTest() { 302 ts.ctx = tests.GetContext(ts.T()) 303 } 304 305 func (ts *NonParallelBuildTestSuite) SetupSuite() { 306 ts.defaultHooks = log.StandardLogger().Hooks 307 ts.logHook = logTest.NewLocal(log.StandardLogger()) 308 } 309 310 func (ts *NonParallelBuildTestSuite) TearDownTestSuite() { 311 ts.logHook.Reset() 312 } 313 314 func (ts *NonParallelBuildTestSuite) TearDownSuite() { 315 log.StandardLogger().ReplaceHooks(ts.defaultHooks) 316 } 317 318 func (ts *NonParallelBuildTestSuite) getLoggerMessages() []string { 319 return helper.SlicesMap(ts.logHook.AllEntries(), func(entry *log.Entry) string { 320 return entry.Message 321 }) 322 } 323 324 func (ts *NonParallelBuildTestSuite) TestAutoYml() { 325 tmpDir := ts.T().TempDir() 326 y := &Yml{ 327 tpl: filepath.Join(tests.Root, "01_helmwave.yml.tpl"), 328 file: filepath.Join(tmpDir, "01_auto_yaml_helmwave.yml"), 329 templater: template.TemplaterSprig, 330 } 331 332 s := &Build{ 333 plandir: tmpDir, 334 tags: cli.StringSlice{}, 335 options: plan.BuildOptions{ 336 MatchAll: true, 337 }, 338 autoYml: true, 339 yml: y, 340 } 341 342 value := strings.ToLower(strings.ReplaceAll(ts.T().Name(), "/", "")) 343 ts.T().Setenv("NAMESPACE", value) 344 345 ts.Require().NoError(s.Run(ts.ctx)) 346 ts.Require().DirExists(filepath.Join(s.plandir, plan.Manifest)) 347 } 348 349 func (ts *NonParallelBuildTestSuite) TestGomplate() { 350 tmpDir := ts.T().TempDir() 351 y := &Yml{ 352 tpl: filepath.Join(tests.Root, "08_helmwave.yml"), 353 file: filepath.Join(tmpDir, "08_helmwave.yml"), 354 templater: template.TemplaterGomplate, 355 } 356 357 s := &Build{ 358 plandir: tmpDir, 359 tags: cli.StringSlice{}, 360 options: plan.BuildOptions{ 361 MatchAll: true, 362 }, 363 autoYml: true, 364 yml: y, 365 } 366 367 ts.Require().NoError(s.Run(ts.ctx)) 368 ts.Require().DirExists(filepath.Join(s.plandir, plan.Manifest)) 369 } 370 371 func (ts *NonParallelBuildTestSuite) TestLifecycle() { 372 tmpDir := ts.T().TempDir() 373 y := &Yml{ 374 tpl: filepath.Join(tests.Root, "13_helmwave.yml"), 375 file: filepath.Join(tmpDir, "13_helmwave.yml"), 376 templater: template.TemplaterSprig, 377 } 378 379 s := &Build{ 380 plandir: tmpDir, 381 tags: cli.StringSlice{}, 382 options: plan.BuildOptions{ 383 MatchAll: true, 384 }, 385 autoYml: true, 386 yml: y, 387 } 388 389 ts.Require().NoError(s.Run(ts.ctx)) 390 ts.Require().DirExists(filepath.Join(s.plandir, plan.Manifest)) 391 392 logMessages := ts.getLoggerMessages() 393 ts.Require().Contains(logMessages, "running pre_build script for nginx") 394 ts.Require().Contains(logMessages, "run global pre_build script") 395 ts.Require().Contains(logMessages, "running post_build script for nginx") 396 ts.Require().Contains(logMessages, "run global post_build script") 397 } 398 399 func (ts *NonParallelBuildTestSuite) TestLifecyclePost() { 400 tmpDir := ts.T().TempDir() 401 y := &Yml{ 402 tpl: filepath.Join(tests.Root, "17_helmwave.yml"), 403 file: filepath.Join(tmpDir, "17_helmwave.yml"), 404 templater: template.TemplaterSprig, 405 } 406 407 s := &Build{ 408 plandir: tmpDir, 409 tags: cli.StringSlice{}, 410 options: plan.BuildOptions{ 411 MatchAll: true, 412 }, 413 autoYml: true, 414 yml: y, 415 } 416 417 err := s.Run(ts.ctx) 418 419 var e *hooks.CommandRunError 420 ts.Require().ErrorAs(err, &e) 421 } 422 423 func (ts *NonParallelBuildTestSuite) TestRemoteSource() { 424 tmpDir := ts.T().TempDir() 425 d, err := os.Getwd() 426 ts.Require().NoError(err) 427 428 ts.Require().NoError(os.Chdir(tmpDir)) 429 ts.T().Cleanup(func() { 430 ts.Require().NoError(os.Chdir(d)) 431 }) 432 433 y := &Yml{ 434 tpl: filepath.Join("tests", "02_helmwave.yml"), 435 file: filepath.Join("tests", "02_helmwave.yml"), 436 templater: template.TemplaterSprig, 437 } 438 439 s := &Build{ 440 plandir: plan.Dir, 441 tags: cli.StringSlice{}, 442 options: plan.BuildOptions{ 443 MatchAll: true, 444 }, 445 remoteSource: "github.com/helmwave/helmwave", 446 yml: y, 447 } 448 449 cache.DefaultConfig.Home = ts.T().TempDir() 450 451 err = s.Run(ts.ctx) 452 ts.Require().NoError(err) 453 454 ts.DirExists(filepath.Join(tmpDir, s.plandir)) 455 ts.FileExists(filepath.Join(tmpDir, s.plandir, plan.File)) 456 }