Browse Source

Now FPB tasks schema in sync with Fuel 9.0 with 2.1 support

YAQL expressions added.
Tasks 2.0 and 2.1 schema coverage improved.

Change-Id: If433f29283cb4897e8137ba2b33215af14103bea
Closes-Bug: #1590389
tags/4.1.0
Ilya Kutukov 3 years ago
parent
commit
c2d906f5ae

+ 405
- 0
fuel_plugin_builder/tests/test_validator_v4.py View File

@@ -493,6 +493,411 @@ class TestValidatorV4(TestValidatorV3):
493 493
     def test_check_tasks_schema_validation_passed(self, utils_mock, *args):
494 494
         pass
495 495
 
496
+    @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
497
+    def test_check_tasks_schema_1_0_validation_failed(self, utils_mock, *args):
498
+        checks = [
499
+            {
500
+                'data': {
501
+                    'id': 'task-id',
502
+                    'type': 'shell',
503
+                    'parameters': {
504
+                        'timeout': 3
505
+                    },
506
+                    'stage': 'post_deployment',
507
+                    'role': '*'
508
+                },
509
+                'errorTextContains': "'cmd' is a required property, "
510
+                                     "value path '0 -> parameters'"
511
+            },
512
+            {
513
+                'data': {
514
+                    'id': 'task-id',
515
+                    'type': 'puppet',
516
+                    'parameters': {
517
+                        'timeout': 3
518
+                    },
519
+                    'stage': 'post_deployment',
520
+                    'role': '*'
521
+                },
522
+                'errorTextContains': "'puppet_manifest' is a required property"
523
+                                     ", value path '0 -> parameters'"
524
+            },
525
+            {
526
+                'data': {
527
+                    'id': 'task-id',
528
+                    'type': 'puppet',
529
+                    'parameters': {
530
+                        'timeout': 3,
531
+                        'cmd': 'xx'
532
+                    },
533
+                    'stage': 'post_deployment',
534
+                    'role': '*'
535
+                },
536
+                'errorTextContains': "'puppet_manifest' is a required property"
537
+                                     ", value path '0 -> parameters'"
538
+            },
539
+            {
540
+                'data': {
541
+                    'id': 'task-id',
542
+                    'type': 'shell',
543
+                    'parameters': {
544
+                        'timeout': 3,
545
+                        'puppet_manifest': 'xx',
546
+                        'puppet_modules': 'yy',
547
+                    },
548
+                    'stage': 'post_deployment',
549
+                    'role': '*'
550
+                },
551
+                'errorTextContains': "'cmd' is a required property, value path"
552
+                                     " '0 -> parameters'"
553
+            },
554
+            {
555
+                'data': {
556
+                    'id': 'task-id',
557
+                    'type': 'puppet',
558
+                    'parameters': {
559
+                        'timeout': 3,
560
+                        'puppet_manifest': 'xx',
561
+                        'puppet_modules': 'yy',
562
+                        'retries': 'asd',
563
+                    },
564
+                    'stage': 'post_deployment',
565
+                    'role': '*'
566
+                },
567
+                'errorTextContains': "'asd' is not of type 'integer', value "
568
+                                     "path '0 -> parameters -> retries'"
569
+            },
570
+            {
571
+                'data': {
572
+                    'id': 'task-id',
573
+                    'type': 'puppet',
574
+                    'parameters': {
575
+                        'timeout': 3,
576
+                        'puppet_manifest': 'xx',
577
+                        'puppet_modules': '',
578
+                        'retries': 1,
579
+                    },
580
+                    'stage': 'pre_deployment',
581
+                    'role': '*'
582
+                },
583
+                'errorTextContains': "'' is too short, value path '0 -> "
584
+                                     "parameters -> puppet_modules'"
585
+            },
586
+            {
587
+                'data': {
588
+                    'id': 'task-id',
589
+                    'type': 'puppet',
590
+                    'parameters': {
591
+                        'timeout': 3,
592
+                        'puppet_manifest': '',
593
+                        'puppet_modules': 'yy',
594
+                        'retries': 1,
595
+                    },
596
+                    'stage': 'pre_deployment',
597
+                    'role': '*'
598
+                },
599
+                'errorTextContains': "'' is too short, value path '0 -> "
600
+                                     "parameters -> puppet_manifest'"
601
+            }
602
+        ]
603
+
604
+        for check in checks:
605
+            utils_mock.parse_yaml.return_value = [check['data']]
606
+            self.assertRaisesRegexp(
607
+                errors.ValidationError,
608
+                check['errorTextContains'],
609
+                self.validator.check_deployment_tasks)
610
+
611
+    @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
612
+    def test_check_tasks_schema_1_0_validation_passed(self, utils_mock, *args):
613
+        data_sets = [
614
+            [
615
+                {
616
+                    'id': 'task_id',
617
+                    'type': 'shell',
618
+                    'parameters': {
619
+                        'timeout': 3,
620
+                        'cmd': 'xx'
621
+                    },
622
+                    'stage': 'post_deployment',
623
+                    'role': '*'
624
+                },
625
+            ],
626
+            [
627
+                {
628
+                    'id': 'task_id',
629
+                    'type': 'shell',
630
+                    'parameters': {
631
+                        'timeout': 3,
632
+                        'cmd': 'xx'
633
+                    },
634
+                    'stage': 'post_deployment',
635
+                    'role': '*'
636
+                },
637
+                {
638
+                    'id': 'task_id',
639
+                    'type': 'puppet',
640
+                    'parameters': {
641
+                        'timeout': 3,
642
+                        'puppet_manifest': 'xx',
643
+                        'puppet_modules': 'xxx'
644
+                    },
645
+                    'stage': 'post_deployment',
646
+                    'role': '*'
647
+                },
648
+            ],
649
+            [
650
+                {
651
+                    'id': 'task_id',
652
+                    'type': 'shell',
653
+                    'parameters': {
654
+                        'timeout': 3,
655
+                        'cmd': 'reboot'
656
+                    },
657
+                    'stage': 'post_deployment',
658
+                    'role': '*'
659
+                },
660
+                {
661
+                    'id': 'task_id',
662
+                    'type': 'shell',
663
+                    'parameters': {
664
+                        'timeout': 3,
665
+                        'cmd': 'xx'
666
+                    },
667
+                    'stage': 'post_deployment',
668
+                    'role': '*'
669
+                },
670
+                {
671
+                    'id': 'task_id',
672
+                    'type': 'puppet',
673
+                    'parameters': {
674
+                        'timeout': 3,
675
+                        'puppet_manifest': 'xx',
676
+                        'puppet_modules': 'xxx'
677
+                    },
678
+                    'stage': 'post_deployment',
679
+                    'role': '*'
680
+                }
681
+            ],
682
+            [
683
+                {
684
+                    'id': 'task_id',
685
+                    'type': 'shell',
686
+                    'parameters': {
687
+                        'timeout': 3,
688
+                        'cmd': 'reboot'
689
+                    },
690
+                    'stage': 'post_deployment',
691
+                    'role': '*'
692
+                },
693
+                {
694
+                    'id': 'task_id',
695
+                    'type': 'shell',
696
+                    'parameters': {
697
+                        'timeout': 3,
698
+                        'puppet_manifest': 'xx',
699
+                        'puppet_modules': 'yy',
700
+                        'cmd': 'reboot'
701
+                    },
702
+                    'stage': 'post_deployment',
703
+                    'role': '*'
704
+                },
705
+                {
706
+                    'id': 'task_id',
707
+                    'type': 'puppet',
708
+                    'parameters': {
709
+                        'timeout': 3,
710
+                        'retries': 10,
711
+                        'puppet_manifest': 'xx',
712
+                        'puppet_modules': 'xxx'
713
+                    },
714
+                    'stage': 'post_deployment',
715
+                    'role': '*'
716
+                },
717
+                {
718
+                    'id': 'task_id',
719
+                    'type': 'puppet',
720
+                    'parameters': {
721
+                        'timeout': 3,
722
+                        'retries': 10,
723
+                        'puppet_manifest': 'xx',
724
+                        'puppet_modules': 'xxx'
725
+                    },
726
+                    'stage': 'post_deployment',
727
+                    'role': 'master'
728
+                },
729
+            ]
730
+        ]
731
+
732
+        for data in data_sets:
733
+            utils_mock.parse_yaml.return_value = data
734
+            self.validator.check_deployment_tasks()
735
+
736
+    @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
737
+    def test_check_tasks_schema_2_0_validation_failed(self, utils_mock, *args):
738
+        tasks_data = [
739
+            {
740
+                'id': 'test',
741
+                'type': 'shell',
742
+                'version': '2'
743
+            },
744
+            {
745
+                'id': 'test',
746
+                'type': 'shell',
747
+                'cross-depends': [
748
+                    {
749
+                        'role': 'role_without_name'
750
+                    }
751
+                ]
752
+            },
753
+            {
754
+                'id': 'test',
755
+                'type': 'shell',
756
+                'parameters': {
757
+                    'strategy': 'NOSUCHSTRATEGY'
758
+                }
759
+            },
760
+            {
761
+                'id': 'test',
762
+                'type': 'shell',
763
+                'parameters': {
764
+                    'strategy': {
765
+                        'type': 'NOSUCHSTRATEGY'
766
+                    }
767
+                }
768
+            }
769
+        ]
770
+
771
+        utils_mock.parse_yaml.return_value = tasks_data
772
+        self.assertRaises(errors.ValidationError,
773
+                          self.validator.check_deployment_tasks)
774
+
775
+    @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
776
+    def test_check_tasks_schema_2_0_validation_passed(self, utils_mock, *args):
777
+        tasks_data = [
778
+            {
779
+                'id': 'task_id',
780
+                'type': 'puppet',
781
+                'version': '2.0.0',
782
+                'parameters': {
783
+                    'timeout': 3,
784
+                    'retries': 10,
785
+                    'puppet_manifest': 'xx',
786
+                    'puppet_modules': 'xxx'
787
+                },
788
+                'stage': 'post_deployment',
789
+                'roles': ['test_role'],
790
+                'cross-depends': [
791
+                    {
792
+                        'name': 'task_id2',
793
+                    },
794
+                    {
795
+                        'name': 'task_id2',
796
+                        'role': ['some_role']
797
+                    },
798
+                    {
799
+                        'name': 'task_id2',
800
+                        'role': 'some_role'
801
+                    },
802
+                    {
803
+                        'name': 'task_id2',
804
+                        'policy': 'all'
805
+                    },
806
+                    {
807
+                        'name': 'task_id2',
808
+                        'policy': 'any'
809
+                    }
810
+                ],
811
+                'cross-depended-by': [
812
+                    {
813
+                        'name': 'task_id2',
814
+                    },
815
+                    {
816
+                        'name': 'task_id2',
817
+                        'role': ['some_role']
818
+                    },
819
+                    {
820
+                        'name': 'task_id2',
821
+                        'role': 'some_role'
822
+                    },
823
+                    {
824
+                        'name': 'task_id2',
825
+                        'policy': 'all'
826
+                    },
827
+                    {
828
+                        'name': 'task_id2',
829
+                        'policy': 'any'
830
+                    }
831
+                ],
832
+                'strategy': {
833
+                    'type': 'parallel',
834
+                    'amount': 10
835
+                }
836
+            }
837
+        ]
838
+
839
+        utils_mock.parse_yaml.return_value = tasks_data
840
+        self.validator.check_deployment_tasks()
841
+
842
+    @mock.patch('fuel_plugin_builder.validators.validator_v4.utils')
843
+    def test_check_tasks_schema_2_1_validation_passed(self, utils_mock, *args):
844
+        # this is a slightly modified task from netconfig.yaml
845
+        tasks_data = [
846
+            {
847
+                "id": "netconfig",
848
+                "type": "puppet",
849
+                "version": "2.1.0",
850
+                "groups": [
851
+                    "primary-controller",
852
+                    "controller",
853
+                ],
854
+                "required_for": [
855
+                    "deploy_end"
856
+                ],
857
+                "requires": [
858
+                    "tools"
859
+                ],
860
+                "condition": {
861
+                    "yaql_exp": "changedAny($.network_scheme, $.dpdk, $.get('"
862
+                                "use_ovs'), $.get('set_rps'), $.get('set_rps')"
863
+                                ", $.get('run_ping_checker'), $.network_scheme"
864
+                                ".endpoints.values().where(\n  $.get('gateway'"
865
+                                ") != null).gateway)\n"
866
+                },
867
+                "parameters": {
868
+                    "puppet_manifest": "/etc/puppet/modules/osnailyfacter/"
869
+                                       "modular/netconfig/netconfig.pp",
870
+                    "puppet_modules": "/etc/puppet/modules",
871
+                    "timeout": 300,
872
+                    "strategy": {
873
+                        "type": "parallel",
874
+                        "amount": {
875
+                            "yaql_exp": "switch($.get('deployed_before', {})."
876
+                                        "get('value') => 1, true => 3)\n"
877
+                        }
878
+                    }
879
+                },
880
+                "test_pre": {
881
+                    "cmd": "ruby /etc/puppet/modules/osnailyfacter/modular/"
882
+                           "netconfig/netconfig_pre.rb"
883
+                },
884
+                "test_post": {
885
+                    "cmd": "ruby /etc/puppet/modules/osnailyfacter/modular/"
886
+                           "netconfig/netconfig_post.rb"
887
+                },
888
+                "cross-depends": {
889
+                    "yaql_exp": "switch(   (\n    $.roles.any($.matches('("
890
+                                "primary-)?(controller|mongo)'))\n    "
891
+                                "or ($.network_metadata.get('vips',{}).get"
892
+                                "('management') = null)\n  ) => [],\n  "
893
+                                "true => [{name =>'virtual_ips'}]\n)\n"
894
+                }
895
+            }
896
+        ]
897
+
898
+        utils_mock.parse_yaml.return_value = tasks_data
899
+        self.validator.check_deployment_tasks()
900
+
496 901
     @mock.patch('fuel_plugin_builder.validators.base.utils.exists')
