Commit 342eb1b2 authored by Stavros Sachtouris's avatar Stavros Sachtouris

Warn/raise error when config file is inaccessible

Fixes grnet/kamaki#71

Raise an error if the file is not readable or not existing, warn if
it is not writable, add a description to the error message when
kamaki attempts to write to a non-writable config file.
parent a13a1d2f
......@@ -5,6 +5,15 @@ Unified Changelog file for Kamaki versions >= 0.13
.. _Changelog-0.13:
v0.13rc6
========
Bug fixes
---------
* Warn or raise errors when the configuration file is inaccessible
[grnet/kamaki#71]
v0.13rc5
========
......
......@@ -178,8 +178,8 @@ def _setup_logging(debug=False, verbose=False):
logger.add_stream_logger('kamaki.clients.send', logging.INFO, sfmt)
logger.add_stream_logger('kamaki.clients.recv', logging.INFO, rfmt)
logger.add_stream_logger(__name__, logging.INFO)
else:
logger.add_stream_logger(__name__, logging.WARNING)
# else:
# logger.add_stream_logger(__name__, logging.WARNING)
global kloger
kloger = logger.get_logger(__name__)
......@@ -237,7 +237,7 @@ def _init_session(arguments, is_non_api=False):
if ca_file:
https.patch_with_certs(ca_file)
else:
warn = red('WARNING: CA certifications path not set (insecure) ')
warn = red('CA certifications path not set (insecure) ')
kloger.warning(warn)
https.patch_ignore_ssl(ignore_ssl)
......@@ -341,12 +341,12 @@ def init_cached_authenticator(config_argument, cloud, logger):
_cnf.write()
if tokens:
return astakos, help_message
logger.warning('WARNING: cloud.%s.token is now empty' % cloud)
logger.warning('cloud.%s.token is now empty' % cloud)
help_message = [
'To set a new token:',
' kamaki config set cloud.%s.token NEW_TOKEN']
except AssertionError as ae:
logger.warning('WARNING: Failed to load authenticator [%s]' % ae)
logger.warning('Failed to load authenticator [%s]' % ae)
return None, help_message
......@@ -528,6 +528,9 @@ def main(func):
for i, a in enumerate(internal_argv):
argv[i] = a
logger.add_stream_logger(
__name__, logging.WARNING,
fmt='%(levelname)s (%(name)s): %(message)s')
_config_arg = ConfigArgument('Path to config file')
parser = ArgumentParseManager(exe, arguments=dict(
config=_config_arg,
......
......@@ -41,6 +41,7 @@ import dateutil.tz
import dateutil.parser
from time import mktime
from sys import stderr
import os.path
from logging import getLogger
from argparse import (
......@@ -107,7 +108,7 @@ class Argument(object):
assert name.count(' ') == 0, '%s: Invalid parse name "%s"' % (
self, name)
msg = '%s: Invalid parse name "%s" should start with a "-"' % (
self, name)
self, name)
assert name.startswith('-'), msg
self.default = default or None
......@@ -149,6 +150,10 @@ class ConfigArgument(Argument):
@value.setter
def value(self, config_file):
if config_file:
if not os.path.exists(config_file):
raiseCLIError(
'Config file "%s" does not exist' % config_file,
importance=3)
self._value = Config(config_file)
self.file_path = config_file
elif self.file_path:
......@@ -248,8 +253,8 @@ class BooleanArgument(ValueArgument):
v = new_value.lower()
if v not in ('true', 'false'):
raise CLIInvalidArgument(
'Invalid value %s=%s' % (self.lvalue, new_value), details=[
'Usage:', '%s=<true|false>' % self.lvalue])
'Invalid value %s=%s' % (self.lvalue, new_value),
details=['Usage:', '%s=<true|false>' % self.lvalue])
self._value = bool(v == 'true')
......@@ -488,9 +493,9 @@ class StatusArgument(ValueArgument):
new_status = new_status.upper()
if new_status not in self.valid_states:
raise CLIInvalidArgument(
'Invalid argument %s' % new_status, details=[
'Usage: '
'%s=[%s]' % (self.lvalue, '|'.join(self.valid_states))])
'Invalid argument %s' % new_status,
details=['Usage:', '%s=[%s]' % (
self.lvalue, '|'.join(self.valid_states))])
self._value = new_status
......@@ -621,7 +626,7 @@ class ArgumentParseManager(object):
next = cur + lt_all - lt_pn
ret += prefix
ret += ('{:<%s}' % (lt_all - lt_pn)).format(arg.help[cur:next])
cur, finish = next, '\n%s' % tab2
cur = next
return ret + '\n'
@staticmethod
......
......@@ -35,8 +35,9 @@ from mock import patch, call
from unittest import TestCase
from StringIO import StringIO
from datetime import datetime
from tempfile import NamedTemporaryFile
from kamaki.cli import argument, errors
from kamaki.cli import argument, errors, CLIError
from kamaki.cli.config import Config
......@@ -109,11 +110,20 @@ class ConfigArgument(TestCase):
def test_value(self):
c = argument._config_arg
self.assertEqual(c.value, None)
exp = '/some/random/path'
c.value = exp
self.assertTrue(isinstance(c.value, Config))
self.assertEqual(c.file_path, exp)
self.assertEqual(c.value.path, exp)
wrong_path = '/some/random/path'
raises_error = False
try:
c.value = wrong_path
except CLIError:
raises_error = True
self.assertTrue(raises_error)
with NamedTemporaryFile() as f:
c.value = f.name
self.assertTrue(isinstance(c.value, Config))
self.assertEqual(c.file_path, f.name)
self.assertEqual(c.value.path, f.name)
def test_get(self):
c = argument._config_arg
......@@ -337,8 +347,7 @@ class KeyValueArgument(TestCase):
(
('k1=v1 v2', 'k3=', 'k 4=v4'),
{'k1': 'v1 v2', 'k3': '', 'k 4': 'v4'}),
(('k=v1', 'k=v2', 'k=v3'), {'k': 'v3'})
):
(('k=v1', 'k=v2', 'k=v3'), {'k': 'v3'})):
kva.value = kvpairs
old.update(exp)
assert_dicts_are_equal(self, kva.value, old)
......
......@@ -76,8 +76,8 @@ def fall_back(func):
try:
inp = func(self, inp)
except Exception as e:
log.warning('WARNING: Error while running %s: %s' % (func, e))
log.warning('\tWARNING: Kamaki will use original data to go on')
log.warning('Error while running %s: %s' % (func, e))
log.warning('Kamaki will use original data to go on')
finally:
return inp
return wrap
......
......@@ -1643,7 +1643,6 @@ class container_create(_PithosAccount):
metadata=self['meta'],
success=(201, ))
except ClientError as ce:
print 'WHAAAA?'
if ce.status in (202, ):
raise CLIError(
'Container %s alread exists' % self.container, details=[
......
......@@ -39,7 +39,7 @@ from collections import defaultdict
from ConfigParser import RawConfigParser, NoOptionError, NoSectionError, Error
from re import match
from kamaki.cli.errors import CLISyntaxError
from kamaki.cli.errors import CLISyntaxError, CLIError
from kamaki import __version__
try:
......@@ -138,6 +138,19 @@ class Config(RawConfigParser):
def __init__(self, path=None, with_defaults=True):
RawConfigParser.__init__(self, dict_type=OrderedDict)
self.path = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
# Check if self.path is accessible
abspath = os.path.abspath(self.path)
if not os.path.exists(self.path):
log.warning('Config file %s does not exist' % abspath)
elif os.access(self.path, os.R_OK):
if not os.access(self.path, os.W_OK):
log.warning('Config file %s is not writable' % abspath)
else:
raise CLIError(
'Config file %s is inaccessible' % abspath,
importance=3, details=['No read permissions for this file'])
self._overrides = defaultdict(dict)
if with_defaults:
self._load_defaults()
......@@ -289,7 +302,12 @@ class Config(RawConfigParser):
"""
:returns: (float) version of the config file or 0.9 if unrecognized
"""
from kamaki.cli import logger
# Ignore logs from "checker" logger
logger.deactivate(__name__)
checker = Config(self.path, with_defaults=False)
logger.activate(__name__)
sections = checker.sections()
# log.debug('Config file heuristic 1: old global section ?')
if 'global' in sections:
......@@ -425,6 +443,10 @@ class Config(RawConfigParser):
f.write(HEADER.lstrip())
f.flush()
RawConfigParser.write(self, f)
except IOError as ioe:
raise CLIError(
'Cannot write to config file %s' % os.path.abspath(self.path),
importance=3, details=[type(ioe), ioe, ])
finally:
if CLOUD_PREFIX not in self.sections():
self.add_section(CLOUD_PREFIX)
......
......@@ -234,7 +234,7 @@ class Config(TestCase):
_cnf = Config(path=f.name)
self.assertEqual(
sorted(['global.%s = %s' % sample for sample in extras]),
sorted(_cnf.rescue_old_file()))
sorted(_cnf.rescue_old_file()))
def test_guess_version(self):
from kamaki.cli.config import Config
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment