Merge lp:~zyga/checkbox/fix-1297746 into lp:checkbox

Proposed by Zygmunt Krynicki
Status: Merged
Approved by: Daniel Manrique
Approved revision: 2852
Merged at revision: 2854
Proposed branch: lp:~zyga/checkbox/fix-1297746
Merge into: lp:checkbox
Diff against target: 505 lines (+153/-53)
6 files modified
plainbox/plainbox/impl/job.py (+35/-20)
plainbox/plainbox/impl/secure/providers/test_v1.py (+10/-7)
plainbox/plainbox/impl/secure/providers/v1.py (+55/-10)
plainbox/plainbox/impl/test_job.py (+16/-8)
plainbox/plainbox/provider_manager.py (+13/-1)
plainbox/plainbox/test_provider_manager.py (+24/-7)
To merge this branch: bzr merge lp:~zyga/checkbox/fix-1297746
Reviewer Review Type Date Requested Status
Daniel Manrique (community) Approve
Review via email: mp+213341@code.launchpad.net

Description of the change

5a7415f plainbox:job: don't require the id field
3a845c8 plainbox:job: validate the presence of job.partial_id
eddc7e0 plainbox:job: drop validator_cls argument
17f4314 plainbox:job: add Problem.deprecated
ad51337 plainbox:job: add strictness control to job validator
3f05db4 plainbox:secure:providers: drop useless import
c1974b4 plainbox:secure:providers: expose control for job validation
b2e6e20 plainbox:provider_manager: enforce strong validation in `manage.py validate`
dd9036e plainbox:provider_manager: handle deprecated fields

To post a comment you must log in.
lp:~zyga/checkbox/fix-1297746 updated
2848. By Zygmunt Krynicki

plainbox:job: add strictness control to job validator

This patch adds two optional arguments to the validator, strict, which
effectively runs the validator at maximum strictness and deprecated
which makes it find deprecated fields or values.

The default settings will ignore harmless errors (thus making the
validator do no evil) while still reporting (and preventing) serious
problems that would definitely fail at job runtime.

Signed-off-by: Zygmunt Krynicki <email address hidden>

2849. By Zygmunt Krynicki

plainbox:secure:providers: drop useless import

Signed-off-by: Zygmunt Krynicki <email address hidden>

2850. By Zygmunt Krynicki

plainbox:secure:providers: expose control for job validation

This patch allows two settings (validate and validation_kwargs) to propagate
from Provider1 to JobDefinitionPlugIn. By default validation is enabled
but no validation keywords are supplied so only serious problems are
"reported" (as in preventing a job definition from being loaded).
Those can be changed by passing the two new keyword-only arguments.

They are passed from Provider1.from_definition(),
through Provider1.__init__() and to JobDefinitionPlugIn where they control
the validation block. The second argument (validation_kwargs) is passed
directly to the validator to control its behavior.

Some tests were altered not to overly validate everything where it is
obvious we don't care about valid jobs, just about getting some dummy
data across.

Signed-off-by: Zygmunt Krynicki <email address hidden>

2851. By Zygmunt Krynicki

plainbox:provider_manager: enforce strong validation in `manage.py validate`

This patch ironically allows the validate command to load broken job
definitions and inspect them manually inside the command core.

Signed-off-by: Zygmunt Krynicki <email address hidden>

2852. By Zygmunt Krynicki

plainbox:provider_manager: handle deprecated fields

This patch makes the `manage.py validate` command support the
Problem.deprecated problem which is used to report 'name' being used
inside job definitions.

Signed-off-by: Zygmunt Krynicki <email address hidden>

Revision history for this message
Daniel Manrique (roadmr) wrote :

