github.com/replicatedhq/ship@v0.55.0/pkg/lifecycle/daemon/headless/daemon_test.go (about) 1 package headless 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "testing" 8 9 "github.com/mitchellh/cli" 10 "github.com/replicatedhq/libyaml" 11 "github.com/replicatedhq/ship/pkg/api" 12 "github.com/replicatedhq/ship/pkg/constants" 13 "github.com/replicatedhq/ship/pkg/lifecycle/render/config/resolve" 14 "github.com/replicatedhq/ship/pkg/state" 15 "github.com/replicatedhq/ship/pkg/templates" 16 "github.com/replicatedhq/ship/pkg/testing/logger" 17 "github.com/spf13/afero" 18 "github.com/spf13/viper" 19 "github.com/stretchr/testify/require" 20 ) 21 22 type TestHeadless struct { 23 Name string 24 State []byte 25 Release *api.Release 26 ExpectedConfig map[string]interface{} 27 ExpectedError bool 28 } 29 30 func TestHeadlessDaemon(t *testing.T) { 31 tests := []TestHeadless{ 32 { 33 Name: "empty", 34 State: []byte(`{}`), 35 36 Release: &api.Release{ 37 Spec: api.Spec{ 38 Config: api.Config{ 39 V1: []libyaml.ConfigGroup{}, 40 }, 41 }, 42 }, 43 ExpectedConfig: map[string]interface{}{}, 44 ExpectedError: false, 45 }, 46 { 47 Name: "one group one item, not required, no value", 48 State: []byte(`{"v1":{"config":{"alpha":""}}}`), 49 Release: &api.Release{ 50 Spec: api.Spec{ 51 Config: api.Config{ 52 V1: []libyaml.ConfigGroup{{ 53 Name: "testing", 54 Items: []*libyaml.ConfigItem{ 55 { 56 Name: "alpha", 57 Value: "", 58 Default: "", 59 Required: false, 60 Hidden: false, 61 }, 62 }, 63 }}, 64 }, 65 }, 66 }, 67 ExpectedConfig: map[string]interface{}{"alpha": ""}, 68 ExpectedError: false, 69 }, 70 { 71 Name: "one group one item, required, no value", 72 State: []byte(`{"v1":{"config":{"alpha":""}}}`), 73 Release: &api.Release{ 74 Spec: api.Spec{ 75 Config: api.Config{ 76 V1: []libyaml.ConfigGroup{{ 77 Name: "testing", 78 Items: []*libyaml.ConfigItem{ 79 { 80 Name: "alpha", 81 Value: "", 82 Default: "", 83 Required: true, 84 Hidden: false, 85 }, 86 }, 87 }}, 88 }, 89 }, 90 }, 91 ExpectedConfig: map[string]interface{}{}, 92 ExpectedError: true, 93 }, 94 { 95 Name: "one group one item, required, value, hidden", 96 State: []byte(`{}`), 97 Release: &api.Release{ 98 Spec: api.Spec{ 99 Config: api.Config{ 100 V1: []libyaml.ConfigGroup{{ 101 Name: "testing", 102 Items: []*libyaml.ConfigItem{ 103 { 104 Name: "alpha", 105 Value: "100", 106 Default: "", 107 Required: true, 108 Hidden: true, 109 }, 110 }, 111 }}, 112 }, 113 }, 114 }, 115 ExpectedConfig: map[string]interface{}{"alpha": "100"}, 116 ExpectedError: false, 117 }, 118 { 119 Name: "one group one item, not required, no value, hidden", 120 State: []byte(`{}`), 121 Release: &api.Release{ 122 Spec: api.Spec{ 123 Config: api.Config{ 124 V1: []libyaml.ConfigGroup{{ 125 Name: "testing", 126 Items: []*libyaml.ConfigItem{ 127 { 128 Name: "alpha", 129 Value: "100", 130 Default: "", 131 Required: false, 132 Hidden: true, 133 }, 134 }, 135 }}, 136 }, 137 }, 138 }, 139 ExpectedConfig: map[string]interface{}{"alpha": "100"}, 140 ExpectedError: false, 141 }, 142 { 143 Name: "one group one item, required, no value, hidden", 144 State: []byte(`{}`), 145 Release: &api.Release{ 146 Spec: api.Spec{ 147 Config: api.Config{ 148 V1: []libyaml.ConfigGroup{{ 149 Name: "testing", 150 Items: []*libyaml.ConfigItem{ 151 { 152 Name: "alpha", 153 Value: "", 154 Default: "", 155 Required: true, 156 Hidden: true, 157 }, 158 }, 159 }}, 160 }, 161 }, 162 }, 163 ExpectedConfig: map[string]interface{}{"alpha": ""}, 164 ExpectedError: false, 165 }, 166 { 167 Name: "one group one item, required, no value, not hidden", 168 State: []byte(`{}`), 169 Release: &api.Release{ 170 Spec: api.Spec{ 171 Config: api.Config{ 172 V1: []libyaml.ConfigGroup{{ 173 Name: "testing", 174 Items: []*libyaml.ConfigItem{ 175 { 176 Name: "alpha", 177 Value: "", 178 Default: "", 179 Required: true, 180 Hidden: true, 181 }, 182 }, 183 }}, 184 }, 185 }, 186 }, 187 ExpectedConfig: map[string]interface{}{"alpha": ""}, 188 ExpectedError: false, 189 }, 190 { 191 Name: "one group one item, required, value, not hidden", 192 State: []byte(`{"v1":{"config":{"alpha": "100"}}}`), 193 Release: &api.Release{ 194 Spec: api.Spec{ 195 Config: api.Config{ 196 V1: []libyaml.ConfigGroup{{ 197 Name: "testing", 198 Items: []*libyaml.ConfigItem{ 199 { 200 Name: "alpha", 201 Value: "100", 202 Default: "", 203 Required: true, 204 Hidden: false, 205 }, 206 }, 207 }}, 208 }, 209 }, 210 }, 211 ExpectedConfig: map[string]interface{}{"alpha": "100"}, 212 ExpectedError: false, 213 }, 214 { 215 Name: "one group one item, not required, no value, not hidden", 216 State: []byte(`{"v1":{"config":{"alpha": ""}}}`), 217 Release: &api.Release{ 218 Spec: api.Spec{ 219 Config: api.Config{ 220 V1: []libyaml.ConfigGroup{{ 221 Name: "testing", 222 Items: []*libyaml.ConfigItem{ 223 { 224 Name: "alpha", 225 Value: "", 226 Default: "", 227 Required: false, 228 Hidden: false, 229 }, 230 }, 231 }}, 232 }, 233 }, 234 }, 235 ExpectedConfig: map[string]interface{}{"alpha": ""}, 236 ExpectedError: false, 237 }, 238 { 239 Name: "one group one item, required, value, hidden", 240 State: []byte(`{}`), 241 Release: &api.Release{ 242 Spec: api.Spec{ 243 Config: api.Config{ 244 V1: []libyaml.ConfigGroup{{ 245 Name: "testing", 246 Items: []*libyaml.ConfigItem{ 247 { 248 Name: "alpha", 249 Value: "100", 250 Default: "", 251 Required: true, 252 Hidden: true, 253 }, 254 }, 255 }}, 256 }, 257 }, 258 }, 259 ExpectedConfig: map[string]interface{}{"alpha": "100"}, 260 ExpectedError: false, 261 }, 262 { 263 Name: "one group two items, neither required, neither present", 264 State: []byte(`{"v1":{"config":{"alpha": "", "beta": ""}}}`), 265 Release: &api.Release{ 266 Spec: api.Spec{ 267 Config: api.Config{ 268 V1: []libyaml.ConfigGroup{{ 269 Name: "testing", 270 Items: []*libyaml.ConfigItem{ 271 { 272 Name: "alpha", 273 Value: "", 274 Default: "", 275 Required: false, 276 Hidden: false, 277 }, 278 { 279 Name: "beta", 280 Value: "", 281 Default: "", 282 Required: false, 283 Hidden: false, 284 }, 285 }, 286 }}, 287 }, 288 }, 289 }, 290 ExpectedConfig: map[string]interface{}{"alpha": "", "beta": ""}, 291 ExpectedError: false, 292 }, 293 { 294 Name: "one group two items, both required, neither present", 295 State: []byte(`{"v1":{"config":{"alpha": "", "beta": ""}}}`), 296 Release: &api.Release{ 297 Spec: api.Spec{ 298 Config: api.Config{ 299 V1: []libyaml.ConfigGroup{{ 300 Name: "testing", 301 Items: []*libyaml.ConfigItem{ 302 { 303 Name: "alpha", 304 Value: "", 305 Default: "", 306 Required: true, 307 Hidden: false, 308 }, 309 { 310 Name: "beta", 311 Value: "", 312 Default: "", 313 Required: true, 314 Hidden: false, 315 }, 316 }, 317 }}, 318 }, 319 }, 320 }, 321 ExpectedConfig: map[string]interface{}{"alpha": "", "beta": ""}, 322 ExpectedError: true, 323 }, 324 { 325 Name: "one group two items, both required, one present", 326 State: []byte(`{"v1":{"config":{"alpha":"", "beta": ""}}}`), 327 Release: &api.Release{ 328 Spec: api.Spec{ 329 Config: api.Config{ 330 V1: []libyaml.ConfigGroup{{ 331 Name: "testing", 332 Items: []*libyaml.ConfigItem{ 333 { 334 Name: "alpha", 335 Value: "100", 336 Default: "", 337 Required: true, 338 Hidden: false, 339 }, 340 { 341 Name: "beta", 342 Value: "", 343 Default: "", 344 Required: true, 345 Hidden: false, 346 }, 347 }, 348 }}, 349 }, 350 }, 351 }, 352 ExpectedConfig: map[string]interface{}{"alpha": "", "beta": ""}, 353 ExpectedError: true, 354 }, 355 { 356 Name: "one group two items, both required, both present", 357 State: []byte(`{}`), 358 Release: &api.Release{ 359 Spec: api.Spec{ 360 Config: api.Config{ 361 V1: []libyaml.ConfigGroup{{ 362 Name: "testing", 363 Items: []*libyaml.ConfigItem{ 364 { 365 Name: "alpha", 366 Value: "100", 367 Default: "", 368 Required: true, 369 Hidden: false, 370 }, 371 { 372 Name: "beta", 373 Value: "200", 374 Default: "", 375 Required: true, 376 Hidden: false, 377 }, 378 }, 379 }}, 380 }, 381 }, 382 }, 383 ExpectedConfig: map[string]interface{}{"alpha": "100", "beta": "200"}, 384 ExpectedError: false, 385 }, 386 { 387 Name: "beta value resolves to alpha value", 388 State: []byte(`{"v1":{"config":{"alpha": "101"}}}`), 389 Release: &api.Release{ 390 Spec: api.Spec{ 391 Config: api.Config{ 392 V1: []libyaml.ConfigGroup{{ 393 Name: "testing", 394 Items: []*libyaml.ConfigItem{ 395 { 396 Name: "alpha", 397 Value: "100", 398 Default: "", 399 Required: false, 400 Hidden: false, 401 }, 402 { 403 Name: "beta", 404 Value: `{{repl ConfigOption "alpha" }}`, 405 Default: "", 406 Required: false, 407 ReadOnly: true, 408 }, 409 }, 410 }}, 411 }, 412 }, 413 }, 414 ExpectedConfig: map[string]interface{}{"alpha": "101", "beta": "101"}, 415 ExpectedError: false, 416 }, 417 { 418 Name: "beta value resolves to alpha value when wrong beta value is presented", 419 State: []byte(`{"v1":{"config":{"alpha": "101", "beta":"abc"}}}`), 420 Release: &api.Release{ 421 Spec: api.Spec{ 422 Config: api.Config{ 423 V1: []libyaml.ConfigGroup{{ 424 Name: "testing", 425 Items: []*libyaml.ConfigItem{ 426 { 427 Name: "alpha", 428 Value: "100", 429 Default: "", 430 Required: false, 431 Hidden: false, 432 }, 433 { 434 Name: "beta", 435 Value: `{{repl ConfigOption "alpha" }}`, 436 Default: "", 437 Required: false, 438 ReadOnly: true, 439 }, 440 }, 441 }}, 442 }, 443 }, 444 }, 445 ExpectedConfig: map[string]interface{}{"alpha": "101", "beta": "101"}, 446 ExpectedError: false, 447 }, 448 { 449 Name: "charlie value resolves to beta value resolves to alpha value", 450 State: []byte(`{"v1":{"config":{"alpha":"100"}}}`), 451 Release: &api.Release{ 452 Spec: api.Spec{ 453 Config: api.Config{ 454 V1: []libyaml.ConfigGroup{{ 455 Name: "testing", 456 Items: []*libyaml.ConfigItem{ 457 { 458 Name: "alpha", 459 Value: "100", 460 Default: "", 461 Required: false, 462 Hidden: false, 463 }, 464 { 465 Name: "beta", 466 Value: `{{repl ConfigOption "alpha" }}`, 467 Default: "", 468 Required: false, 469 ReadOnly: true, 470 }, 471 { 472 Name: "charlie", 473 Value: `{{repl ConfigOption "beta" }}`, 474 Default: "", 475 Required: false, 476 ReadOnly: true, 477 }, 478 }, 479 }}, 480 }, 481 }, 482 }, 483 ExpectedConfig: map[string]interface{}{"alpha": "100", "beta": "100", "charlie": "100"}, 484 ExpectedError: false, 485 }, 486 { 487 Name: "multiple groups with multiple items", 488 State: []byte(`{}`), 489 Release: &api.Release{ 490 Spec: api.Spec{ 491 Config: api.Config{ 492 V1: []libyaml.ConfigGroup{ 493 { 494 Name: "testing", 495 Items: []*libyaml.ConfigItem{ 496 { 497 Name: "cluster info", 498 Value: "", 499 Default: "", 500 Required: true, 501 Hidden: false, 502 }, 503 { 504 Name: "worker replicas", 505 Value: "", 506 Default: "", 507 Required: true, 508 Hidden: false, 509 }, 510 }, 511 }, 512 { 513 Name: "testing", 514 Items: []*libyaml.ConfigItem{ 515 { 516 Name: "semver", 517 Value: "", 518 Default: "", 519 Required: true, 520 Hidden: false, 521 }, 522 }, 523 }, 524 { 525 Name: "testing", 526 Items: []*libyaml.ConfigItem{ 527 { 528 Name: "alpha", 529 Value: "hello world", 530 Default: "", 531 Required: false, 532 Hidden: false, 533 }, 534 { 535 Name: "beta", 536 Value: `{{repl ConfigOption "alpha" }}`, 537 Default: "", 538 Required: false, 539 ReadOnly: true, 540 Hidden: true, 541 }, 542 { 543 Name: "charlie", 544 Value: `{{repl ConfigOption "beta" }}`, 545 Default: "", 546 Required: false, 547 ReadOnly: true, 548 Hidden: false, 549 }, 550 }, 551 }, 552 }, 553 }, 554 }, 555 }, 556 ExpectedConfig: map[string]interface{}{"alpha": "100", "beta": "100", "charlie": "100"}, 557 ExpectedError: true, 558 }, 559 { 560 Name: "beta value resolves to alpha default", 561 State: []byte(`{"v1":{"config":{}}}`), 562 Release: &api.Release{ 563 Spec: api.Spec{ 564 Config: api.Config{ 565 V1: []libyaml.ConfigGroup{{ 566 Name: "testing", 567 Items: []*libyaml.ConfigItem{ 568 { 569 Name: "alpha", 570 Value: "", 571 Default: "100", 572 Required: false, 573 Hidden: false, 574 }, 575 { 576 Name: "beta", 577 Value: `{{repl ConfigOption "alpha" }}`, 578 Default: "", 579 Required: false, 580 ReadOnly: true, 581 }, 582 }, 583 }}, 584 }, 585 }, 586 }, 587 ExpectedConfig: map[string]interface{}{"alpha": "100", "beta": "100"}, 588 ExpectedError: false, 589 }, 590 } 591 592 for _, test := range tests { 593 t.Run(test.Name, func(t *testing.T) { 594 req := require.New(t) 595 596 fakeFS := afero.Afero{Fs: afero.NewMemMapFs()} 597 598 err := fakeFS.WriteFile(constants.StatePath, test.State, 0666) 599 req.NoError(err) 600 601 v := viper.New() 602 testLogger := &logger.TestLogger{T: t} 603 604 builder := &templates.BuilderBuilder{ 605 Logger: testLogger, 606 Viper: v, 607 } 608 609 manager, err := state.NewDisposableManager(testLogger, fakeFS, v) 610 req.NoError(err) 611 612 resolver := &resolve.APIConfigRenderer{ 613 Logger: testLogger, 614 Viper: v, 615 BuilderBuilder: builder, 616 } 617 618 daemon := &HeadlessDaemon{ 619 StateManager: manager, 620 Logger: testLogger, 621 ConfigRenderer: resolver, 622 UI: cli.NewMockUi(), 623 } 624 625 ctx := context.Background() 626 627 err = daemon.HeadlessResolve(ctx, test.Release) 628 if test.ExpectedError { 629 req.Error(err) 630 } else { 631 resolvedConfig := daemon.ResolvedConfig 632 req.Equal(test.ExpectedConfig, resolvedConfig) 633 } 634 635 err = manager.CommitState() 636 req.NoError(err) 637 638 updatedState, err := fakeFS.ReadFile(constants.StatePath) 639 req.NoError(err) 640 641 var indented bytes.Buffer 642 err = json.Indent(&indented, test.State, "", " ") 643 req.NoError(err) 644 645 req.Equal(indented.Bytes(), updatedState) 646 }) 647 } 648 }