|
5 | 5 | "github.com/AliceO2Group/Control/core/task/sm" |
6 | 6 | . "github.com/onsi/ginkgo/v2" |
7 | 7 | . "github.com/onsi/gomega" |
| 8 | + "gopkg.in/yaml.v3" |
8 | 9 | ) |
9 | 10 |
|
10 | 11 | func complexRoleTree() (root Role, leaves map[string]Role) { |
@@ -254,4 +255,266 @@ var _ = Describe("role", func() { |
254 | 255 | }) |
255 | 256 | }) |
256 | 257 | }) |
| 258 | + |
| 259 | + Describe("unmarshaling a YAML workflow template into a tree of roles", func() { |
| 260 | + Context("when the YAML template is a simple tree with a single task", func() { |
| 261 | + const yamlTemplate = ` |
| 262 | +name: root_role |
| 263 | +description: description of the root role |
| 264 | +defaults: |
| 265 | + default1: "true" |
| 266 | + default2: value of default2 |
| 267 | + default3: false |
| 268 | +vars: |
| 269 | + var1: "{{ default2 != 'none' }}" |
| 270 | + var2: "{{ default1 == 'true' && default3 == 'true' }}" |
| 271 | + var3: value of var3 |
| 272 | +roles: |
| 273 | + - name: "first_role" |
| 274 | + enabled: "{{ default1 == 'true' }}" |
| 275 | + vars: |
| 276 | + var3: "{{ default2 }}" |
| 277 | + constraints: |
| 278 | + - attribute: some_attribute |
| 279 | + value: "{{ default2 }}" |
| 280 | + roles: |
| 281 | + - name: 'first_subrole' |
| 282 | + vars: |
| 283 | + var4: '{{default1 == "true" ? var3 : "value of var4"}}' |
| 284 | + task: |
| 285 | + load: name_of_task1 |
| 286 | + - name: "second_subrole" |
| 287 | + enabled: 'false' |
| 288 | + roles: |
| 289 | + - name: "first_subsubrole" |
| 290 | + connect: |
| 291 | + - name: connection_name |
| 292 | + type: pull |
| 293 | + target: "{{ Up(2).Path }}.first_subrole:connection_name" |
| 294 | + rateLogging: "{{ default1 }}" |
| 295 | + task: |
| 296 | + load: name_of_task2 |
| 297 | + - name: "second_role" |
| 298 | + call: |
| 299 | + func: testplugin.Noop() |
| 300 | + trigger: CONFIGURE |
| 301 | + timeout: 1s |
| 302 | + critical: false |
| 303 | +` |
| 304 | + role := new(aggregatorRole) |
| 305 | + |
| 306 | + It("should unmarshal successfully", func() { |
| 307 | + err := yaml.Unmarshal([]byte(yamlTemplate), role) |
| 308 | + Expect(err).NotTo(HaveOccurred()) |
| 309 | + }) |
| 310 | + It("should create a tree with a task role and a call role", func() { |
| 311 | + Expect(role.GetName()).To(Equal("root_role")) |
| 312 | + Expect(role.GetRoles()).To(HaveLen(2)) |
| 313 | + |
| 314 | + Expect(role.GetRoles()[0].GetName()).To(Equal("first_role")) |
| 315 | + Expect(role.GetRoles()[0]).To(BeAssignableToTypeOf(&aggregatorRole{})) |
| 316 | + Expect(role.GetRoles()[0]).NotTo(BeAssignableToTypeOf(&callRole{})) |
| 317 | + Expect(role.GetRoles()[0].GetRoles()).To(HaveLen(2)) |
| 318 | + |
| 319 | + Expect(role.GetRoles()[0].GetRoles()[0].GetName()).To(Equal("first_subrole")) |
| 320 | + Expect(role.GetRoles()[0].GetRoles()[0]).To(BeAssignableToTypeOf(&taskRole{})) |
| 321 | + |
| 322 | + Expect(role.GetRoles()[0].GetRoles()[1].GetName()).To(Equal("second_subrole")) |
| 323 | + Expect(role.GetRoles()[0].GetRoles()[1]).To(BeAssignableToTypeOf(&aggregatorRole{})) |
| 324 | + Expect(role.GetRoles()[0].GetRoles()[1].GetRoles()).To(HaveLen(1)) |
| 325 | + |
| 326 | + Expect(role.GetRoles()[0].GetRoles()[1].GetRoles()[0].GetName()).To(Equal("first_subsubrole")) |
| 327 | + Expect(role.GetRoles()[0].GetRoles()[1].GetRoles()[0]).To(BeAssignableToTypeOf(&taskRole{})) |
| 328 | + |
| 329 | + Expect(role.GetRoles()[1].GetName()).To(Equal("second_role")) |
| 330 | + Expect(role.GetRoles()[1]).To(BeAssignableToTypeOf(&callRole{})) |
| 331 | + Expect(role.GetRoles()[1]).NotTo(BeAssignableToTypeOf(&taskRole{})) |
| 332 | + }) |
| 333 | + It("should set the variables correctly", func() { |
| 334 | + Expect(role.GetDefaults().Raw()).To(HaveLen(3)) |
| 335 | + Expect(role.GetRoles()[0].GetRoles()[0].GetVars().Raw()).To(HaveLen(1)) |
| 336 | + Expect(role.GetRoles()[0].GetRoles()[1].GetVars().Raw()).To(HaveLen(0)) |
| 337 | + Expect(role.GetRoles()[0].GetRoles()[0].ConsolidatedVarStack()).To(HaveLen(7)) |
| 338 | + Expect(role.GetRoles()[0].GetRoles()[1].ConsolidatedVarStack()).To(HaveLen(6)) |
| 339 | + Expect(role.GetRoles()[0].GetRoles()[0].GetVars().Raw()["var4"]).To(Equal("{{default1 == \"true\" ? var3 : \"value of var4\"}}")) |
| 340 | + }) |
| 341 | + }) |
| 342 | + |
| 343 | + Context("when the YAML template resembles readout-dataflow", func() { |
| 344 | + const yamlTemplate = ` |
| 345 | +name: !public readout-dataflow |
| 346 | +description: !public "Main workflow template for ALICE data taking" |
| 347 | +defaults: |
| 348 | + ############################### |
| 349 | + # General Configuration Panel |
| 350 | + ############################### |
| 351 | + dcs_enabled: !public |
| 352 | + value: "false" |
| 353 | + type: bool |
| 354 | + label: "DCS" |
| 355 | + description: "Enable/disable DCS SOR/EOR commands" |
| 356 | + widget: checkBox |
| 357 | + panel: General_Configuration |
| 358 | + index: 0 |
| 359 | + dd_enabled: !public |
| 360 | + value: "true" |
| 361 | + type: bool |
| 362 | + label: "Data Distribution (FLP)" |
| 363 | + description: "Enable/disable Data Distribution components running on FLPs (StfBuilder and StfSender)" |
| 364 | + widget: checkBox |
| 365 | + panel: General_Configuration |
| 366 | + index: 1 |
| 367 | + hosts: '["host1", "host2"]' |
| 368 | +vars: |
| 369 | + auto_stop_enabled: "{{ auto_stop_timeout != 'none' }}" |
| 370 | + ddsched_enabled: "{{ epn_enabled == 'true' && dd_enabled == 'true' }}" |
| 371 | +roles: |
| 372 | + ########################### |
| 373 | + # Start of CTP Readout role |
| 374 | + ########################### |
| 375 | + - name: "readout-ctp" |
| 376 | + enabled: "{{ ctp_readout_enabled == 'true' }}" |
| 377 | + vars: |
| 378 | + detector: "{{ctp_readout_enabled == 'true' ? inventory.DetectorForHost( ctp_readout_host ) : \"\" }}" |
| 379 | + readout_cfg_uri_standalone: "consul-ini://{{ consul_endpoint }}/o2/components/{{config.ResolvePath('readout/' + run_type + '/any/readout-standalone-' + ctp_readout_host)}}" |
| 380 | + readout_cfg_uri_stfb: "consul-ini://{{ consul_endpoint }}/o2/components/{{config.Resolve('readout', run_type, 'any', 'readout-stfb-' + ctp_readout_host)}}" |
| 381 | + dd_discovery_ib_hostname: "{{ ctp_readout_host }}-ib" # MUST be defined for all stfb and stfs |
| 382 | + # dpl_workflow is set to ctp_dpl_workflow |
| 383 | + dpl_workflow: "{{ util.PrefixedOverride( 'dpl_workflow', 'ctp' ) }}" |
| 384 | + dpl_command: "{{ util.PrefixedOverride( 'dpl_command', 'ctp' ) }}" |
| 385 | + stfs_shm_segment_size: "{{ ctp_stfs_shm_segment_size }}" |
| 386 | + it: "{{ ctp_readout_host }}" |
| 387 | + constraints: |
| 388 | + - attribute: machine_id |
| 389 | + value: "{{ ctp_readout_host }}" |
| 390 | + roles: |
| 391 | + - name: "readout" |
| 392 | + vars: |
| 393 | + readout_cfg_uri: '{{dd_enabled == "true" ? readout_cfg_uri_stfb : readout_cfg_uri_standalone}}' |
| 394 | + task: |
| 395 | + load: readout-ctp |
| 396 | + - name: "data-distribution" |
| 397 | + enabled: "{{dd_enabled == 'true' && (qcdd_enabled == 'false' && minimal_dpl_enabled == 'false' && dpl_workflow == 'none' && dpl_command == 'none')}}" |
| 398 | + roles: |
| 399 | + # stfb-standalone not supported on CTP machine |
| 400 | + # if ctp_readout_enabled, we also assume stfb_standalone is false |
| 401 | + - name: "stfb" |
| 402 | + vars: |
| 403 | + dd_discovery_stfb_id: stfb-{{ ctp_readout_host }}-{{ uid.New() }} # must be defined for all stfb roles |
| 404 | + connect: |
| 405 | + - name: readout |
| 406 | + type: pull |
| 407 | + target: "{{ Up(2).Path }}.readout:readout" |
| 408 | + rateLogging: "{{ fmq_rate_logging }}" |
| 409 | + task: |
| 410 | + load: stfbuilder-senderoutput |
| 411 | + - name: host-{{ it }} |
| 412 | + for: |
| 413 | + range: "{{ hosts }}" |
| 414 | + var: it |
| 415 | + vars: |
| 416 | + detector: "{{ inventory.DetectorForHost( it ) }}" |
| 417 | + readout_cfg_uri_standalone: "consul-ini://{{ consul_endpoint }}/o2/components/{{config.ResolvePath('readout/' + run_type + '/any/readout-standalone-' + it)}}" |
| 418 | + readout_cfg_uri_stfb: "consul-ini://{{ consul_endpoint }}/o2/components/{{config.Resolve('readout', run_type, 'any', 'readout-stfb-' + it)}}" |
| 419 | + dd_discovery_ib_hostname: "{{ it }}-ib" # MUST be defined for all stfb and stfs |
| 420 | + # dpl_workflow is set to <detector>_dpl_workflow if such an override exists |
| 421 | + dpl_workflow: "{{ util.PrefixedOverride( 'dpl_workflow', strings.ToLower( inventory.DetectorForHost( it ) ) ) }}" |
| 422 | + dpl_command: "{{ util.PrefixedOverride( 'dpl_command', strings.ToLower( inventory.DetectorForHost( it ) ) ) }}" |
| 423 | + constraints: |
| 424 | + - attribute: machine_id |
| 425 | + value: "{{ it }}" |
| 426 | + roles: |
| 427 | + - name: "readout" |
| 428 | + vars: |
| 429 | + readout_cfg_uri: '{{dd_enabled == "true" ? readout_cfg_uri_stfb : readout_cfg_uri_standalone}}' |
| 430 | + task: |
| 431 | + load: readout |
| 432 | + - name: dcs |
| 433 | + enabled: "{{dcs_enabled == 'true'}}" |
| 434 | + defaults: |
| 435 | + ############################### |
| 436 | + # DCS Panel |
| 437 | + ############################### |
| 438 | + dcs_detectors: "{{ detectors }}" |
| 439 | + dcs_sor_parameters: !public |
| 440 | + value: "{}" |
| 441 | + type: string |
| 442 | + label: "Global SOR parameters" |
| 443 | + description: "additional parameters for the DCS SOR" |
| 444 | + widget: editBox |
| 445 | + panel: DCS |
| 446 | + index: 2 |
| 447 | + visibleif: $$dcs_enabled === "true" |
| 448 | + dcs_eor_parameters: !public |
| 449 | + value: "{}" |
| 450 | + type: string |
| 451 | + label: "Global EOR parameters" |
| 452 | + description: "additional parameters for the DCS EOR" |
| 453 | + widget: editBox |
| 454 | + panel: DCS |
| 455 | + index: 3 |
| 456 | + visibleif: $$dcs_enabled === "true" |
| 457 | + roles: |
| 458 | + - name: pfr |
| 459 | + call: |
| 460 | + func: dcs.PrepareForRun() |
| 461 | + trigger: before_CONFIGURE |
| 462 | + await: after_CONFIGURE |
| 463 | + timeout: "{{ dcs_pfr_timeout }}" |
| 464 | + critical: false |
| 465 | + - name: sor |
| 466 | + call: |
| 467 | + func: dcs.StartOfRun() |
| 468 | + trigger: before_START_ACTIVITY |
| 469 | + timeout: "{{ dcs_sor_timeout }}" |
| 470 | + critical: true |
| 471 | +` |
| 472 | + role := new(aggregatorRole) |
| 473 | + |
| 474 | + It("should unmarshal successfully", func() { |
| 475 | + err := yaml.Unmarshal([]byte(yamlTemplate), role) |
| 476 | + Expect(err).NotTo(HaveOccurred()) |
| 477 | + }) |
| 478 | + |
| 479 | + It("should create a complex tree correctly", func() { |
| 480 | + Expect(role.GetName()).To(Equal("readout-dataflow")) |
| 481 | + Expect(role.GetRoles()).To(HaveLen(2)) // GetRoles excludes iterator roles |
| 482 | + Expect(role.Roles).To(HaveLen(3)) |
| 483 | + Expect(role.GetRoles()[0].GetName()).To(Equal("readout-ctp")) |
| 484 | + |
| 485 | + Expect(role.Roles[1].GetName()).To(Equal("host-{{ it }}")) |
| 486 | + Expect(role.Roles[1]).To(BeAssignableToTypeOf(&iteratorRole{})) |
| 487 | + |
| 488 | + Expect(role.GetRoles()[1]).To(BeAssignableToTypeOf(&aggregatorRole{})) |
| 489 | + Expect(role.GetRoles()[1].GetRoles()).To(HaveLen(2)) |
| 490 | + Expect(role.GetRoles()[1].GetRoles()[0]).To(BeAssignableToTypeOf(&callRole{})) |
| 491 | + }) |
| 492 | + |
| 493 | + It("should set the variables correctly", func() { |
| 494 | + Expect(role.GetDefaults().Raw()).To(HaveLen(3)) |
| 495 | + Expect(role.GetRoles()[0].GetVars().Raw()).To(HaveLen(8)) |
| 496 | + Expect(role.Roles[1].GetVars().Raw()).To(HaveLen(6)) |
| 497 | + Expect(role.GetRoles()[1].GetVars().Raw()).To(HaveLen(0)) |
| 498 | + |
| 499 | + // CTP subtree |
| 500 | + cvs, err := role.GetRoles()[0].GetRoles()[1].GetRoles()[0].ConsolidatedVarStack() |
| 501 | + Expect(err).NotTo(HaveOccurred()) |
| 502 | + Expect(cvs).To(HaveLen(14)) |
| 503 | + |
| 504 | + Expect(cvs["dd_enabled"]).To(Equal("true")) |
| 505 | + Expect(cvs["readout_cfg_uri_stfb"]).To(Equal("consul-ini://{{ consul_endpoint }}/o2/components/{{config.Resolve('readout', run_type, 'any', 'readout-stfb-' + ctp_readout_host)}}")) |
| 506 | + Expect(cvs["dd_discovery_ib_hostname"]).To(Equal("{{ ctp_readout_host }}-ib")) |
| 507 | + Expect(cvs["ddsched_enabled"]).To(Equal("{{ epn_enabled == 'true' && dd_enabled == 'true' }}")) |
| 508 | + |
| 509 | + // DCS subtree |
| 510 | + cvs, err = role.GetRoles()[1].GetRoles()[0].ConsolidatedVarStack() |
| 511 | + Expect(err).NotTo(HaveOccurred()) |
| 512 | + Expect(cvs).To(HaveLen(8)) |
| 513 | + |
| 514 | + Expect(cvs["dcs_enabled"]).To(Equal("false")) |
| 515 | + Expect(cvs["dcs_detectors"]).To(Equal("{{ detectors }}")) |
| 516 | + Expect(cvs["dcs_sor_parameters"]).To(Equal("{}")) |
| 517 | + }) |
| 518 | + }) |
| 519 | + }) |
257 | 520 | }) |
0 commit comments