Add validate command
Like JJB, Zuul and Nodepool, we need to have a CLI command to validate our configuration files. Change-Id: I4ccac21a2d77917667e1e844ab9ee1f1f281ea9f Signed-off-by: Paul Belanger <pabelanger@redhat.com>
This commit is contained in:
parent
103a882cbf
commit
f4b18fdd74
@ -45,8 +45,11 @@ class Builder(object):
|
||||
self.grafana = Grafana(CONF.grafana.url, CONF.grafana.apikey)
|
||||
self.parser = YamlParser()
|
||||
|
||||
def update_dashboard(self, path):
|
||||
def load_files(self, path):
|
||||
self.parser.parse(path)
|
||||
|
||||
def update_dashboard(self, path):
|
||||
self.load_files(path)
|
||||
dashboards = self.parser.data.get('dashboard', {})
|
||||
for item in dashboards:
|
||||
data, md5 = self.parser.get_dashboard(item)
|
||||
|
@ -42,6 +42,14 @@ class Commands(object):
|
||||
def update(self, path):
|
||||
self.builder.update_dashboard(path)
|
||||
|
||||
def validate(self, path):
|
||||
try:
|
||||
self.builder.load_files(path)
|
||||
print('SUCCESS!')
|
||||
except Exception as e:
|
||||
print('%s: ERROR: %s' % (path, e))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def add_command_parsers(subparsers):
|
||||
parser_update = subparsers.add_parser('update')
|
||||
@ -49,6 +57,11 @@ def add_command_parsers(subparsers):
|
||||
'path', help='colon-separated list of paths to YAML files or'
|
||||
' directories')
|
||||
|
||||
parser_validate = subparsers.add_parser('validate')
|
||||
parser_validate.add_argument(
|
||||
'path', help='colon-separated list of paths to YAML files or'
|
||||
' directories')
|
||||
|
||||
|
||||
command_opt = cfg.SubCommandOpt('action', handler=add_command_parsers)
|
||||
|
||||
|
@ -16,12 +16,37 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
from tests import conf_fixture
|
||||
|
||||
|
||||
def get_scenarios(fixtures_path, in_ext='yaml', out_ext='json'):
|
||||
scenarios = []
|
||||
files = []
|
||||
for dirpath, dirs, fs in os.walk(fixtures_path):
|
||||
files.extend([os.path.join(dirpath, f) for f in fs])
|
||||
|
||||
input_files = [f for f in files if re.match(r'.*\.{0}$'.format(in_ext), f)]
|
||||
|
||||
for input_filename in input_files:
|
||||
output_candidate = re.sub(
|
||||
r'\.{0}$'.format(in_ext), '.{0}'.format(out_ext), input_filename)
|
||||
if output_candidate not in files:
|
||||
output_candidate = None
|
||||
|
||||
scenarios.append((input_filename, {
|
||||
'in_filename': input_filename,
|
||||
'out_filename': output_candidate,
|
||||
}))
|
||||
|
||||
return scenarios
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
|
0
tests/cmd/__init__.py
Normal file
0
tests/cmd/__init__.py
Normal file
55
tests/cmd/base.py
Normal file
55
tests/cmd/base.py
Normal file
@ -0,0 +1,55 @@
|
||||
# Copyright 2015 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 sys
|
||||
|
||||
import fixtures
|
||||
import six
|
||||
|
||||
from grafana_dashboards import cmd
|
||||
from tests.base import TestCase
|
||||
|
||||
|
||||
class TestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
|
||||
def clear():
|
||||
cmd.CONF.reset()
|
||||
cmd.CONF.unregister_opt(cmd.command_opt)
|
||||
cmd.CONF.reset()
|
||||
self.addCleanup(clear)
|
||||
|
||||
def shell(self, argstr, exitcodes=(0,)):
|
||||
orig = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
try:
|
||||
sys.stdout = six.StringIO()
|
||||
sys.stderr = six.StringIO()
|
||||
argv = ['grafana-dashboards']
|
||||
argv += argstr.split()
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.argv', argv))
|
||||
cmd.main()
|
||||
except SystemExit:
|
||||
exc_type, exc_value, exc_trackback = sys.exc_info()
|
||||
self.assertIn(exc_value.code, exitcodes)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stdout = orig
|
||||
stderr = sys.stderr.getvalue()
|
||||
sys.stderr.close()
|
||||
sys.stderr = orig_stderr
|
||||
return (stdout, stderr)
|
71
tests/cmd/test_validate.py
Normal file
71
tests/cmd/test_validate.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Copyright 2015 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 re
|
||||
|
||||
from testscenarios.testcase import TestWithScenarios
|
||||
from testtools import matchers
|
||||
|
||||
from tests.base import get_scenarios
|
||||
from tests.cmd.base import TestCase
|
||||
|
||||
|
||||
class TestCaseValidateScenarios(TestWithScenarios, TestCase):
|
||||
fixtures_path = os.path.join(
|
||||
os.path.dirname(__file__), '../fixtures/cmd/validate')
|
||||
scenarios = get_scenarios(fixtures_path)
|
||||
|
||||
def test_command(self):
|
||||
if os.path.basename(self.in_filename).startswith('good-'):
|
||||
self._validate_success()
|
||||
else:
|
||||
self._validate_failure()
|
||||
|
||||
def _validate_failure(self):
|
||||
required = [
|
||||
'%s: ERROR:' % self.in_filename,
|
||||
]
|
||||
stdout, stderr = self.shell(
|
||||
'validate %s' % self.in_filename, exitcodes=[1])
|
||||
for r in required:
|
||||
self.assertThat(
|
||||
(stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def _validate_success(self):
|
||||
required = [
|
||||
'SUCCESS!',
|
||||
]
|
||||
stdout, stderr = self.shell(
|
||||
'validate %s' % self.in_filename, exitcodes=[0])
|
||||
for r in required:
|
||||
self.assertThat(
|
||||
(stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
|
||||
class TestCaseValidate(TestCase):
|
||||
|
||||
def test_validate_without_path(self):
|
||||
required = [
|
||||
'.*?^usage: grafana-dashboards validate \[-h\] path',
|
||||
'.*?^grafana-dashboards validate: error: (too few arguments|the '
|
||||
'following arguments are required: path)',
|
||||
]
|
||||
stdout, stderr = self.shell('validate', exitcodes=[2])
|
||||
for r in required:
|
||||
self.assertThat(
|
||||
(stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
2
tests/fixtures/cmd/validate/bad-dashboard-0001.yaml
vendored
Normal file
2
tests/fixtures/cmd/validate/bad-dashboard-0001.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
board:
|
||||
title: New dashboard
|
2
tests/fixtures/cmd/validate/good-dashboard-0001.yaml
vendored
Normal file
2
tests/fixtures/cmd/validate/good-dashboard-0001.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
dashboard:
|
||||
title: New dashboard
|
@ -17,8 +17,6 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
import doctest
|
||||
import testtools
|
||||
@ -26,28 +24,6 @@ import testtools
|
||||
from grafana_dashboards.parser import YamlParser
|
||||
|
||||
|
||||
def get_scenarios(fixtures_path, in_ext='yaml', out_ext='json'):
|
||||
scenarios = []
|
||||
files = []
|
||||
for dirpath, dirs, fs in os.walk(fixtures_path):
|
||||
files.extend([os.path.join(dirpath, f) for f in fs])
|
||||
|
||||
input_files = [f for f in files if re.match(r'.*\.{0}$'.format(in_ext), f)]
|
||||
|
||||
for input_filename in input_files:
|
||||
output_candidate = re.sub(
|
||||
r'\.{0}$'.format(in_ext), '.{0}'.format(out_ext), input_filename)
|
||||
if output_candidate not in files:
|
||||
output_candidate = None
|
||||
|
||||
scenarios.append((input_filename, {
|
||||
'in_filename': input_filename,
|
||||
'out_filename': output_candidate,
|
||||
}))
|
||||
|
||||
return scenarios
|
||||
|
||||
|
||||
class TestCase(object):
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
|
@ -17,7 +17,7 @@ import os
|
||||
from testscenarios.testcase import TestWithScenarios
|
||||
from testtools import TestCase
|
||||
|
||||
from tests.schema.base import get_scenarios
|
||||
from tests.base import get_scenarios
|
||||
from tests.schema.base import TestCase as BaseTestCase
|
||||
|
||||
|
||||
|
@ -13,44 +13,14 @@
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
import fixtures
|
||||
import six
|
||||
from testtools import matchers
|
||||
|
||||
from grafana_dashboards import cmd
|
||||
from tests.base import TestCase
|
||||
from tests.cmd.base import TestCase
|
||||
|
||||
|
||||
class TestCaseCmd(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCaseCmd, self).setUp()
|
||||
cmd.CONF.reset()
|
||||
|
||||
def shell(self, argstr, exitcodes=(0,)):
|
||||
orig = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
try:
|
||||
sys.stdout = six.StringIO()
|
||||
sys.stderr = six.StringIO()
|
||||
argv = ['grafana-dashboards']
|
||||
argv += argstr.split()
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.argv', argv))
|
||||
cmd.main()
|
||||
except SystemExit:
|
||||
exc_type, exc_value, exc_trackback = sys.exc_info()
|
||||
self.assertIn(exc_value.code, exitcodes)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stdout = orig
|
||||
stderr = sys.stderr.getvalue()
|
||||
sys.stderr.close()
|
||||
sys.stderr = orig_stderr
|
||||
return (stdout, stderr)
|
||||
|
||||
def test_update_without_path(self):
|
||||
required = [
|
||||
'.*?^usage: grafana-dashboards update \[-h\] path',
|
||||
|
Loading…
x
Reference in New Issue
Block a user