Tuskar command to import a set of roles
This change adds a command to import a set of roles into the Tuskar storage. This will typically be ran during the installation and setup to provide a set of initial roles. Change-Id: I3395363c709c89072025083ca3da3189aae9e5d9
This commit is contained in:
parent
b43e6af3df
commit
5f1df10369
@ -26,6 +26,7 @@ packages =
|
|||||||
console_scripts =
|
console_scripts =
|
||||||
tuskar-api = tuskar.cmd.api:main
|
tuskar-api = tuskar.cmd.api:main
|
||||||
tuskar-dbsync = tuskar.cmd.dbsync:main
|
tuskar-dbsync = tuskar.cmd.dbsync:main
|
||||||
|
tuskar-load-roles = tuskar.cmd.load_roles:main
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
all_files = 1
|
all_files = 1
|
||||||
|
56
tuskar/cmd/load_roles.py
Normal file
56
tuskar/cmd/load_roles.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 __future__ import print_function
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from tuskar.common import service
|
||||||
|
from tuskar.storage.load_roles import load_roles
|
||||||
|
|
||||||
|
|
||||||
|
def _print_names(message, names):
|
||||||
|
print("{0}: \n {1}".format(message, '\n '.join(names)))
|
||||||
|
|
||||||
|
|
||||||
|
cfg.CONF.register_cli_opt(cfg.BoolOpt('dry-run'))
|
||||||
|
cfg.CONF.register_cli_opt(cfg.StrOpt('directory', positional=True))
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
|
||||||
|
if argv is None:
|
||||||
|
argv = sys.argv
|
||||||
|
|
||||||
|
service.prepare_service(argv)
|
||||||
|
|
||||||
|
all_roles, created, updated = load_roles(cfg.CONF.directory,
|
||||||
|
cfg.CONF.dry_run)
|
||||||
|
|
||||||
|
if len(created):
|
||||||
|
_print_names("Created", created)
|
||||||
|
|
||||||
|
if len(updated):
|
||||||
|
_print_names("Updated", updated)
|
||||||
|
|
||||||
|
if not cfg.CONF.dry_run:
|
||||||
|
print("Imported {0} roles".format(len(all_roles)))
|
||||||
|
else:
|
||||||
|
_print_names("Found", all_roles)
|
||||||
|
print("Imported 0 roles")
|
104
tuskar/storage/load_roles.py
Normal file
104
tuskar/storage/load_roles.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 __future__ import print_function
|
||||||
|
|
||||||
|
from os import listdir
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
from tuskar.storage.exceptions import UnknownName
|
||||||
|
from tuskar.storage.stores import TemplateStore
|
||||||
|
|
||||||
|
|
||||||
|
def _list_roles(directory):
|
||||||
|
"""Scan a directory and yield a tuple for all the roles containing the
|
||||||
|
role name and the full path to the role.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not path.isdir(directory):
|
||||||
|
raise ValueError("The given path is not a valid directory.")
|
||||||
|
|
||||||
|
directory = path.abspath(directory)
|
||||||
|
|
||||||
|
for filename in listdir(directory):
|
||||||
|
|
||||||
|
if not filename.endswith("yaml") and not filename.endswith("yml"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield filename, path.join(directory, filename)
|
||||||
|
|
||||||
|
|
||||||
|
def _read_role(role_path):
|
||||||
|
|
||||||
|
with open(role_path) as role_file:
|
||||||
|
return role_file.read()
|
||||||
|
|
||||||
|
|
||||||
|
def _create_or_update(name, contents, store=None):
|
||||||
|
|
||||||
|
if store is None:
|
||||||
|
store = TemplateStore()
|
||||||
|
|
||||||
|
try:
|
||||||
|
role = store.retrieve_by_name(name)
|
||||||
|
|
||||||
|
if role.contents != contents:
|
||||||
|
role = store.update(role.uuid, contents)
|
||||||
|
|
||||||
|
return False, role
|
||||||
|
except UnknownName:
|
||||||
|
return True, store.create(name, contents)
|
||||||
|
|
||||||
|
|
||||||
|
def load_roles(directory, dry_run=False):
|
||||||
|
"""Given a directory path, import the YAML role files into the
|
||||||
|
TemplateStore. When dry_run=True is passed, run through the roles but don't
|
||||||
|
add any to the store.
|
||||||
|
|
||||||
|
The returned tuple contains all the role names and then the names split
|
||||||
|
over where were created and updated. On a dry run the first item will
|
||||||
|
contain all of the roles found while the second two will be empty lists as
|
||||||
|
no files were updated or created.
|
||||||
|
|
||||||
|
:param directory: Directory name containing the roles
|
||||||
|
:type directory: str
|
||||||
|
|
||||||
|
:return: Summary of the results as a tuple with the total count and then
|
||||||
|
the names of the created and updated roles.
|
||||||
|
:rtype: tuple(list, list, list)
|
||||||
|
"""
|
||||||
|
|
||||||
|
all_roles, created, updated = [], [], []
|
||||||
|
|
||||||
|
roles = _list_roles(directory)
|
||||||
|
|
||||||
|
for name, role_path in roles:
|
||||||
|
|
||||||
|
contents = _read_role(role_path)
|
||||||
|
all_roles.append(name)
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
continue
|
||||||
|
|
||||||
|
role_created, _ = _create_or_update(name, contents)
|
||||||
|
|
||||||
|
if role_created:
|
||||||
|
created.append(name)
|
||||||
|
else:
|
||||||
|
updated.append(name)
|
||||||
|
|
||||||
|
return all_roles, created, updated
|
36
tuskar/tests/cmd/test_load_roles.py
Normal file
36
tuskar/tests/cmd/test_load_roles.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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 mock import call
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
from tuskar.cmd import load_roles
|
||||||
|
from tuskar.tests.base import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class LoadRoleTests(TestCase):
|
||||||
|
|
||||||
|
@patch('tuskar.storage.load_roles._list_roles',
|
||||||
|
return_value=[['role_name.yaml', '/path/role_name.yaml']])
|
||||||
|
@patch('tuskar.storage.load_roles._read_role', return_value="YAML")
|
||||||
|
@patch('tuskar.cmd.load_roles._print_names')
|
||||||
|
def test_main(self, mock_print, mock_read, mock_list):
|
||||||
|
|
||||||
|
# test
|
||||||
|
load_roles.main("/path/".split())
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertEqual([
|
||||||
|
call('Created', ['role_name.yaml'])
|
||||||
|
], mock_print.call_args_list)
|
104
tuskar/tests/storage/test_load_roles.py
Normal file
104
tuskar/tests/storage/test_load_roles.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 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 os import path
|
||||||
|
from shutil import rmtree
|
||||||
|
from tempfile import mkdtemp
|
||||||
|
|
||||||
|
from tuskar.storage.load_roles import _create_or_update
|
||||||
|
from tuskar.storage.load_roles import _list_roles
|
||||||
|
from tuskar.storage.load_roles import load_roles
|
||||||
|
from tuskar.storage.stores import TemplateStore
|
||||||
|
from tuskar.tests.base import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class LoadRoleTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(LoadRoleTests, self).setUp()
|
||||||
|
self.directory = mkdtemp()
|
||||||
|
|
||||||
|
self.store = TemplateStore()
|
||||||
|
|
||||||
|
roles = ['role1.yaml', 'rubbish', 'role2.yml']
|
||||||
|
for role in roles:
|
||||||
|
self._create_role(role)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(LoadRoleTests, self).tearDown()
|
||||||
|
rmtree(self.directory)
|
||||||
|
|
||||||
|
def _create_role(self, role):
|
||||||
|
"""Create a mock role file which simple contains it's own name as
|
||||||
|
the file contents.
|
||||||
|
"""
|
||||||
|
with open(path.join(self.directory, role), 'w') as f:
|
||||||
|
f.write("CONTENTS FOR {0}".format(role))
|
||||||
|
|
||||||
|
def test_list_roles(self):
|
||||||
|
|
||||||
|
# test
|
||||||
|
roles = sorted(_list_roles(self.directory))
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertEqual([
|
||||||
|
('role1.yaml', path.join(self.directory, "role1.yaml")),
|
||||||
|
('role2.yml', path.join(self.directory, "role2.yml")),
|
||||||
|
], roles)
|
||||||
|
|
||||||
|
def test_list_roles_invalid(self):
|
||||||
|
|
||||||
|
# setup
|
||||||
|
invalid_path = path.join(self.directory, "FAKEPATH/")
|
||||||
|
self.assertFalse(path.isdir(invalid_path))
|
||||||
|
|
||||||
|
# test
|
||||||
|
list_call = _list_roles(invalid_path)
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertRaises(ValueError, list, list_call)
|
||||||
|
|
||||||
|
def test_dry_run(self):
|
||||||
|
|
||||||
|
# test
|
||||||
|
total, created, updated = load_roles(
|
||||||
|
self.directory, dry_run=True)
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
|
||||||
|
self.assertEqual([], created)
|
||||||
|
self.assertEqual([], updated)
|
||||||
|
|
||||||
|
def test_import(self):
|
||||||
|
|
||||||
|
# test
|
||||||
|
total, created, updated = load_roles(self.directory)
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
|
||||||
|
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(created))
|
||||||
|
self.assertEqual([], updated)
|
||||||
|
|
||||||
|
def test_import_update(self):
|
||||||
|
|
||||||
|
# setup
|
||||||
|
_create_or_update("role2.yml", "contents")
|
||||||
|
|
||||||
|
# test
|
||||||
|
total, created, updated = load_roles(self.directory)
|
||||||
|
|
||||||
|
# verify
|
||||||
|
self.assertEqual(['role1.yaml', 'role2.yml'], sorted(total))
|
||||||
|
self.assertEqual(['role1.yaml', ], created)
|
||||||
|
self.assertEqual(['role2.yml', ], updated)
|
Loading…
Reference in New Issue
Block a user