Implement a command line to start/stop keystone services

Picked up code from old repositories. Main change is in service.py.

related blueprint kolla-kubernetes
implements blueprint kolla-kubernetes-cli

Change-Id: I9caefe557ac827ca7a3b8f9a1693d623cf369080
This commit is contained in:
Davanum Srinivas 2016-05-08 22:10:46 -04:00
parent 27f40b1f4a
commit 7bd0c7566c
26 changed files with 1085 additions and 60 deletions

View File

View File

@ -0,0 +1,48 @@
# 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 cliff import command
from oslo_config import cfg
from oslo_log import log
from kolla_kubernetes.common import file_utils
from kolla_kubernetes import service
CONF = cfg.CONF
LOG = log.getLogger(__name__)
class Run(command.Command):
"""Run a service."""
def get_parser(self, prog_name):
parser = super(Run, self).get_parser(prog_name)
parser.add_argument('service')
return parser
def take_action(self, parsed_args):
service.run_service(parsed_args.service,
file_utils.get_services_dir(CONF.service_dir))
class Kill(command.Command):
"""Kill a service."""
def get_parser(self, prog_name):
parser = super(Kill, self).get_parser(prog_name)
parser.add_argument('service')
return parser
def take_action(self, parsed_args):
service.kill_service(parsed_args.service,
file_utils.get_services_dir(CONF.service_dir))

View File

View File

@ -0,0 +1,160 @@
# 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.path
import shlex
import sys
from cliff import app
from cliff import commandmanager
from cliff import interactive
from oslo_config import cfg
from oslo_log import log
from kolla_kubernetes.common import file_utils
from kolla_kubernetes.common import utils
PROJECT = 'kolla_kubernetes'
VERSION = '1.0'
CONF = cfg.CONF
CONF.import_group('kolla', 'kolla_kubernetes.config')
CONF.import_group('kolla_kubernetes', 'kolla_kubernetes.config')
log.register_options(CONF)
log.set_defaults(
default_log_levels='requests.packages.urllib3.connectionpool=WARNING')
cli_opts = [
cfg.StrOpt('service-dir',
default=utils.env(
'KM_SERVICE_DIR', default=os.path.join(
file_utils.find_base_dir(), 'services')),
help='Directory with services, (Env: KM_SERVICE_DIR)'),
]
CONF.register_cli_opts(cli_opts)
class KollaKubernetesInteractiveApp(interactive.InteractiveApp):
def do_run(self, arg):
self.default(arg)
def do_help(self, arg):
line_parts = shlex.split(arg)
try:
self.command_manager.find_command(line_parts)
return self.default(self.parsed('help ' + arg))
except ValueError:
# There is a builtin cmd2 command
pass
return interactive.InteractiveApp.do_help(self, arg)
class KollaKubernetesShell(app.App):
def __init__(self):
super(KollaKubernetesShell, self).__init__(
description='Kolla-kubernetes command-line interface',
version=VERSION,
command_manager=commandmanager.CommandManager(
'kolla_kubernetes.cli'),
deferred_help=True,
interactive_app_factory=KollaKubernetesInteractiveApp
)
def configure_logging(self):
return
def initialize_app(self, argv):
self.options.service_dir = CONF.service_dir
def print_help(self):
outputs = []
max_len = 0
self.stdout.write('\nCommands :\n')
for name, ep in sorted(self.command_manager):
factory = ep.load()
cmd = factory(self, None)
one_liner = cmd.get_description().split('\n')[0]
outputs.append((name, one_liner))
max_len = max(len(name), max_len)
for name, one_liner in outputs:
self.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner))
def _separate_args(argv):
conf_opts = _config_opts_map()
config_args = []
command_args = argv[:]
while command_args:
nargs = conf_opts.get(command_args[0])
if nargs:
config_args.extend(command_args[:nargs])
command_args = command_args[nargs:]
else:
break
return config_args, command_args
def _config_opts_map():
opts = {'--help': 1, '-h': 1, '--config-dir': 2, '--config-file': 2,
'--version': 1}
for opt in CONF._all_cli_opts():
if opt[1]:
arg = '%s-%s' % (opt[1].name, opt[0].name)
else:
arg = opt[0].name
if isinstance(opt[0], cfg.BoolOpt):
nargs = 1
opts['--no%s' % arg] = 1
else:
nargs = 2
opts['--%s' % arg] = nargs
if opt[0].short:
opts['-%s' % opt[0].short] = nargs
for dep_opt in opt[0].deprecated_opts:
if getattr(dep_opt, 'group'):
opts['--%s-%s' % (dep_opt.group, dep_opt.name)] = nargs
else:
opts['--%s' % dep_opt.name] = nargs
return opts
def main(argv=sys.argv[1:]):
config_args, command_args = _separate_args(argv)
need_help = (['help'] == command_args or '-h' in config_args or
'--help' in config_args)
if need_help:
CONF([], project=PROJECT, version=VERSION)
CONF.print_help()
return KollaKubernetesShell().print_help()
CONF(config_args, project=PROJECT, version=VERSION)
log.setup(CONF, PROJECT, VERSION)
if '-d' in config_args or '--debug' in config_args:
command_args.insert(0, '--debug')
CONF.log_opt_values(
log.getLogger(PROJECT), log.INFO)
return KollaKubernetesShell().run(command_args)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