497 902
     def test_check_tasks_schema_validation_no_file(self, exists_mock, *args):
498 903
         mocked_methods = ['validate_schema']

+ 49
- 12
fuel_plugin_builder/validators/schemas/v4.py View File

@@ -47,18 +47,43 @@ class SchemaV4(SchemaV3):
47 47
         self.roleless_tasks = ROLELESS_TASKS
48 48
         self.role_aliases = ROLE_ALIASES
49 49
 
50
+    @property
51
+    def _node_resolve_policy(self):
52
+        return {
53
+            'type': 'string',
54
+            'enum': ['all', 'any']
55
+        }
56
+
57
+    @property
58
+    def _yaql_expression(self):
59
+        return {
60
+            '$schema': 'http://json-schema.org/draft-04/schema#',
61
+            'type': 'object',
62
+            'required': ['yaql_exp'],
63
+            'properties': {
64
+                'yaql_exp': {'type': 'string'},
65
+            }
66
+        }
67
+
50 68
     @property
51 69
     def _task_relation(self):
52 70
         return {
71
+            '$schema': 'http://json-schema.org/draft-04/schema#',
53 72
             'type': 'object',
54 73
             'required': ['name'],
55 74
             'properties': {
56
-                'name': {'type': 'string'},
57
-                'role': self._task_role,
58
-                'policy': {
59
-                    'type': 'string',
60
-                    'enum': ['all', 'any']
61
-                }
75
+                'name': {
76
+                    'oneOf': [
77
+                        {'type': 'string'},
78
+                        self._yaql_expression],
79
+                },
80
+                'role': {
81
+                    'oneOf': [
82
+                        {'type': 'string'},
83
+                        {'type': 'array'},
84
+                        self._yaql_expression]
85
+                },
86
+                'policy': self._node_resolve_policy,
62 87
             }
63 88
         }
64 89
 
@@ -83,10 +108,18 @@ class SchemaV4(SchemaV3):
83 108
     @property
84 109
     def _task_strategy(self):
85 110
         return {
111
+            '$schema': 'http://json-schema.org/draft-04/schema#',
86 112
             'type': 'object',
113
+            'required': ['type'],
87 114
             'properties': {
88 115
                 'type': {
89
-                    'enum': ['parallel', 'one_by_one']
116
+                    'type': 'string',
117
+                    'enum': ['parallel', 'one_by_one']},
118
+                'amount': {
119
+                    'oneOf': [
120
+                        {'type': 'integer'},
121
+                        self._yaql_expression
122
+                    ]
90 123
                 }
91 124
             }
92 125
         }
@@ -149,11 +182,15 @@ class SchemaV4(SchemaV3):
149 182
                 'required_for': self.task_group,
150 183
                 'requires': self.task_group,
151 184
                 'cross-depends': {
152
-                    'type': 'array',
153
-                    'items': self._task_relation},
185
+                    'oneOf': [
186
+                        {'type': 'array', 'items': self._task_relation},
187
+                        self._yaql_expression]
188
+                },
154 189
                 'cross-depended-by': {
155
-                    'type': 'array',
156
-                    'items': self._task_relation},
190
+                    'oneOf': [
191
+                        {'type': 'array', 'items': self._task_relation},
192
+                        self._yaql_expression]
193
+                },
157 194
                 'stage': self._task_stage,
158 195
                 'tasks': {  # used only for 'group' tasks
159 196
                     'type': 'array',
@@ -161,7 +198,7 @@ class SchemaV4(SchemaV3):
161 198
                         'type': 'string',
162 199
                         'pattern': TASK_ROLE_PATTERN}},
163 200
                 'reexecute_on': self._task_reexecute,
164
-                'parameters': parameters or {},
201
+                'parameters': parameters,
165 202
             },
166 203
         }
167 204
 

Loading…
Cancel
Save