Awesome, maybe next we can plug this validation into the tarmac tests to protect against borked jobs?

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plainbox/plainbox/impl/job.py'
--- plainbox/plainbox/impl/job.py 2014-03-27 13:57:14 +0000
+++ plainbox/plainbox/impl/job.py 2014-03-28 22:08:58 +0000
@@ -50,6 +50,7 @@
50 missing = 'missing'50 missing = 'missing'
51 wrong = 'wrong'51 wrong = 'wrong'
52 useless = 'useless'52 useless = 'useless'
53 deprecated = 'deprecated'
5354
5455
55class ValidationError(ValueError):56class ValidationError(ValueError):
@@ -75,15 +76,27 @@
75 """76 """
7677
77 @staticmethod78 @staticmethod
78 def validate(job):79 def validate(job, strict=False, deprecated=False):
79 """80 """
80 Validate the specified job81 Validate the specified job
82
83 :param strict:
84 Enforce strict validation. Non-conforming jobs will be rejected.
85 This is off by default to ensure that non-critical errors don't
86 prevent jobs from running.
87 :param deprecated:
88 Enforce deprecation validation. Jobs having deprecated fields will
89 be rejected. This is off by default to allow backwards compatible
90 jobs to be used without any changes.
81 """91 """
82 # Check if id is empty92 # Check if name is still being used, if running in strict mode
83 if job.id is None:93 if deprecated and job.name is not None:
94 raise ValidationError(job.fields.name, Problem.deprecated)
95 # Check if the partial_id field is empty
96 if job.partial_id is None:
84 raise ValidationError(job.fields.id, Problem.missing)97 raise ValidationError(job.fields.id, Problem.missing)
85 # Check if summary is empty98 # Check if summary is empty, if running in strict mode
86 if job.summary is None:99 if strict and job.summary is None:
87 raise ValidationError(job.fields.summary, Problem.missing)100 raise ValidationError(job.fields.summary, Problem.missing)
88 # Check if plugin is empty101 # Check if plugin is empty
89 if job.plugin is None:102 if job.plugin is None:
@@ -91,11 +104,13 @@
91 # Check if plugin has a good value104 # Check if plugin has a good value
92 if job.plugin not in JobDefinition.plugin.get_all_symbols():105 if job.plugin not in JobDefinition.plugin.get_all_symbols():
93 raise ValidationError(job.fields.plugin, Problem.wrong)106 raise ValidationError(job.fields.plugin, Problem.wrong)
94 # Check if user is given without a command to run107 # Check if user is given without a command to run, if running in strict
95 if job.user is not None and job.command is None:108 # mode
109 if strict and job.user is not None and job.command is None:
96 raise ValidationError(job.fields.user, Problem.useless)110 raise ValidationError(job.fields.user, Problem.useless)
97 # Check if environ is given without a command to run111 # Check if environ is given without a command to run, if running in
98 if job.environ is not None and job.command is None:112 # strict mode
113 if strict and job.environ is not None and job.command is None:
99 raise ValidationError(job.fields.environ, Problem.useless)114 raise ValidationError(job.fields.environ, Problem.useless)
100 # Verify that command is present on a job within the subset that should115 # Verify that command is present on a job within the subset that should
101 # really have them (shell, local, resource, attachment, user-verify and116 # really have them (shell, local, resource, attachment, user-verify and
@@ -121,8 +136,9 @@
121 if job.description is None:136 if job.description is None:
122 raise ValidationError(137 raise ValidationError(
123 job.fields.description, Problem.missing)138 job.fields.description, Problem.missing)
124 # Ensure that manual jobs don't have command139 # Ensure that manual jobs don't have command, if running in strict
125 if job.command is not None:140 # mode
141 if strict and job.command is not None:
126 raise ValidationError(job.fields.command, Problem.useless)142 raise ValidationError(job.fields.command, Problem.useless)
127143
128144
@@ -481,16 +497,12 @@
481 @classmethod497 @classmethod
482 def from_rfc822_record(cls, record):498 def from_rfc822_record(cls, record):
483 """499 """
484 Create a JobDefinition instance from rfc822 record500 Create a JobDefinition instance from rfc822 record. The resulting
501 instance may not be valid but will always be created. Only valid jobs
502 should be executed.
485503
486 The record must be a RFC822Record instance.504 The record must be a RFC822Record instance.
487
488 Only the 'id' and 'plugin' keys are required.
489 All other data is stored as is and is entirely optional.
490 """505 """
491 if 'id' not in record.data and 'name' not in record.data:
492 # TRANSLATORS: don't translate id or translate it as 'id field'
493 raise ValueError(_("Cannot create job without an id"))
494 # Strip the trailing newlines form all the raw values coming from the506 # Strip the trailing newlines form all the raw values coming from the
495 # RFC822 parser. We don't need them and they don't match gettext keys507 # RFC822 parser. We don't need them and they don't match gettext keys
496 # (xgettext strips out those newlines)508 # (xgettext strips out those newlines)
@@ -498,14 +510,17 @@
498 key: value.rstrip('\n')510 key: value.rstrip('\n')
499 for key, value in record.raw_data.items()})511 for key, value in record.raw_data.items()})
500512
501 def validate(self, validator_cls=CheckBoxJobValidator):513 def validate(self, **validation_kwargs):
502 """514 """
503 Validate this job definition with the specified validator515 Validate this job definition with the specified validator
504516
517 :param validation_kwargs:
518 Keyword arguments to pass to the
519 :meth:`CheckBoxJobValidator.validate()`
505 :raises ValidationError:520 :raises ValidationError:
506 If the job has any problems that make it unsuitable for execution.521 If the job has any problems that make it unsuitable for execution.
507 """522 """
508 validator_cls.validate(self)523 CheckBoxJobValidator.validate(self, **validation_kwargs)
509524
510 def create_child_job_from_record(self, record):525 def create_child_job_from_record(self, record):
511 """526 """
512527
=== modified file 'plainbox/plainbox/impl/secure/providers/test_v1.py'
--- plainbox/plainbox/impl/secure/providers/test_v1.py 2014-03-28 13:02:24 +0000
+++ plainbox/plainbox/impl/secure/providers/test_v1.py 2014-03-28 22:08:58 +0000
@@ -25,7 +25,6 @@
25"""25"""
2626
27from unittest import TestCase27from unittest import TestCase
28import os
2928
30from plainbox.impl.job import JobDefinition29from plainbox.impl.job import JobDefinition
31from plainbox.impl.secure.config import Unset30from plainbox.impl.secure.config import Unset
@@ -649,7 +648,10 @@
649 self.provider = Provider1(648 self.provider = Provider1(
650 self.NAME, self.VERSION, self.DESCRIPTION, self.SECURE,649 self.NAME, self.VERSION, self.DESCRIPTION, self.SECURE,
651 self.GETTEXT_DOMAIN, self.JOBS_DIR, self.WHITELISTS_DIR,650 self.GETTEXT_DOMAIN, self.JOBS_DIR, self.WHITELISTS_DIR,
652 self.DATA_DIR, self.BIN_DIR, self.LOCALE_DIR, self.BASE_DIR)651 self.DATA_DIR, self.BIN_DIR, self.LOCALE_DIR, self.BASE_DIR,
652 # We are using dummy job definitions so let's not shout about those
653 # being invalid in each test
654 validate=False)
653655
654 def test_repr(self):656 def test_repr(self):
655 self.assertEqual(657 self.assertEqual(
@@ -792,11 +794,11 @@
792 JobDefinitionPlugIn("/path/to/jobs1.txt", (794 JobDefinitionPlugIn("/path/to/jobs1.txt", (
793 "id: a2\n"795 "id: a2\n"
794 "\n"796 "\n"
795 "id: a1\n"), self.provider),797 "id: a1\n"), self.provider, validate=False),
796 JobDefinitionPlugIn("/path/to/jobs2.txt", (798 JobDefinitionPlugIn("/path/to/jobs2.txt", (
797 "id: a3\n"799 "id: a3\n"
798 "\n"800 "\n"
799 "id: a4\n"), self.provider)801 "id: a4\n"), self.provider, validate=False)
800 ]802 ]
801 with self.provider._job_collection.fake_plugins(fake_plugins):803 with self.provider._job_collection.fake_plugins(fake_plugins):
802 job_list = self.provider.get_builtin_jobs()804 job_list = self.provider.get_builtin_jobs()
@@ -840,11 +842,11 @@
840 JobDefinitionPlugIn("/path/to/jobs1.txt", (842 JobDefinitionPlugIn("/path/to/jobs1.txt", (
841 "id: a2\n"843 "id: a2\n"
842 "\n"844 "\n"
843 "id: a1\n"), self.provider),845 "id: a1\n"), self.provider, validate=False),
844 JobDefinitionPlugIn("/path/to/jobs2.txt", (846 JobDefinitionPlugIn("/path/to/jobs2.txt", (
845 "id: a3\n"847 "id: a3\n"
846 "\n"848 "\n"
847 "id: a4\n"), self.provider)849 "id: a4\n"), self.provider, validate=False)
848 ]850 ]
849 with self.provider._job_collection.fake_plugins(fake_plugins):851 with self.provider._job_collection.fake_plugins(fake_plugins):
850 job_list, problem_list = self.provider.load_all_jobs()852 job_list, problem_list = self.provider.load_all_jobs()
@@ -862,7 +864,8 @@
862 """864 """
863 fake_plugins = [865 fake_plugins = [
864 JobDefinitionPlugIn(866 JobDefinitionPlugIn(
865 "/path/to/jobs1.txt", "id: working\n", self.provider)867 "/path/to/jobs1.txt", "id: working\n", self.provider,
868 validate=False)
866 ]869 ]
867 fake_problems = [870 fake_problems = [
868 PlugInError("some problem"),871 PlugInError("some problem"),
869872
=== modified file 'plainbox/plainbox/impl/secure/providers/v1.py'
--- plainbox/plainbox/impl/secure/providers/v1.py 2014-03-28 13:02:24 +0000
+++ plainbox/plainbox/impl/secure/providers/v1.py 2014-03-28 22:08:58 +0000
@@ -31,6 +31,7 @@
31from plainbox.abc import IProvider1, IProviderBackend131from plainbox.abc import IProvider1, IProviderBackend1
32from plainbox.i18n import gettext as _32from plainbox.i18n import gettext as _
33from plainbox.impl.job import JobDefinition33from plainbox.impl.job import JobDefinition
34from plainbox.impl.job import ValidationError
34from plainbox.impl.secure.config import Config, Variable35from plainbox.impl.secure.config import Config, Variable
35from plainbox.impl.secure.config import IValidator36from plainbox.impl.secure.config import IValidator
36from plainbox.impl.secure.config import NotEmptyValidator37from plainbox.impl.secure.config import NotEmptyValidator
@@ -87,12 +88,30 @@
87 list of :class:`plainbox.impl.job.JobDefinition` instances from a file.88 list of :class:`plainbox.impl.job.JobDefinition` instances from a file.
88 """89 """
8990
90 def __init__(self, filename, text, provider):91 def __init__(self, filename, text, provider, *,
92 validate=True, validation_kwargs=None):
91 """93 """
92 Initialize the plug-in with the specified name text94 Initialize the plug-in with the specified name text
95
96 :param filename:
97 Name of the file with job definitions
98 :param text:
99 Full text of the file with job definitions
100 :param provider:
101 A provider object to which those jobs belong to
102 :param validate:
103 Enable job validation. Incorrect job definitions will not be loaded
104 and will abort the process of loading of the remainder of the jobs.
105 This is ON by default to prevent broken job definitions from being
106 used. This is a keyword-only argument.
107 :param validation_kwargs:
108 Keyword arguments to pass to the JobDefinition.validate(). Note,
109 this is a single argument. This is a keyword-only argument.
93 """110 """
94 self._filename = filename111 self._filename = filename
95 self._job_list = []112 self._job_list = []
113 if validation_kwargs is None:
114 validation_kwargs = {}
96 logger.debug(_("Loading jobs definitions from %r..."), filename)115 logger.debug(_("Loading jobs definitions from %r..."), filename)
97 try:116 try:
98 records = load_rfc822_records(117 records = load_rfc822_records(
@@ -108,10 +127,16 @@
108 raise PlugInError(127 raise PlugInError(
109 _("Cannot define job from record {!r}: {}").format(128 _("Cannot define job from record {!r}: {}").format(
110 record, exc))129 record, exc))
111 else:130 job._provider = provider
112 job._provider = provider131 if validate:
113 self._job_list.append(job)132 try:
114 logger.debug(_("Loaded %r"), job)133 job.validate(**validation_kwargs)
134 except ValidationError as exc:
135 raise PlugInError(
136 _("Problem in job definition, field {}: {}").format(
137 exc.field, exc.problem))
138 self._job_list.append(job)
139 logger.debug(_("Loaded %r"), job)
115140
116 @property141 @property
117 def plugin_name(self):142 def plugin_name(self):
@@ -142,7 +167,7 @@
142167
143 def __init__(self, name, version, description, secure, gettext_domain,168 def __init__(self, name, version, description, secure, gettext_domain,
144 jobs_dir, whitelists_dir, data_dir, bin_dir, locale_dir,169 jobs_dir, whitelists_dir, data_dir, bin_dir, locale_dir,
145 base_dir):170 base_dir, *, validate=True, validation_kwargs=None):
146 """171 """
147 Initialize a provider with a set of meta-data and directories.172 Initialize a provider with a set of meta-data and directories.
148173
@@ -187,6 +212,16 @@
187 path of the directory with (perhaps) all of jobs_dir,212 path of the directory with (perhaps) all of jobs_dir,
188 whitelist_dir, data_dir, bin_dir, locale_dir. This may be None.213 whitelist_dir, data_dir, bin_dir, locale_dir. This may be None.
189 This is also the effective value of $CHECKBOX_SHARE214 This is also the effective value of $CHECKBOX_SHARE
215
216 :param validate:
217 Enable job validation. Incorrect job definitions will not be loaded
218 and will abort the process of loading of the remainder of the jobs.
219 This is ON by default to prevent broken job definitions from being
220 used. This is a keyword-only argument.
221
222 :param validation_kwargs:
223 Keyword arguments to pass to the JobDefinition.validate(). Note,
224 this is a single argument. This is a keyword-only argument.
190 """225 """
191 # Meta-data226 # Meta-data
192 self._name = name227 self._name = name
@@ -215,22 +250,31 @@
215 jobs_dir_list = []250 jobs_dir_list = []
216 self._job_collection = FsPlugInCollection(251 self._job_collection = FsPlugInCollection(
217 jobs_dir_list, ext=(".txt", ".txt.in"),252 jobs_dir_list, ext=(".txt", ".txt.in"),
218 wrapper=JobDefinitionPlugIn, provider=self)253 wrapper=JobDefinitionPlugIn, provider=self,
254 validate=validate, validation_kwargs=validation_kwargs)
219 # Setup translations255 # Setup translations
220 if gettext_domain and locale_dir:256 if gettext_domain and locale_dir:
221 gettext.bindtextdomain(self._gettext_domain, self._locale_dir)257 gettext.bindtextdomain(self._gettext_domain, self._locale_dir)
222258
223 @classmethod259 @classmethod
224 def from_definition(cls, definition, secure):260 def from_definition(cls, definition, secure, *, validate=True,
261 validation_kwargs=None):
225 """262 """
226 Initialize a provider from Provider1Definition object263 Initialize a provider from Provider1Definition object
227264
228 :param definition:265 :param definition:
229 A Provider1Definition object to use as reference266 A Provider1Definition object to use as reference
230
231 :param secure:267 :param secure:
232 Value of the secure flag. This cannot be expressed by a definition268 Value of the secure flag. This cannot be expressed by a definition
233 object.269 object.
270 :param validate:
271 Enable job validation. Incorrect job definitions will not be loaded
272 and will abort the process of loading of the remainder of the jobs.
273 This is ON by default to prevent broken job definitions from being
274 used. This is a keyword-only argument.
275 :param validation_kwargs:
276 Keyword arguments to pass to the JobDefinition.validate(). Note,
277 this is a single argument. This is a keyword-only argument.
234278
235 This method simplifies initialization of a Provider1 object where the279 This method simplifies initialization of a Provider1 object where the
236 caller already has a Provider1Definition object. Depending on the value280 caller already has a Provider1Definition object. Depending on the value
@@ -248,7 +292,8 @@
248 secure, definition.effective_gettext_domain,292 secure, definition.effective_gettext_domain,
249 definition.effective_jobs_dir, definition.effective_whitelists_dir,293 definition.effective_jobs_dir, definition.effective_whitelists_dir,
250 definition.effective_data_dir, definition.effective_bin_dir,294 definition.effective_data_dir, definition.effective_bin_dir,
251 definition.effective_locale_dir, definition.location or None)295 definition.effective_locale_dir, definition.location or None,
296 validate=validate, validation_kwargs=validation_kwargs)
252297
253 def __repr__(self):298 def __repr__(self):
254 return "<{} name:{!r}>".format(self.__class__.__name__, self.name)299 return "<{} name:{!r}>".format(self.__class__.__name__, self.name)
255300
=== modified file 'plainbox/plainbox/impl/test_job.py'
--- plainbox/plainbox/impl/test_job.py 2014-03-27 13:57:24 +0000
+++ plainbox/plainbox/impl/test_job.py 2014-03-28 22:08:58 +0000
@@ -42,6 +42,19 @@
4242
43class CheckBoxJobValidatorTests(TestCase):43class CheckBoxJobValidatorTests(TestCase):
4444
45 def test_validate_checks_for_deprecated_name(self):
46 """
47 verify that validate() checks if jobs have a value for the 'id'
48 field.
49 """
50 job = JobDefinition({
51 'name': 'name'
52 })
53 with self.assertRaises(ValidationError) as boom:
54 CheckBoxJobValidator.validate(job, deprecated=True)
55 self.assertEqual(boom.exception.field, JobDefinition.fields.name)
56 self.assertEqual(boom.exception.problem, Problem.deprecated)
57
45 def test_validate_checks_for_missing_id(self):58 def test_validate_checks_for_missing_id(self):
46 """59 """
47 verify that validate() checks if jobs have a value for the 'id'60 verify that validate() checks if jobs have a value for the 'id'
@@ -91,7 +104,7 @@
91 'user': 'root'104 'user': 'root'
92 })105 })
93 with self.assertRaises(ValidationError) as boom:106 with self.assertRaises(ValidationError) as boom:
94 CheckBoxJobValidator.validate(job)107 CheckBoxJobValidator.validate(job, strict=True)
95 self.assertEqual(boom.exception.field, JobDefinition.fields.user)108 self.assertEqual(boom.exception.field, JobDefinition.fields.user)
96 self.assertEqual(boom.exception.problem, Problem.useless)109 self.assertEqual(boom.exception.problem, Problem.useless)
97110
@@ -106,7 +119,7 @@
106 'environ': 'VAR_NAME'119 'environ': 'VAR_NAME'
107 })120 })
108 with self.assertRaises(ValidationError) as boom:121 with self.assertRaises(ValidationError) as boom:
109 CheckBoxJobValidator.validate(job)122 CheckBoxJobValidator.validate(job, strict=True)
110 self.assertEqual(boom.exception.field, JobDefinition.fields.environ)123 self.assertEqual(boom.exception.field, JobDefinition.fields.environ)
111 self.assertEqual(boom.exception.problem, Problem.useless)124 self.assertEqual(boom.exception.problem, Problem.useless)
112125
@@ -137,7 +150,7 @@
137 'command': 'run_some_test'150 'command': 'run_some_test'
138 })151 })
139 with self.assertRaises(ValidationError) as boom:152 with self.assertRaises(ValidationError) as boom:
140 CheckBoxJobValidator.validate(job)153 CheckBoxJobValidator.validate(job, strict=True)
141 self.assertEqual(boom.exception.field, JobDefinition.fields.command)154 self.assertEqual(boom.exception.field, JobDefinition.fields.command)
142 self.assertEqual(boom.exception.problem, Problem.useless)155 self.assertEqual(boom.exception.problem, Problem.useless)
143156
@@ -254,11 +267,6 @@
254 self.assertEqual(job.command, None)267 self.assertEqual(job.command, None)
255 self.assertEqual(job.description, None)268 self.assertEqual(job.description, None)
256269
257 def test_from_rfc822_record_missing_id(self):
258 record = RFC822Record({'plugin': 'plugin'})
259 with self.assertRaises(ValueError):
260 JobDefinition.from_rfc822_record(record)
261
262 def test_str(self):270 def test_str(self):
263 job = JobDefinition(self._min_record.data)271 job = JobDefinition(self._min_record.data)
264 self.assertEqual(str(job), "id")272 self.assertEqual(str(job), "id")
265273
=== modified file 'plainbox/plainbox/provider_manager.py'
--- plainbox/plainbox/provider_manager.py 2014-03-28 13:22:04 +0000
+++ plainbox/plainbox/provider_manager.py 2014-03-28 22:08:58 +0000
@@ -623,7 +623,7 @@
623 problem_list = []623 problem_list = []
624 for job in job_list:624 for job in job_list:
625 try:625 try:
626 job.validate()626 job.validate(strict=True, deprecated=True)
627 except JobValidationError as exc:627 except JobValidationError as exc:
628 problem_list.append((job, exc))628 problem_list.append((job, exc))
629 return problem_list629 return problem_list
@@ -636,6 +636,7 @@
636 Problem.missing: _("missing definition of required field"),636 Problem.missing: _("missing definition of required field"),
637 Problem.wrong: _("incorrect value supplied"),637 Problem.wrong: _("incorrect value supplied"),
638 Problem.useless: _("useless field in this context"),638 Problem.useless: _("useless field in this context"),
639 Problem.deprecated: _("usage of deprecated field"),
639 }640 }
640 for job, error in problem_list:641 for job, error in problem_list:
641 if isinstance(error, JobValidationError):642 if isinstance(error, JobValidationError):
@@ -664,6 +665,17 @@
664 else:665 else:
665 print(_("All jobs seem to be valid"))666 print(_("All jobs seem to be valid"))
666667
668 def get_provider(self):
669 """
670 Get a Provider1 that describes the current provider
671
672 This version disables all validation so that we can see totally broken
673 providers and let us validate them and handle the errors explicitly.
674 """
675 return Provider1.from_definition(
676 # NOTE: don't validate, we want to validate manually
677 self.definition, secure=False, validate=False)
678
667679
668@docstring(680@docstring(
669 # TRANSLATORS: please leave various options (both long and short forms),681 # TRANSLATORS: please leave various options (both long and short forms),
670682
=== modified file 'plainbox/plainbox/test_provider_manager.py'
--- plainbox/plainbox/test_provider_manager.py 2014-03-28 13:22:04 +0000
+++ plainbox/plainbox/test_provider_manager.py 2014-03-28 22:08:58 +0000
@@ -120,7 +120,7 @@
120 self.tmpdir + os.path.join("/foo", "lib", "plainbox-providers-1",120 self.tmpdir + os.path.join("/foo", "lib", "plainbox-providers-1",
121 "2014.com.example:test", "jobs",121 "2014.com.example:test", "jobs",
122 "jobs.txt"),122 "jobs.txt"),
123 "name: dummy\nplugin: shell\ncommand: true\n")123 "id: dummy\nplugin: shell\ncommand: true\n")
124124
125 def test_install__flat_partial(self):125 def test_install__flat_partial(self):
126 """126 """
@@ -155,7 +155,7 @@
155 self.assertFileContent(155 self.assertFileContent(
156 self.tmpdir + os.path.join(156 self.tmpdir + os.path.join(
157 prefix, "share", "2014.com.example:test", "jobs", "jobs.txt"),157 prefix, "share", "2014.com.example:test", "jobs", "jobs.txt"),
158 "name: dummy\nplugin: shell\ncommand: true\n")158 "id: dummy\nplugin: shell\ncommand: true\n")
159 self.assertFileContent(159 self.assertFileContent(
160 self.tmpdir + os.path.join(160 self.tmpdir + os.path.join(
161 prefix, "share", "2014.com.example:test", "whitelists",161 prefix, "share", "2014.com.example:test", "whitelists",
@@ -198,7 +198,7 @@
198 self.tmpdir, "dist", "2014.com.example.test-1.0.tar.gz")198 self.tmpdir, "dist", "2014.com.example.test-1.0.tar.gz")
199 self.assertTarballContent(199 self.assertTarballContent(
200 tarball, "2014.com.example.test-1.0/jobs/jobs.txt",200 tarball, "2014.com.example.test-1.0/jobs/jobs.txt",
201 "name: dummy\nplugin: shell\ncommand: true\n")201 "id: dummy\nplugin: shell\ncommand: true\n")
202 self.assert_common_sdist(tarball)202 self.assert_common_sdist(tarball)
203203
204 def test_sdist__partial(self):204 def test_sdist__partial(self):
@@ -287,7 +287,7 @@
287 """287 """
288 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")288 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")
289 with open(filename, "wt", encoding='UTF-8') as stream:289 with open(filename, "wt", encoding='UTF-8') as stream:
290 print("name: broken", file=stream)290 print("id: broken", file=stream)
291 print("plugin: shell", file=stream)291 print("plugin: shell", file=stream)
292 with TestIO() as test_io:292 with TestIO() as test_io:
293 self.tool.main(["validate"])293 self.tool.main(["validate"])
@@ -303,7 +303,7 @@
303 """303 """
304 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")304 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")
305 with open(filename, "wt", encoding='UTF-8') as stream:305 with open(filename, "wt", encoding='UTF-8') as stream:
306 print("name: broken", file=stream)306 print("id: broken", file=stream)
307 print("plugin: magic", file=stream)307 print("plugin: magic", file=stream)
308 with TestIO() as test_io:308 with TestIO() as test_io:
309 self.tool.main(["validate"])309 self.tool.main(["validate"])
@@ -321,7 +321,7 @@
321 """321 """
322 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")322 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")
323 with open(filename, "wt", encoding='UTF-8') as stream:323 with open(filename, "wt", encoding='UTF-8') as stream:
324 print("name: broken", file=stream)324 print("id: broken", file=stream)
325 print("plugin: manual", file=stream)325 print("plugin: manual", file=stream)
326 print("description: broken job definition", file=stream)326 print("description: broken job definition", file=stream)
327 print("command: true", file=stream)327 print("command: true", file=stream)
@@ -332,6 +332,23 @@
332 "jobs/broken.txt:1-4: job '2014.com.example::broken', field 'command': "332 "jobs/broken.txt:1-4: job '2014.com.example::broken', field 'command': "
333 "useless field in this context\n"))333 "useless field in this context\n"))
334334
335 def test_validate__broken_deprecated_field(self):
336 """
337 verify that ./manage.py validate shows information about deprecated fields
338 """
339 filename = os.path.join(self.tmpdir, "jobs", "broken.txt")
340 with open(filename, "wt", encoding='UTF-8') as stream:
341 print("name: broken", file=stream)
342 print("plugin: manual", file=stream)
343 print("description: broken job definition", file=stream)
344 print("command: true", file=stream)
345 with TestIO() as test_io:
346 self.tool.main(["validate"])
347 self.assertEqual(
348 test_io.stdout, (
349 "jobs/broken.txt:1-4: job '2014.com.example::broken', field 'name': "
350 "usage of deprecated field\n"))
351
335 def test_info(self):352 def test_info(self):
336 """353 """
337 verify that ./manage.py info shows basic provider information354 verify that ./manage.py info shows basic provider information
@@ -359,7 +376,7 @@
359 os.mkdir(os.path.join(tmpdir, "jobs"))376 os.mkdir(os.path.join(tmpdir, "jobs"))
360 filename = os.path.join(tmpdir, "jobs", "jobs.txt")377 filename = os.path.join(tmpdir, "jobs", "jobs.txt")
361 with open(filename, "wt", encoding='UTF-8') as stream:378 with open(filename, "wt", encoding='UTF-8') as stream:
362 print("name: dummy", file=stream)379 print("id: dummy", file=stream)
363 print("plugin: shell", file=stream)380 print("plugin: shell", file=stream)
364 print("command: true", file=stream)381 print("command: true", file=stream)
365 os.mkdir(os.path.join(tmpdir, "whitelists"))382 os.mkdir(os.path.join(tmpdir, "whitelists"))

Subscribers

People subscribed via source and target branches