View File

@ -0,0 +1,109 @@
# 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 errno
import logging
import os
import platform
import sys
from oslo_utils import importutils
from kolla_kubernetes import exception
LOG = logging.getLogger(__name__)
def find_os_type():
return platform.linux_distribution()[0]
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
def get_services_dir(base_dir):
if os.path.exists(os.path.join(base_dir, 'services')):
return os.path.join(base_dir, 'services')
elif os.path.exists(os.path.join(get_src_dir(), 'services')):
return os.path.join(get_src_dir(), 'services')
raise exception.KollaDirNotFoundException(
'Unable to detect kolla-kubernetes directory'
)
def get_src_dir():
kolla_kubernetes = importutils.import_module('kolla_kubernetes')
mod_path = os.path.abspath(kolla_kubernetes.__file__)
# remove the file and module to get to the base.
return os.path.dirname(os.path.dirname(mod_path))
def get_shared_directory():
if os.path.exists('/usr/local/share/kolla'):
return '/usr/local/share/kolla'
elif os.path.exists('/usr/share/kolla'):
return '/usr/share/kolla'
return None
def find_base_dir():
script_path = os.path.dirname(os.path.realpath(sys.argv[0]))
base_script_path = os.path.basename(script_path)
if base_script_path == 'kolla-kubernetes':
return script_path
if base_script_path == 'kolla_kubernetes':
return os.path.join(script_path, '..')
if base_script_path == 'cmd':
return os.path.join(script_path, '..', '..')
if base_script_path == 'subunit':
return get_src_dir()
if base_script_path == 'bin':
if os.path.exists('/usr/local/share/kolla'):
return '/usr/local/share/kolla'
elif os.path.exists('/usr/share/kolla'):
return '/usr/share/kolla'
else:
return get_src_dir()
raise exception.KollaDirNotFoundException(
'Unable to detect kolla-kubernetes directory'
)
def find_config_file(filename):
filepath = os.path.join('/etc/kolla-kubernetes', filename)
if os.access(filepath, os.R_OK):
config_file = filepath
else:
config_file = os.path.join(find_base_dir(),
'etc', filename)
return config_file
POSSIBLE_PATHS = {'/usr/share/kolla-kubernetes',
get_src_dir(),
find_base_dir()}
def find_file(filename):
for path in POSSIBLE_PATHS:
file_path = os.path.join(path, filename)
if os.path.exists(file_path):
return file_path
raise exception.KollaNotFoundException(filename, entity='file')

View File

