Add tenant yaml validation option to zuul client

This patch adds a new command 'tenant-conf-check' to the Zuul client
command. This option runs a tenant_file validation by running the schema
valiation of the file. The command exits -1 if errors have been
detected. The command does not use RPC call but instead expects to
find the tenant_file on the local filesystem.

Change-Id: I6582bbc37706971085dac5c3ca3b4c690c515f9e
This commit is contained in:
Fabien Boucher 2018-07-12 19:47:46 +02:00
parent 8053d05773
commit 185f59068c
5 changed files with 116 additions and 0 deletions

View File

@ -113,3 +113,14 @@ Show
Example::
zuul show running-jobs
tenant-conf-check
^^^^^^^^^^^^^^^^^
.. program-output:: zuul tenant-conf-check --help
Example::
zuul tenant-conf-check
This command validates the tenant configuration schema. It exits '-1' in
case of errors detected.

View File

@ -0,0 +1,5 @@
---
features:
- |
Zuul client got a new command 'tenant-conf-check'. This command validates the
schema of the tenant configuration and exits -1 if errors have been detected.

View File

@ -0,0 +1,10 @@
- tenant:
name: tenant-one
source:
gerrit:
config-projects:
- common-config
untrusted-projects:
- invalid: []
- org/project1
- org/project2

63
tests/unit/test_client.py Normal file
View File

@ -0,0 +1,63 @@
# Copyright 2018 Red Hat, Inc.
#
# 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 os
import sys
import subprocess
import configparser
import fixtures
from tests.base import BaseTestCase
from tests.base import FIXTURE_DIR
class TestTenantValidationClient(BaseTestCase):
config_file = 'zuul.conf'
def setUp(self):
super(TestTenantValidationClient, self).setUp()
self.test_root = self.useFixture(fixtures.TempDir(
rootdir=os.environ.get("ZUUL_TEST_ROOT"))).path
self.config = configparser.ConfigParser()
self.config.read(os.path.join(FIXTURE_DIR, self.config_file))
def test_client_tenant_conf_check(self):
self.config.set(
'scheduler', 'tenant_config',
os.path.join(FIXTURE_DIR, 'config/tenant-parser/simple.yaml'))
self.config.write(
open(os.path.join(self.test_root, 'tenant_ok.conf'), 'w'))
p = subprocess.Popen(
[os.path.join(sys.prefix, 'bin/zuul'),
'-c', os.path.join(self.test_root, 'tenant_ok.conf'),
'tenant-conf-check'], stdout=subprocess.PIPE)
p.communicate()
self.assertEqual(p.returncode, 0, 'The command must exit 0')
self.config.set(
'scheduler', 'tenant_config',
os.path.join(FIXTURE_DIR, 'config/tenant-parser/invalid.yaml'))
self.config.write(
open(os.path.join(self.test_root, 'tenant_ko.conf'), 'w'))
p = subprocess.Popen(
[os.path.join(sys.prefix, 'bin/zuul'),
'-c', os.path.join(self.test_root, 'tenant_ko.conf'),
'tenant-conf-check'], stdout=subprocess.PIPE)
out, _ = p.communicate()
self.assertEqual(p.returncode, 1, "The command must exit 1")
self.assertIn(
b"expected a dictionary for dictionary", out,
"Expected error message not found")

View File

@ -140,6 +140,11 @@ class Client(zuul.cmd.ZuulApp):
# TODO: add filters such as queue, project, changeid etc
show_running_jobs.set_defaults(func=self.show_running_jobs)
cmd_conf_check = subparsers.add_parser(
'tenant-conf-check',
help='validate the tenant configuration')
cmd_conf_check.set_defaults(func=self.validate)
return parser
def parseArguments(self, args=None):
@ -387,6 +392,28 @@ class Client(zuul.cmd.ZuulApp):
},
}
def validate(self):
from zuul import scheduler
from zuul import configloader
sched = scheduler.Scheduler(self.config, testonly=True)
self.configure_connections(source_only=True)
sched.registerConnections(self.connections, load=False)
loader = configloader.ConfigLoader(
sched.connections, sched, None)
tenant_config, script = sched._checkTenantSourceConf(self.config)
unparsed_abide = loader.readConfig(tenant_config, from_script=script)
try:
for conf_tenant in unparsed_abide.tenants:
loader.tenant_parser.getSchema()(conf_tenant)
print("Tenants config validated with success")
err_code = 0
except Exception as e:
print("Error when validating tenants config")
print(e)
err_code = 1
finally:
sys.exit(err_code)
def main():
Client().main()