Merge "Remove layoutvalidator" into feature/zuulv3
This commit is contained in:
commit
e7800a008b
|
@ -1,18 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
not_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: test-org/test
|
||||
check:
|
||||
- test-merge
|
||||
- test-test
|
|
@ -1,40 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
# merge-failure-message needs a string.
|
||||
merge-failure-message:
|
||||
|
||||
- name: gate
|
||||
manager: DependentPipelineManager
|
||||
failure-message: Build failed. For information on how to proceed, see http://wiki.example.org/Test_Failures
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: comment-added
|
||||
approval:
|
||||
- approved: 1
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 2
|
||||
submit: true
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -2
|
||||
merge-failure:
|
||||
start:
|
||||
review_gerrit:
|
||||
verified: 0
|
||||
precedence: high
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
check:
|
||||
- project-check
|
|
@ -1,13 +0,0 @@
|
|||
pipelines:
|
||||
- name: 'check'
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
ref: /some/ref/path
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- project-check
|
|
@ -1,2 +0,0 @@
|
|||
# Pipelines completely missing. At least one is required.
|
||||
pipelines:
|
|
@ -1,8 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
||||
# merge-mode must be one of merge, merge-resolve, cherry-pick.
|
||||
merge-mode: foo
|
|
@ -1,7 +0,0 @@
|
|||
pipelines:
|
||||
# name is required for pipelines
|
||||
- noname: check
|
||||
manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,8 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
# The manager must be one of IndependentPipelineManager
|
||||
# or DependentPipelineManager
|
||||
manager: NonexistentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,10 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
gerrit:
|
||||
# non-event is not a valid gerrit event
|
||||
- event: non-event
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,11 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
# event is a required item but it is missing.
|
||||
- approval:
|
||||
- approved: 1
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,11 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: comment-added
|
||||
# approved is not a valid entry. Should be approval.
|
||||
approved: 1
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,6 +0,0 @@
|
|||
pipelines:
|
||||
# The pipeline must have a name.
|
||||
- manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,6 +0,0 @@
|
|||
pipelines:
|
||||
# The pipeline must have a manager
|
||||
- name: check
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,9 +0,0 @@
|
|||
pipelines:
|
||||
# Names must be unique.
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
|
@ -1,10 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
||||
# gate pipeline is not defined.
|
||||
gate:
|
||||
- test
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
|
||||
projects:
|
||||
- name: foo
|
||||
check:
|
||||
# Indentation is one level too deep on the last line.
|
||||
- test
|
||||
- foo
|
|
@ -1,21 +0,0 @@
|
|||
# Template is going to be called but missing a parameter
|
||||
|
||||
pipelines:
|
||||
- name: 'check'
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
open: True
|
||||
current-patchset: True
|
||||
approval:
|
||||
- verified: [1, 2]
|
||||
username: jenkins
|
||||
- workflow: 1
|
||||
reject:
|
||||
# Reject only takes 'approval', has no need for open etc..
|
||||
open: True
|
||||
approval:
|
||||
- code-review: [-1, -2]
|
||||
username: core-person
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
|
@ -1,28 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
jobs:
|
||||
- name: ^.*$
|
||||
swift:
|
||||
- name: logs
|
||||
- name: ^.*-merge$
|
||||
# swift requires a name
|
||||
swift:
|
||||
container: merge_assets
|
||||
failure-message: Unable to merge change
|
||||
|
||||
projects:
|
||||
- name: test-org/test
|
||||
check:
|
||||
- test-merge
|
||||
- test-test
|
|
@ -1,20 +0,0 @@
|
|||
# Template is going to be called but missing a parameter
|
||||
|
||||
pipelines:
|
||||
- name: 'check'
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
|
||||
project-templates:
|
||||
- name: template-generic
|
||||
check:
|
||||
# Template uses the 'project' parameter' which must be provided
|
||||
- '{project}-merge'
|
||||
|
||||
projects:
|
||||
- name: organization/project
|
||||
template:
|
||||
- name: template-generic
|
||||
# Here we 'forgot' to pass 'project'
|
|
@ -1,23 +0,0 @@
|
|||
# Template is going to be called with an extra parameter
|
||||
|
||||
pipelines:
|
||||
- name: 'check'
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
|
||||
project-templates:
|
||||
- name: template-generic
|
||||
check:
|
||||
# Template only uses the 'project' parameter'
|
||||
- '{project}-merge'
|
||||
|
||||
projects:
|
||||
- name: organization/project
|
||||
template:
|
||||
- name: template-generic
|
||||
project: 'MyProjectName'
|
||||
# Feed an extra parameters which is not going to be used
|
||||
# by the template. That is an error.
|
||||
extraparam: 'IShouldNotBeSet'
|
|
@ -1,10 +0,0 @@
|
|||
# Template refers to an unexisting pipeline
|
||||
|
||||
project-templates:
|
||||
- name: template-generic
|
||||
unexisting-pipeline: # pipeline does not exist
|
||||
|
||||
projects:
|
||||
- name: organization/project
|
||||
template:
|
||||
- name: template-generic
|
|
@ -1,42 +0,0 @@
|
|||
[gearman]
|
||||
server=127.0.0.1
|
||||
|
||||
[zuul]
|
||||
layout_config=layout.yaml
|
||||
url_pattern=http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}
|
||||
job_name_in_report=true
|
||||
|
||||
[merger]
|
||||
git_dir=/tmp/zuul-test/git
|
||||
git_user_email=zuul@example.com
|
||||
git_user_name=zuul
|
||||
zuul_url=http://zuul.example.com/p
|
||||
|
||||
[swift]
|
||||
authurl=https://identity.api.example.org/v2.0/
|
||||
user=username
|
||||
key=password
|
||||
tenant_name=" "
|
||||
|
||||
default_container=logs
|
||||
region_name=EXP
|
||||
logserver_prefix=http://logs.example.org/server.app/
|
||||
|
||||
[connection review_gerrit]
|
||||
driver=gerrit
|
||||
server=review.example.com
|
||||
user=jenkins
|
||||
sshkey=none
|
||||
|
||||
[connection other_gerrit]
|
||||
driver=gerrit
|
||||
server=review2.example.com
|
||||
user=jenkins2
|
||||
sshkey=none
|
||||
|
||||
[connection my_smtp]
|
||||
driver=smtp
|
||||
server=localhost
|
||||
port=25
|
||||
default_from=zuul@example.com
|
||||
default_to=you@example.com
|
|
@ -1,18 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
source: review_gerrit
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
other_gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
check:
|
||||
- project-check
|
|
@ -1,102 +0,0 @@
|
|||
includes:
|
||||
- python-file: openstack_functions.py
|
||||
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
require:
|
||||
open: True
|
||||
current-patchset: True
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- verified: [-1, -2]
|
||||
username: jenkins
|
||||
approval:
|
||||
- workflow: 1
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: post
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: ref-updated
|
||||
ref: ^(?!refs/).*$
|
||||
ignore-deletes: True
|
||||
|
||||
- name: gate
|
||||
manager: DependentPipelineManager
|
||||
success-message: Your change is awesome.
|
||||
failure-message: Build failed. For information on how to proceed, see http://wiki.example.org/Test_Failures
|
||||
require:
|
||||
open: True
|
||||
current-patchset: True
|
||||
approval:
|
||||
- verified: [1, 2]
|
||||
username: jenkins
|
||||
- workflow: 1
|
||||
reject:
|
||||
approval:
|
||||
- code-review: [-1, -2]
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: comment-added
|
||||
approval:
|
||||
- approved: 1
|
||||
start:
|
||||
review_gerrit:
|
||||
verified: 0
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 2
|
||||
code-review: 1
|
||||
submit: true
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -2
|
||||
workinprogress: true
|
||||
|
||||
- name: merge-check
|
||||
manager: IndependentPipelineManager
|
||||
source: review_gerrit
|
||||
ignore-dependencies: true
|
||||
trigger:
|
||||
zuul:
|
||||
- event: project-change-merged
|
||||
merge-failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
jobs:
|
||||
- name: ^.*-merge$
|
||||
failure-message: Unable to merge change
|
||||
hold-following-changes: true
|
||||
- name: test-merge
|
||||
parameter-function: devstack_params
|
||||
- name: test-test
|
||||
- name: test-merge2
|
||||
success-pattern: http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}/success
|
||||
failure-pattern: http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}/fail
|
||||
- name: project-testfile
|
||||
files:
|
||||
- 'tools/.*-requires'
|
||||
|
||||
projects:
|
||||
- name: test-org/test
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- test-merge2:
|
||||
- test-thing1:
|
||||
- test-thing2
|
||||
- test-thing3
|
||||
gate:
|
||||
- test-thing
|
||||
post:
|
||||
- test-post
|
|
@ -1,53 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
merge-failure-message: "Could not merge the change. Please rebase..."
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: post
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: ref-updated
|
||||
ref: ^(?!refs/).*$
|
||||
merge-failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
- name: gate
|
||||
manager: DependentPipelineManager
|
||||
failure-message: Build failed. For information on how to proceed, see http://wiki.example.org/Test_Failures
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: comment-added
|
||||
approval:
|
||||
- approved: 1
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 2
|
||||
submit: true
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -2
|
||||
merge-failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
my_smtp:
|
||||
to: you@example.com
|
||||
start:
|
||||
review_gerrit:
|
||||
verified: 0
|
||||
precedence: high
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
check:
|
||||
- project-check
|
|
@ -1,36 +0,0 @@
|
|||
includes:
|
||||
- python-file: custom_functions.py
|
||||
|
||||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- username: jenkins
|
||||
older-than: 48h
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- email: jenkins@example.com
|
||||
newer-than: 48h
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- approved: 1
|
||||
- event: comment-added
|
||||
require-approval:
|
||||
- approved: 1
|
||||
username: jenkins
|
||||
email: jenkins@example.com
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
projects:
|
||||
- name: org/project
|
||||
merge-mode: cherry-pick
|
||||
check:
|
||||
- project-check
|
|
@ -1,32 +0,0 @@
|
|||
pipelines:
|
||||
- name: check
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
success:
|
||||
review_gerrit:
|
||||
verified: 1
|
||||
failure:
|
||||
review_gerrit:
|
||||
verified: -1
|
||||
|
||||
jobs:
|
||||
- name: ^.*$
|
||||
swift:
|
||||
- name: logs
|
||||
- name: ^.*-merge$
|
||||
swift:
|
||||
- name: assets
|
||||
container: merge_assets
|
||||
failure-message: Unable to merge change
|
||||
- name: test-test
|
||||
swift:
|
||||
- name: mostly
|
||||
container: stash
|
||||
|
||||
projects:
|
||||
- name: test-org/test
|
||||
check:
|
||||
- test-merge
|
||||
- test-test
|
|
@ -1,17 +0,0 @@
|
|||
pipelines:
|
||||
- name: 'check'
|
||||
manager: IndependentPipelineManager
|
||||
trigger:
|
||||
review_gerrit:
|
||||
- event: patchset-created
|
||||
|
||||
project-templates:
|
||||
- name: template-generic
|
||||
check:
|
||||
- '{project}-merge'
|
||||
|
||||
projects:
|
||||
- name: organization/project
|
||||
template:
|
||||
- name: template-generic
|
||||
project: 'myproject'
|
|
@ -1,36 +0,0 @@
|
|||
[gearman]
|
||||
server=127.0.0.1
|
||||
|
||||
[zuul]
|
||||
layout_config=layout.yaml
|
||||
url_pattern=http://logs.example.com/{change.number}/{change.patchset}/{pipeline.name}/{job.name}/{build.number}
|
||||
job_name_in_report=true
|
||||
|
||||
[merger]
|
||||
git_dir=/tmp/zuul-test/git
|
||||
git_user_email=zuul@example.com
|
||||
git_user_name=zuul
|
||||
zuul_url=http://zuul.example.com/p
|
||||
|
||||
[swift]
|
||||
authurl=https://identity.api.example.org/v2.0/
|
||||
user=username
|
||||
key=password
|
||||
tenant_name=" "
|
||||
|
||||
default_container=logs
|
||||
region_name=EXP
|
||||
logserver_prefix=http://logs.example.org/server.app/
|
||||
|
||||
[connection review_gerrit]
|
||||
driver=gerrit
|
||||
server=review.example.com
|
||||
user=jenkins
|
||||
sshkey=none
|
||||
|
||||
[connection my_smtp]
|
||||
driver=smtp
|
||||
server=localhost
|
||||
port=25
|
||||
default_from=zuul@example.com
|
||||
default_to=you@example.com
|
|
@ -1,81 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from six.moves import configparser as ConfigParser
|
||||
import os
|
||||
import re
|
||||
|
||||
import testtools
|
||||
import voluptuous
|
||||
import yaml
|
||||
|
||||
import zuul.layoutvalidator
|
||||
import zuul.lib.connections
|
||||
|
||||
FIXTURE_DIR = os.path.join(os.path.dirname(__file__),
|
||||
'fixtures')
|
||||
LAYOUT_RE = re.compile(r'^(good|bad)_.*\.yaml$')
|
||||
|
||||
|
||||
class TestLayoutValidator(testtools.TestCase):
|
||||
def setUp(self):
|
||||
self.skip("Disabled for early v3 development")
|
||||
|
||||
def test_layouts(self):
|
||||
"""Test layout file validation"""
|
||||
print()
|
||||
errors = []
|
||||
for fn in os.listdir(os.path.join(FIXTURE_DIR, 'layouts')):
|
||||
m = LAYOUT_RE.match(fn)
|
||||
if not m:
|
||||
continue
|
||||
print(fn)
|
||||
|
||||
# Load any .conf file by the same name but .conf extension.
|
||||
config_file = ("%s.conf" %
|
||||
os.path.join(FIXTURE_DIR, 'layouts',
|
||||
fn.split('.yaml')[0]))
|
||||
if not os.path.isfile(config_file):
|
||||
config_file = os.path.join(FIXTURE_DIR, 'layouts',
|
||||
'zuul_default.conf')
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read(config_file)
|
||||
connections = zuul.lib.connections.configure_connections(config)
|
||||
|
||||
layout = os.path.join(FIXTURE_DIR, 'layouts', fn)
|
||||
data = yaml.safe_load(open(layout))
|
||||
validator = zuul.layoutvalidator.LayoutValidator()
|
||||
if m.group(1) == 'good':
|
||||
try:
|
||||
validator.validate(data, connections)
|
||||
except voluptuous.Invalid as e:
|
||||
raise Exception(
|
||||
'Unexpected YAML syntax error in %s:\n %s' %
|
||||
(fn, str(e)))
|
||||
else:
|
||||
try:
|
||||
validator.validate(data, connections)
|
||||
raise Exception("Expected a YAML syntax error in %s." %
|
||||
fn)
|
||||
except voluptuous.Invalid as e:
|
||||
error = str(e)
|
||||
print(' ', error)
|
||||
if error in errors:
|
||||
raise Exception("Error has already been tested: %s" %
|
||||
error)
|
||||
else:
|
||||
errors.append(error)
|
||||
pass
|
|
@ -1,372 +0,0 @@
|
|||
# Copyright 2013 OpenStack Foundation
|
||||
# Copyright 2013 Antoine "hashar" Musso
|
||||
# Copyright 2013 Wikimedia Foundation Inc.
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import voluptuous as v
|
||||
import string
|
||||
|
||||
|
||||
# Several forms accept either a single item or a list, this makes
|
||||
# specifying that in the schema easy (and explicit).
|
||||
def toList(x):
|
||||
return v.Any([x], x)
|
||||
|
||||
|
||||
class ConfigSchema(object):
|
||||
tenant_source = v.Schema({'repos': [str]})
|
||||
|
||||
def validateTenantSources(self, value, path=[]):
|
||||
if isinstance(value, dict):
|
||||
for k, val in value.items():
|
||||
self.validateTenantSource(val, path + [k])
|
||||
else:
|
||||
raise v.Invalid("Invalid tenant source", path)
|
||||
|
||||
def validateTenantSource(self, value, path=[]):
|
||||
# TODOv3(jeblair): validate against connections
|
||||
self.tenant_source(value)
|
||||
|
||||
def getSchema(self, data, connections=None):
|
||||
tenant = {v.Required('name'): str,
|
||||
'include': toList(str),
|
||||
'source': self.validateTenantSources}
|
||||
|
||||
schema = v.Schema({'tenants': [tenant]})
|
||||
|
||||
return schema
|
||||
|
||||
|
||||
class LayoutSchema(object):
|
||||
manager = v.Any('IndependentPipelineManager',
|
||||
'DependentPipelineManager')
|
||||
|
||||
precedence = v.Any('normal', 'low', 'high')
|
||||
|
||||
approval = v.Schema({'username': str,
|
||||
'email-filter': str,
|
||||
'email': str,
|
||||
'older-than': str,
|
||||
'newer-than': str,
|
||||
}, extra=True)
|
||||
|
||||
require = {'approval': toList(approval),
|
||||
'open': bool,
|
||||
'current-patchset': bool,
|
||||
'status': toList(str)}
|
||||
|
||||
reject = {'approval': toList(approval)}
|
||||
|
||||
window = v.All(int, v.Range(min=0))
|
||||
window_floor = v.All(int, v.Range(min=1))
|
||||
window_type = v.Any('linear', 'exponential')
|
||||
window_factor = v.All(int, v.Range(min=1))
|
||||
|
||||
pipeline = {v.Required('name'): str,
|
||||
v.Required('manager'): manager,
|
||||
'source': str,
|
||||
'precedence': precedence,
|
||||
'description': str,
|
||||
'require': require,
|
||||
'reject': reject,
|
||||
'success-message': str,
|
||||
'failure-message': str,
|
||||
'merge-failure-message': str,
|
||||
'footer-message': str,
|
||||
'dequeue-on-new-patchset': bool,
|
||||
'ignore-dependencies': bool,
|
||||
'disable-after-consecutive-failures':
|
||||
v.All(int, v.Range(min=1)),
|
||||
'window': window,
|
||||
'window-floor': window_floor,
|
||||
'window-increase-type': window_type,
|
||||
'window-increase-factor': window_factor,
|
||||
'window-decrease-type': window_type,
|
||||
'window-decrease-factor': window_factor,
|
||||
}
|
||||
|
||||
project_template = {v.Required('name'): str}
|
||||
project_templates = [project_template]
|
||||
|
||||
swift = {v.Required('name'): str,
|
||||
'container': str,
|
||||
'expiry': int,
|
||||
'max_file_size': int,
|
||||
'max-file-size': int,
|
||||
'max_file_count': int,
|
||||
'max-file-count': int,
|
||||
'logserver_prefix': str,
|
||||
'logserver-prefix': str,
|
||||
}
|
||||
|
||||
skip_if = {'project': str,
|
||||
'branch': str,
|
||||
'all-files-match-any': toList(str),
|
||||
}
|
||||
|
||||
job = {v.Required('name'): str,
|
||||
'queue-name': str,
|
||||
'failure-message': str,
|
||||
'success-message': str,
|
||||
'failure-pattern': str,
|
||||
'success-pattern': str,
|
||||
'hold-following-changes': bool,
|
||||
'voting': bool,
|
||||
'attempts': int,
|
||||
'mutex': str,
|
||||
'tags': toList(str),
|
||||
'branch': toList(str),
|
||||
'files': toList(str),
|
||||
'swift': toList(swift),
|
||||
'skip-if': toList(skip_if),
|
||||
}
|
||||
jobs = [job]
|
||||
|
||||
job_name = v.Schema(v.Match("^\S+$"))
|
||||
|
||||
def validateJob(self, value, path=[]):
|
||||
if isinstance(value, list):
|
||||
for (i, val) in enumerate(value):
|
||||
self.validateJob(val, path + [i])
|
||||
elif isinstance(value, dict):
|
||||
for k, val in value.items():
|
||||
self.validateJob(val, path + [k])
|
||||
else:
|
||||
self.job_name.schema(value)
|
||||
|
||||
def validateTemplateCalls(self, calls):
|
||||
""" Verify a project pass the parameters required
|
||||
by a project-template
|
||||
"""
|
||||
for call in calls:
|
||||
schema = self.templates_schemas[call.get('name')]
|
||||
schema(call)
|
||||
|
||||
def collectFormatParam(self, tree):
|
||||
"""In a nested tree of string, dict and list, find out any named
|
||||
parameters that might be used by str.format(). This is used to find
|
||||
out whether projects are passing all the required parameters when
|
||||
using a project template.
|
||||
|
||||
Returns a set() of all the named parameters found.
|
||||
"""
|
||||
parameters = set()
|
||||
if isinstance(tree, str):
|
||||
# parse() returns a tuple of
|
||||
# (literal_text, field_name, format_spec, conversion)
|
||||
# We are just looking for field_name
|
||||
parameters = set([t[1] for t in string.Formatter().parse(tree)
|
||||
if t[1] is not None])
|
||||
elif isinstance(tree, list):
|
||||
for item in tree:
|
||||
parameters.update(self.collectFormatParam(item))
|
||||
elif isinstance(tree, dict):
|
||||
for item in tree:
|
||||
parameters.update(self.collectFormatParam(tree[item]))
|
||||
|
||||
return parameters
|
||||
|
||||
def getDriverSchema(self, dtype, connections):
|
||||
# TODO(jhesketh): Make the driver discovery dynamic
|
||||
connection_drivers = {
|
||||
'trigger': {
|
||||
'gerrit': 'zuul.trigger.gerrit',
|
||||
},
|
||||
'reporter': {
|
||||
'gerrit': 'zuul.reporter.gerrit',
|
||||
'smtp': 'zuul.reporter.smtp',
|
||||
'sql': 'zuul.reporter.sql',
|
||||
},
|
||||
}
|
||||
standard_drivers = {
|
||||
'trigger': {
|
||||
'timer': 'zuul.trigger.timer',
|
||||
'zuul': 'zuul.trigger.zuultrigger',
|
||||
}
|
||||
}
|
||||
|
||||
schema = {}
|
||||
# Add the configured connections as available layout options
|
||||
for connection_name, connection in connections.items():
|
||||
for dname, dmod in connection_drivers.get(dtype, {}).items():
|
||||
if connection.driver_name == dname:
|
||||
schema[connection_name] = toList(__import__(
|
||||
connection_drivers[dtype][dname],
|
||||
fromlist=['']).getSchema())
|
||||
|
||||
# Standard drivers are always available and don't require a unique
|
||||
# (connection) name
|
||||
for dname, dmod in standard_drivers.get(dtype, {}).items():
|
||||
schema[dname] = toList(__import__(
|
||||
standard_drivers[dtype][dname], fromlist=['']).getSchema())
|
||||
|
||||
return schema
|
||||
|
||||
def getSchema(self, data, connections=None):
|
||||
if not isinstance(data, dict):
|
||||
raise Exception("Malformed layout configuration: top-level type "
|
||||
"should be a dictionary")
|
||||
pipelines = data.get('pipelines')
|
||||
if not pipelines:
|
||||
pipelines = []
|
||||
pipelines = [p['name'] for p in pipelines if 'name' in p]
|
||||
|
||||
# Whenever a project uses a template, it better have to exist
|
||||
project_templates = data.get('project-templates', [])
|
||||
template_names = [t['name'] for t in project_templates
|
||||
if 'name' in t]
|
||||
|
||||
# A project using a template must pass all parameters to it.
|
||||
# We first collect each templates parameters and craft a new
|
||||
# schema for each of the template. That will later be used
|
||||
# by validateTemplateCalls().
|
||||
self.templates_schemas = {}
|
||||
for t_name in template_names:
|
||||
# Find out the parameters used inside each templates:
|
||||
template = [t for t in project_templates
|
||||
if t['name'] == t_name]
|
||||
template_parameters = self.collectFormatParam(template)
|
||||
|
||||
# Craft the templates schemas
|
||||
schema = {v.Required('name'): v.Any(*template_names)}
|
||||
for required_param in template_parameters:
|
||||
# special case 'name' which will be automatically provided
|
||||
if required_param == 'name':
|
||||
continue
|
||||
# add this template parameters as requirements:
|
||||
schema.update({v.Required(required_param): str})
|
||||
|
||||
# Register the schema for validateTemplateCalls()
|
||||
self.templates_schemas[t_name] = v.Schema(schema)
|
||||
|
||||
project = {'name': str,
|
||||
'merge-mode': v.Any('merge', 'merge-resolve,',
|
||||
'cherry-pick'),
|
||||
'template': self.validateTemplateCalls,
|
||||
}
|
||||
|
||||
# And project should refers to existing pipelines
|
||||
for p in pipelines:
|
||||
project[p] = self.validateJob
|
||||
projects = [project]
|
||||
|
||||
# Sub schema to validate a project template has existing
|
||||
# pipelines and jobs.
|
||||
project_template = {'name': str}
|
||||
for p in pipelines:
|
||||
project_template[p] = self.validateJob
|
||||
project_templates = [project_template]
|
||||
|
||||
# TODO(jhesketh): source schema is still defined above as sources
|
||||
# currently aren't key/value so there is nothing to validate. Need to
|
||||
# revisit this and figure out how to allow drivers with and without
|
||||
# params. eg support all:
|
||||
# source: gerrit
|
||||
# and
|
||||
# source:
|
||||
# gerrit:
|
||||
# - val
|
||||
# - val2
|
||||
# and
|
||||
# source:
|
||||
# gerrit: something
|
||||
# etc...
|
||||
self.pipeline['trigger'] = v.Required(
|
||||
self.getDriverSchema('trigger', connections))
|
||||
for action in ['start', 'success', 'failure', 'merge-failure',
|
||||
'disabled']:
|
||||
self.pipeline[action] = self.getDriverSchema('reporter',
|
||||
connections)
|
||||
|
||||
# Gather our sub schemas
|
||||
schema = v.Schema({'includes': self.includes,
|
||||
v.Required('pipelines'): [self.pipeline],
|
||||
'jobs': self.jobs,
|
||||
'project-templates': project_templates,
|
||||
v.Required('projects'): projects,
|
||||
})
|
||||
return schema
|
||||
|
||||
|
||||
class LayoutValidator(object):
|
||||
def checkDuplicateNames(self, data, path):
|
||||
items = []
|
||||
for i, item in enumerate(data):
|
||||
if item['name'] in items:
|
||||
raise v.Invalid("Duplicate name: %s" % item['name'],
|
||||
path + [i])
|
||||
items.append(item['name'])
|
||||
|
||||
def extraDriverValidation(self, dtype, driver_data, connections=None):
|
||||
# Some drivers may have extra validation to run on the layout
|
||||
# TODO(jhesketh): Make the driver discovery dynamic
|
||||
connection_drivers = {
|
||||
'trigger': {
|
||||
'gerrit': 'zuul.trigger.gerrit',
|
||||
},
|
||||
'reporter': {
|
||||
'gerrit': 'zuul.reporter.gerrit',
|
||||
'smtp': 'zuul.reporter.smtp',
|
||||
},
|
||||
}
|
||||
standard_drivers = {
|
||||
'trigger': {
|
||||
'timer': 'zuul.trigger.timer',
|
||||
'zuul': 'zuul.trigger.zuultrigger',
|
||||
}
|
||||
}
|
||||
|
||||
for dname, d_conf in driver_data.items():
|
||||
for connection_name, connection in connections.items():
|
||||
if connection_name == dname:
|
||||
if (connection.driver_name in
|
||||
connection_drivers.get(dtype, {}).keys()):
|
||||
module = __import__(
|
||||
connection_drivers[dtype][connection.driver_name],
|
||||
fromlist=['']
|
||||
)
|
||||
if 'validate_conf' in dir(module):
|
||||
module.validate_conf(d_conf)
|
||||
break
|
||||
if dname in standard_drivers.get(dtype, {}).keys():
|
||||
module = __import__(standard_drivers[dtype][dname],
|
||||
fromlist=[''])
|
||||
if 'validate_conf' in dir(module):
|
||||
module.validate_conf(d_conf)
|
||||
|
||||
def validate(self, data, connections=None):
|
||||
schema = LayoutSchema().getSchema(data, connections)
|
||||
schema(data)
|
||||
self.checkDuplicateNames(data['pipelines'], ['pipelines'])
|
||||
if 'jobs' in data:
|
||||
self.checkDuplicateNames(data['jobs'], ['jobs'])
|
||||
self.checkDuplicateNames(data['projects'], ['projects'])
|
||||
if 'project-templates' in data:
|
||||
self.checkDuplicateNames(
|
||||
data['project-templates'], ['project-templates'])
|
||||
|
||||
for pipeline in data['pipelines']:
|
||||
self.extraDriverValidation('trigger', pipeline['trigger'],
|
||||
connections)
|
||||
for action in ['start', 'success', 'failure', 'merge-failure']:
|
||||
if action in pipeline:
|
||||
self.extraDriverValidation('reporter', pipeline[action],
|
||||
connections)
|
||||
|
||||
|
||||
class ConfigValidator(object):
|
||||
def validate(self, data, connections=None):
|
||||
schema = ConfigSchema().getSchema(data, connections)
|
||||
schema(data)
|
Loading…
Reference in New Issue