@ -0,0 +1,102 @@
# 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 collections
import logging
import os
import jinja2
from jinja2 import meta
import six
import yaml
from kolla_kubernetes.common import type_utils
LOG = logging.getLogger(__name__)
# Customize PyYAML library to return the OrderedDict. That is needed, because
# when iterating on dict, we reuse its previous values when processing the
# next values and the order has to be preserved.
def ordered_dict_constructor(loader, node):
"""OrderedDict constructor for PyYAML."""
return collections.OrderedDict(loader.construct_pairs(node))
def ordered_dict_representer(dumper, data):
"""Representer for PyYAML which is able to work with OrderedDict."""
return dumper.represent_dict(data.items())
yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
ordered_dict_constructor)
yaml.add_representer(collections.OrderedDict, ordered_dict_representer)
def jinja_render(fullpath, global_config, extra=None):
variables = global_config
if extra:
variables.update(extra)
myenv = jinja2.Environment(
loader=jinja2.FileSystemLoader(
os.path.dirname(fullpath)))
myenv.filters['bool'] = type_utils.str_to_bool
return myenv.get_template(os.path.basename(fullpath)).render(variables)
def jinja_render_str(content, global_config, name='dafault_name', extra=None):
variables = global_config
if extra:
variables.update(extra)
myenv = jinja2.Environment(loader=jinja2.DictLoader({name: content}))
myenv.filters['bool'] = type_utils.str_to_bool
return myenv.get_template(name).render(variables)
def jinja_find_required_variables(fullpath):
myenv = jinja2.Environment(loader=jinja2.FileSystemLoader(
os.path.dirname(fullpath)))
myenv.filters['bool'] = type_utils.str_to_bool
template_source = myenv.loader.get_source(myenv,
os.path.basename(fullpath))[0]
parsed_content = myenv.parse(template_source)
return meta.find_undeclared_variables(parsed_content)
def dict_jinja_render(raw_dict, jvars):
"""Renders dict with jinja2 using provided variables and itself.
By using itself, we mean reusing the previous values from dict for the
potential render of the next value in dict.
"""
for key, value in raw_dict.items():
if isinstance(value, six.string_types):
value = jinja_render_str(value, jvars)
elif isinstance(value, dict):
value = dict_jinja_render(value, jvars)
jvars[key] = value
def yaml_jinja_render(filename, jvars):
"""Parses YAML file and templates it with jinja2.
1. YAML file is rendered by jinja2 based on the provided variables.
2. Rendered file is parsed.
3. The every element dictionary being a result of parsing is rendered again
with itself.
"""
with open(filename, 'r') as yaml_file:
raw_dict = yaml.load(yaml_file)
dict_jinja_render(raw_dict, jvars)

View File

@ -0,0 +1,19 @@
# 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.
def str_to_bool(text):
if not text:
return False
if text.lower() in ['true', 'yes']:
return True
return False

View File

@ -0,0 +1,46 @@
# 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 collections
import os
from six.moves.urllib import parse
def env(*args, **kwargs):
for arg in args:
value = os.environ.get(arg)
if value:
return value
return kwargs.get('default', '')
def dict_update(d, u):
"""Recursively update 'd' with 'u' and return the result."""
if not isinstance(u, collections.Mapping):
return u
for k, v in u.items():
if isinstance(v, collections.Mapping):
d[k] = dict_update(d.get(k, {}), v)
else:
d[k] = u[k]
return d
def get_query_string(search_opts):
if search_opts:
qparams = sorted(search_opts.items(), key=lambda x: x[0])
query_string = "?%s" % parse.urlencode(qparams, doseq=True)
else:
query_string = ""
return query_string

View File

@ -0,0 +1,55 @@
# 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 oslo_config import cfg
from kolla_kubernetes.common import utils
CONF = cfg.CONF
kolla_opts = [
cfg.StrOpt('namespace',
default='kollaglue',
help='The Docker namespace name'),
cfg.StrOpt('tag',
default='1.0.0',
help='The Docker tag'),
cfg.StrOpt('base',
default='centos',
help='The base distro which was used to build images'),
cfg.StrOpt('base-tag',
default='latest',
help='The base distro image tag'),
cfg.StrOpt('install-type',
default='binary',
help='The method of the OpenStack install'),
cfg.StrOpt('deployment-id',
default=utils.env('USER', default='default'),
help='Uniq name for deployment'),
cfg.StrOpt('profile',
default='default',
help='Build profile which was used to build images')
]
kolla_opt_group = cfg.OptGroup(name='kolla',
title='Options for Kolla Docker images')
CONF.register_group(kolla_opt_group)
CONF.register_cli_opts(kolla_opts, kolla_opt_group)
CONF.register_opts(kolla_opts, kolla_opt_group)
kubernetes_opts = [
cfg.StrOpt('host', default='http://localhost:8080'),
cfg.StrOpt('kubectl_path', default='kubectl')
]
kubernetes_opt_group = cfg.OptGroup(name='kolla_kubernetes')
CONF.register_group(kubernetes_opt_group)
CONF.register_cli_opts(kubernetes_opts, kubernetes_opt_group)
CONF.register_opts(kubernetes_opts, kubernetes_opt_group)

View File

@ -0,0 +1,31 @@
# 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.
class KollaException(Exception):
pass
class KollaDirNotFoundException(KollaException):
pass
class KollaNotFoundException(KollaException):
def __init__(self, message, entity='file'):
super(KollaNotFoundException, self).__init__(
'The %s "%s" was not found' % (entity, message))
class KollaNotSupportedException(KollaNotFoundException):
def __init__(self, operation='update', entity='kubernetes'):
super(KollaNotFoundException, self).__init__(
'Operation "%s" is not supported by "%s"' % (operation, entity))

174
kolla_kubernetes/service.py Normal file
View File

@ -0,0 +1,174 @@
# 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 datetime
import functools
import os.path
import subprocess
import tempfile
import time
from oslo_config import cfg
from oslo_log import log as logging
import yaml
from kolla_kubernetes.common import file_utils
from kolla_kubernetes.common import jinja_utils
from kolla_kubernetes import service_definition
LOG = logging.getLogger()
CONF = cfg.CONF
CONF.import_group('kolla', 'kolla_kubernetes.config')
CONF.import_group('kolla_kubernetes', 'kolla_kubernetes.config')
def execute_if_enabled(f):
"""Decorator for executing methods only if runner is enabled."""
@functools.wraps(f)
def wrapper(self, *args, **kwargs):
if not self._enabled:
return
return f(self, *args, **kwargs)
return wrapper
class File(object):
def __init__(self, conf, name, service_name):
self._conf = conf
self._name = name
self._service_name = service_name
class Command(object):
def __init__(self, conf, name, service_name):
self._conf = conf
self._name = name
self._service_name = service_name
class JvarsDict(dict):
"""Dict which can contain the 'global_vars' which are always preserved.
They cannot be be overriden by any update nor single item setting.
"""
def __init__(self, *args, **kwargs):
super(JvarsDict, self).__init__(*args, **kwargs)
self.global_vars = {}
def __setitem__(self, key, value, force=False):
if not force and key in self.global_vars:
return
return super(JvarsDict, self).__setitem__(key, value)
def set_force(self, key, value):
"""Sets the variable even if it will override a global variable."""
return self.__setitem__(key, value, force=True)
def update(self, other_dict, force=False):
if not force:
other_dict = {key: value for key, value in other_dict.items()
if key not in self.global_vars}
super(JvarsDict, self).update(other_dict)
def set_global_vars(self, global_vars):
self.update(global_vars)
self.global_vars = global_vars
def _load_variables_from_file(service_dir, project_name):
jvars = JvarsDict()
f = file_utils.find_config_file('globals.yml')
if os.path.exists(f):
with open(f, 'r') as gf:
jvars.set_global_vars(yaml.load(gf))
f = file_utils.find_config_file('passwords.yml')
if os.path.exists(f):
with open(f, 'r') as gf:
jvars.update(yaml.load(gf))
# Apply the basic variables that aren't defined in any config file.
jvars.update({
'deployment_id': CONF.kolla.deployment_id,
'node_config_directory': '',
'timestamp': str(time.time())
})
dir = file_utils.get_shared_directory()
if dir and os.path.exists(os.path.join(dir, 'ansible/group_vars/all.yml')):
all_yml_name = os.path.join(dir, 'ansible/group_vars/all.yml')
jinja_utils.yaml_jinja_render(all_yml_name, jvars)
proj_yml_name = os.path.join(dir, 'ansible/roles',
project_name, 'defaults', 'main.yml')
if dir and os.path.exists(proj_yml_name):
jinja_utils.yaml_jinja_render(proj_yml_name, jvars)
else:
LOG.warning('Path missing %s' % proj_yml_name)
return jvars
def _build_runner(service_name, service_dir, variables=None):
ts = time.time()
ts = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d_%H-%M-%S_')
temp_dir = tempfile.mkdtemp(prefix='kolla-' + ts)
working_dir = os.path.join(temp_dir, 'kubernetes')
os.makedirs(working_dir)
for filename in service_definition.find_service_files(service_name,
service_dir):
proj_filename = filename.split('/')[-1].replace('.j2', '')
proj_name = filename.split('/')[-2]
LOG.debug(
'proj_filename : %s proj_name: %s' % (proj_filename, proj_name))
# is this a snapshot or from original src?
variables = _load_variables_from_file(service_dir, proj_name)
# 1. validate the definition with the given variables
service_definition.validate(service_name, service_dir, variables)
content = yaml.load(
jinja_utils.jinja_render(filename, variables))
with open(os.path.join(working_dir, proj_filename), 'w') as f:
LOG.debug('_build_runner : service file : %s' %
os.path.join(working_dir, proj_filename))
f.write(yaml.dump(content, default_flow_style=False))
return working_dir
def run_service(service_name, service_dir, variables=None):
directory = _build_runner(service_name, service_dir, variables=variables)
_deploy_instance(directory, service_name)
def kill_service(service_name, service_dir, variables=None):
directory = _build_runner(service_name, service_dir, variables=variables)
_delete_instance(directory, service_name)
def _deploy_instance(directory, service_name):
server = "--server=" + CONF.kolla_kubernetes.host
cmd = [CONF.kolla_kubernetes.kubectl_path, server, "create", "-f",
directory]
LOG.info('Command : %r' % cmd)
subprocess.call(cmd)
def _delete_instance(directory, service_name):
server = "--server=" + CONF.kolla_kubernetes.host
cmd = [CONF.kolla_kubernetes.kubectl_path, server, "delete", "-f",
directory]
LOG.info('Command : %r' % cmd)
subprocess.call(cmd)

View File

@ -0,0 +1,151 @@
# 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.path
import socket
import yaml
import jinja2
from oslo_log import log
from kolla_kubernetes.common import jinja_utils
from kolla_kubernetes import exception
CNF_FIELDS = ('source', 'dest', 'owner', 'perm')
CMD_FIELDS = ('run_once', 'dependencies', 'command', 'env',
'delay', 'retries', 'files')
DEP_FIELDS = ('path', 'scope')
SCOPE_OPTS = ('global', 'local')
LOG = log.getLogger()
def find_service_file(service_name, service_dir):
# let's be flexible with the input, to make life easy
# for users.
if not os.path.exists(service_dir):
raise exception.KollaNotFoundException(service_dir,
entity='service directory')
short_name = service_name.split('/')[-1].replace('_ansible_tasks', '-init')
for root, dirs, names in os.walk(service_dir):
for name in names:
if short_name in name:
return os.path.join(root, name)
raise exception.KollaNotFoundException(service_name,
entity='service definition')
def find_service_files(service_name, service_dir):
# let's be flexible with the input, to make life easy
# for users.
if not os.path.exists(service_dir):
raise exception.KollaNotFoundException(service_dir,
entity='service directory')
short_name = service_name.split('/')[-1].replace('_ansible_tasks', '-init')
files = []
for root, dirs, names in os.walk(service_dir):
for name in names:
if short_name in name:
files.append(os.path.join(root, name))
if not files:
raise exception.KollaNotFoundException(service_name,
entity='service definition')
return files
def inspect(service_name, service_dir):
filename = find_service_file(service_name, service_dir)
try:
required_variables = set.union(
jinja_utils.jinja_find_required_variables(filename))
except jinja2.exceptions.TemplateNotFound:
raise exception.KollaNotFoundException(filename,
entity='service definition')
return dict(required_variables=list(required_variables))
def validate(service_name, service_dir, variables=None, deps=None):
if variables is None:
variables = {}
if deps is None:
deps = {}
filename = find_service_file(service_name, service_dir)
try:
cnf = yaml.load(jinja_utils.jinja_render(filename, variables))
except jinja2.exceptions.TemplateNotFound:
raise exception.KollaNotFoundException(filename,
entity='service definition')
def get_commands():
for cmd in cnf.get('commands', {}):
yield cmd, cnf['commands'][cmd]
if 'service' in cnf:
yield 'daemon', cnf['service']['daemon']
LOG.debug('%s: file found at %s' % (cnf['metadata']['name'], filename))
for cmd, cmd_info in get_commands():
_validate_command(filename, cmd, cmd_info, deps,
cnf['name'], service_dir)
return deps
def _validate_config(filename, conf, service_dir):
for file in conf:
for key in conf[file]:
assert key in CNF_FIELDS, '%s: %s not in %s' % (filename,
key, CNF_FIELDS)
srcs = conf[file]['source']
if isinstance(srcs, str):
srcs = [srcs]
for src in srcs:
file_path = os.path.join(service_dir, '..', src)
if not file_path.startswith('/etc'):
assert os.path.exists(file_path), '%s missing' % file_path
def _validate_command(filename, cmd, cmd_info, deps,
service_name, service_dir):
for key in cmd_info:
assert key in CMD_FIELDS, '%s not in %s' % (key, CMD_FIELDS)
_, group, role = service_name.split('/')
regs = ['%s/%s' % (role, cmd),
'%s/%s/%s' % (socket.gethostname(), role, cmd)]
reqs = cmd_info.get('dependencies', [])
for reg in regs:
if reg not in deps:
deps[reg] = {'waiters': {}}
deps[reg]['registered_by'] = cmd
deps[reg]['name'] = cmd
deps[reg]['run_by'] = filename
for req in reqs:
for key in req:
assert key in DEP_FIELDS, '%s: %s not in %s' % (filename,
key, DEP_FIELDS)
scope = req.get('scope', 'global')
assert scope in SCOPE_OPTS, '%s: %s not in %s' % (filename,
scope, SCOPE_OPTS)
req_path = req['path']
if scope == 'local':
req_path = os.path.join(socket.gethostname(), req_path)
if req_path not in deps:
deps[req_path] = {'waiters': {}}
for reg in regs:
deps[req_path]['waiters'][cmd] = reg
if 'files' in cmd_info:
_validate_config(filename, cmd_info['files'], service_dir)
LOG.debug('%s: command "%s" validated' % (service_name, cmd))

View File

@ -1,8 +1,3 @@
# -*- coding: utf-8 -*-
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 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
@ -15,9 +10,32 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import os.path
import sys
from oslo_config import cfg
from oslotest import base
import six
import testscenarios
class TestCase(base.BaseTestCase):
# Python 3, thank you for dropping contextlib.nested
if six.PY3:
@contextlib.contextmanager
def nested(*contexts):
with contextlib.ExitStack() as stack:
yield [stack.enter_context(c) for c in contexts]
else:
nested = contextlib.nested
class BaseTestCase(testscenarios.WithScenarios,
base.BaseTestCase):
"""Test case base class for all unit tests."""
def setUp(self):
super(BaseTestCase, self).setUp()
self.addCleanup(cfg.CONF.reset)
mod_dir = os.path.dirname(sys.modules[__name__].__file__)
self.project_dir = os.path.abspath(os.path.join(mod_dir, '..', '..'))

View File

@ -0,0 +1,21 @@
# 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 kolla_kubernetes.common import file_utils
from kolla_kubernetes.tests import base
class FindBaseDirTest(base.BaseTestCase):
def test_when_is_a_test(self):
tdir = file_utils.find_base_dir()
self.assertEqual(self.project_dir, tdir)

View File

@ -0,0 +1,29 @@
# 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 collections
from kolla_kubernetes.common import jinja_utils
from kolla_kubernetes.tests import base
class TestJinjaUtils(base.BaseTestCase):
def test_dict_jinja_render(self):
raw_dict = collections.OrderedDict([
('first_key', '{{ test_var }}_test',),
('second_key', '{{ first_key }}_test'),
])
jvars = {'test_var': 'test'}
jinja_utils.dict_jinja_render(raw_dict, jvars)
self.assertEqual(jvars['first_key'], 'test_test')
self.assertEqual(jvars['second_key'], 'test_test_test')

View File

@ -0,0 +1,35 @@
# 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 kolla_kubernetes.common import type_utils
from kolla_kubernetes.tests import base
class StrToBoolTest(base.BaseTestCase):
scenarios = [
('none', dict(text=None, expect=False)),
('empty', dict(text='', expect=False)),
('junk', dict(text='unlikely', expect=False)),
('no', dict(text='no', expect=False)),
('yes', dict(text='yes', expect=True)),
('0', dict(text='0', expect=False)),
('1', dict(text='1', expect=False)),
('True', dict(text='True', expect=True)),
('False', dict(text='False', expect=False)),
('true', dict(text='true', expect=True)),
('false', dict(text='false', expect=False)),
('shouty', dict(text='TRUE', expect=True)),
]
def test_str_to_bool(self):
self.assertEqual(self.expect, type_utils.str_to_bool(self.text))

View File

@ -0,0 +1,41 @@
# 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 kolla_kubernetes.common import utils
from kolla_kubernetes.tests import base
class TestDictUpdate(base.BaseTestCase):
def test_flat_no_overwrites(self):
a = {'a': 'foo', 'b': 'no'}
b = {'c': 'foo', 'd': 'no'}
expect = {'a': 'foo', 'c': 'foo', 'b': 'no', 'd': 'no'}
self.assertEqual(expect, utils.dict_update(a, b))
def test_flat_with_overwrites(self):
a = {'a': 'foo', 'b': 'no'}
b = {'c': 'foo', 'b': 'yes'}
expect = {'a': 'foo', 'c': 'foo', 'b': 'yes'}
self.assertEqual(expect, utils.dict_update(a, b))
def test_nested_no_overwrites(self):
a = {'a': 'foo', 'b': {'bb': 'no'}}
b = {'c': 'foo'}
expect = {'a': 'foo', 'c': 'foo', 'b': {'bb': 'no'}}
self.assertEqual(expect, utils.dict_update(a, b))
def test_nested_with_overwrites(self):
a = {'a': 'foo', 'b': {'bb': 'no'}}
b = {'c': 'foo', 'b': {'bb': 'yes'}}
expect = {'a': 'foo', 'c': 'foo', 'b': {'bb': 'yes'}}
self.assertEqual(expect, utils.dict_update(a, b))

View File

@ -1,28 +0,0 @@
# -*- coding: 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.
"""
test_kolla-kubernetes
----------------------------------
Tests for `kolla-kubernetes` module.
"""
from kolla_kubernetes.tests import base
class TestKollaKubernetes(base.TestCase):
def test_something(self):
pass

View File

@ -1,20 +0,0 @@
apiVersion: v1
kind: Service
spec:
ports:
- port: 35357
selector:
name: keystone-admin
metadata:
name: keystone-admin
---
apiVersion: v1
kind: Service
spec:
ports:
- port: 5000
selector:
name: keystone-public
metadata:
name: keystone-public

View File

@ -3,3 +3,10 @@
# process, which may cause wedges in the gate later.
pbr>=1.6
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
oslo.config>=3.9.0 # Apache-2.0
oslo.utils>=3.5.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
six>=1.9.0 # MIT
Jinja2>=2.8 # BSD License (3 clause)
PyYAML>=3.1.0 # MIT

View File

@ -3,15 +3,14 @@ kind: Pod
spec:
hostNetwork: True
containers:
#TODO: Use a jinja2 template for image
- image: kollaglue/centos-binary-keystone:2.0.0
- image: "{{ keystone_image_full }}"
name: keystone
volumeMounts:
- mountPath: "/var/lib/kolla/config_files"
- mountPath: {{ container_config_directory }}
name: keystone-config
env:
- name: KOLLA_CONFIG_STRATEGY
value: COPY_ALWAYS
value: {{ config_strategy }}
volumes:
- name: keystone-config
hostPath:

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: Service
spec:
ports:
- port: {{ keystone_admin_port }}
selector:
name: keystone-admin
metadata:
name: keystone-admin

View File

@ -0,0 +1,9 @@
apiVersion: v1
kind: Service
spec:
ports:
- port: {{ keystone_public_port }}
selector:
name: keystone-public
metadata:
name: keystone-public

View File

@ -21,7 +21,17 @@ classifier =
[files]
packages =
kolla-kubernetes
kolla_kubernetes
data_files =
share/kolla-kubernetes/services = services/*
[entry_points]
console_scripts =
kolla-kubernetes = kolla_kubernetes.cmd.shell:main
kolla_kubernetes.cli =
run = kolla_kubernetes.cli.service:Run
kill = kolla_kubernetes.cli.service:Kill
[build_sphinx]
source-dir = doc/source