Skip to content

Commit ffdcc3c

Browse files
author
Clark Perkins
committed
Start using config in the client
1 parent f9b0078 commit ffdcc3c

File tree

8 files changed

+72
-74
lines changed

8 files changed

+72
-74
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def test_python_version():
4848

4949
requirements = [
5050
'Jinja2==2.7.3',
51-
'PyYAML==3.11',
51+
'PyYAML>=3.10',
5252
'click>=6.0,<7.0',
5353
'click-shell==0.1',
5454
'keyring==3.7',

stackdio/cli/__init__.py

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
from requests import ConnectionError
1414

1515
from stackdio.cli import mixins
16-
from stackdio.client import StackdIO
16+
from stackdio.client import StackdioClient
1717
from stackdio.client.config import StackdioConfig
18-
from stackdio.client.exceptions import MissingConfigException
1918
from stackdio.client.version import __version__
2019

2120

@@ -39,7 +38,7 @@ def load_config(fail_on_misconfigure, section='stackdio'):
3938

4039

4140
def get_client(config):
42-
return StackdIO(
41+
return StackdioClient(
4342
base_url=config['url'],
4443
auth=(
4544
config['username'],
@@ -66,29 +65,6 @@ def stackdio(ctx):
6665
ctx.obj = StackdioObj(ctx, ctx.invoked_subcommand != 'configure')
6766

6867

69-
@stackdio.group()
70-
def stacks():
71-
pass
72-
73-
74-
@stackdio.group()
75-
def blueprints():
76-
pass
77-
78-
79-
@blueprints.command()
80-
@click.option('--template', '-t')
81-
@click.option('--var-file', '-v', multiple=True)
82-
def create(template, var_file):
83-
click.echo(template)
84-
click.echo(var_file)
85-
86-
87-
@stackdio.group()
88-
def formulas():
89-
pass
90-
91-
9268
@stackdio.command()
9369
def configure():
9470
config = StackdioConfig(create=True)
@@ -150,8 +126,8 @@ def get_names(self):
150126
return ["do_initial_setup", "do_help"]
151127

152128
def _init_stacks(self):
153-
"""Instantiate a StackdIO object"""
154-
self.stacks = StackdIO(
129+
"""Instantiate a StackdioClient object"""
130+
self.stacks = StackdioClient(
155131
base_url=self.config["url"],
156132
auth=(
157133
self.config["username"],
@@ -275,7 +251,7 @@ def do_account_summary(self, args=None):
275251

276252

277253
def main():
278-
stackdio()
254+
stackdio(obj={})
279255

280256
# parser = argparse.ArgumentParser(
281257
# description="Invoke the stackdio cli")

stackdio/cli/mixins/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +0,0 @@
1-
import stackdio.cli.mixins.blueprints
2-
import stackdio.cli.mixins.bootstrap
3-
import stackdio.cli.mixins.formulas
4-
import stackdio.cli.mixins.stacks

stackdio/cli/mixins/blueprints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def _create_blueprint(self, args, bootstrap=False):
170170
if not bootstrap:
171171
print("Creating blueprint")
172172

173-
r = self.stacks.create_blueprint(bp_json)
173+
r = self.stacks.create_blueprint(bp_json, raise_for_status=False)
174174
print(json.dumps(r, indent=2))
175175

176176
def _create_single(self, template_file, var_files, no_prompt):

stackdio/client/__init__.py

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,41 +19,52 @@
1919

2020
from .account import AccountMixin
2121
from .blueprint import BlueprintMixin
22-
from .exceptions import BlueprintException, StackException, IncompatibleVersionException
22+
from .config import StackdioConfig
23+
from .exceptions import (
24+
BlueprintException,
25+
StackException,
26+
IncompatibleVersionException,
27+
MissingUrlException
28+
)
2329
from .formula import FormulaMixin
24-
from .http import get, post, patch
30+
from .http import HttpMixin, get, post, patch
2531
from .image import ImageMixin
2632
from .region import RegionMixin
2733
from .settings import SettingsMixin
2834
from .stack import StackMixin
2935
from .version import _parse_version_string
3036

3137
logger = logging.getLogger(__name__)
38+
logger.addHandler(logging.NullHandler())
3239

3340

34-
class StackdIO(BlueprintMixin, FormulaMixin, AccountMixin,
35-
ImageMixin, RegionMixin, StackMixin, SettingsMixin):
41+
class StackdioClient(BlueprintMixin, FormulaMixin, AccountMixin, ImageMixin,
42+
RegionMixin, StackMixin, SettingsMixin, HttpMixin):
3643

37-
def __init__(self, protocol="https", host="localhost", port=443,
38-
base_url=None, auth=None, verify=True):
44+
def __init__(self, url=None, username=None, password=None, verify=True):
45+
self.config = StackdioConfig()
3946

40-
super(StackdIO, self).__init__(auth=auth, verify=verify)
41-
if base_url:
42-
self.url = base_url if base_url.endswith('/') else "%s/" % base_url
43-
else:
44-
self.url = "{protocol}://{host}:{port}/api/".format(
45-
protocol=protocol,
46-
host=host,
47-
port=port)
47+
if self.config.usable_config:
48+
# Grab stuff from the config
49+
url = self.config['url']
50+
username = self.config['username']
51+
verify = self.config['verify']
4852

49-
self.auth = auth
53+
super(StackdioClient, self).__init__(url=url, auth=(username, password), verify=verify)
54+
self.url = url
5055

51-
_, self.version = _parse_version_string(self.get_version())
56+
try:
57+
_, self.version = _parse_version_string(self.get_version())
58+
except MissingUrlException:
59+
self.version = None
5260

53-
if self.version[0] != 0 or self.version[1] != 7:
61+
if self.version and self.version[0] != 0 or self.version[1] != 7:
5462
raise IncompatibleVersionException('Server version {0}.{1}.{2} not '
5563
'supported.'.format(**self.version))
5664

65+
def usable(self):
66+
return self.config.usable_config
67+
5768
@get('')
5869
def get_root(self):
5970
pass
@@ -67,12 +78,12 @@ def get_version(self, resp):
6778
return resp['version']
6879

6980
@post('cloud/security_groups/')
70-
def create_security_group(self, name, description, cloud_provider, is_default=True):
71-
"""Create a security group"""
81+
def create_security_group(self, name, description, cloud_account, group_id, is_default=True):
7282

7383
return {
74-
"name": name,
75-
"description": description,
76-
"cloud_provider": cloud_provider,
77-
"is_default": is_default
84+
'name': name,
85+
'description': description,
86+
'cloud_account': cloud_account,
87+
'group_id': group_id,
88+
'is_default': is_default
7889
}

stackdio/client/config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,34 @@
2222
from requests.exceptions import ConnectionError, MissingSchema
2323

2424
from stackdio.client.compat import ConfigParser, NoOptionError
25-
from stackdio.client.exceptions import MissingConfigException
2625

2726

2827
CFG_DIR = os.path.join(os.path.expanduser('~'), '.stackdio')
2928
CFG_FILE = os.path.join(CFG_DIR, 'client.cfg')
3029

3130

3231
class StackdioConfig(object):
32+
"""
33+
A wrapper around python's ConfigParser class
34+
"""
3335

3436
BOOL_MAP = {
3537
str(True): True,
3638
str(False): False,
3739
}
3840

39-
def __init__(self, section='stackdio', config_file=CFG_FILE, create=False):
41+
def __init__(self, config_file=CFG_FILE, section='default'):
4042
super(StackdioConfig, self).__init__()
4143

4244
self.section = section
4345

4446
self._cfg_file = config_file
4547

46-
if not create and not os.path.isfile(config_file):
47-
raise MissingConfigException('{0} does not exist'.format(config_file))
48+
self.usable_config = os.path.isfile(config_file)
4849

4950
self._config = ConfigParser()
5051

51-
if create:
52+
if not self.usable_config:
5253
self._config.add_section(section)
5354
else:
5455
self._config.read(config_file)

stackdio/client/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#
1717

1818

19-
class MissingConfigException(Exception):
19+
class MissingUrlException(Exception):
2020
pass
2121

2222

stackdio/client/http.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323

2424
from inspect import getcallargs
2525

26+
from .exceptions import MissingUrlException
27+
2628
logger = logging.getLogger(__name__)
2729
logger.addHandler(logging.NullHandler())
2830

29-
HTTP_INSECURE_MESSAGE = "\n".join([
31+
HTTP_INSECURE_MESSAGE = '\n'.join([
3032
"You have chosen not to verify ssl connections.",
3133
"This is insecure, but it's your choice.",
3234
"This has been your single warning."
@@ -37,14 +39,15 @@ class HttpMixin(object):
3739
"""Add HTTP request features to an object"""
3840

3941
HEADERS = {
40-
'json': {"content-type": "application/json"},
41-
'xml': {"content-type": "application/xml"}
42+
'json': {'content-type': 'application/json'},
43+
'xml': {'content-type': 'application/xml'},
4244
}
4345

44-
def __init__(self, auth=None, verify=True):
46+
def __init__(self, url, auth=None, verify=True):
4547
super(HttpMixin, self).__init__()
4648

47-
self._http_options = {
49+
self.url = url
50+
self.http_options = {
4851
'auth': auth,
4952
'verify': verify,
5053
}
@@ -59,6 +62,9 @@ def __init__(self, auth=None, verify=True):
5962
from requests.packages.urllib3 import disable_warnings
6063
disable_warnings()
6164

65+
def usable(self):
66+
raise NotImplementedError()
67+
6268

6369
def default_response(obj, response):
6470
return response
@@ -102,12 +108,20 @@ def __repr__(self):
102108
# We need this so we can save the client object as an attribute, and then it can be used
103109
# in __call__
104110
def __get__(self, obj, objtype=None):
111+
if objtype:
112+
assert issubclass(objtype, HttpMixin)
113+
assert isinstance(obj, HttpMixin)
114+
105115
self.obj = obj
106-
assert issubclass(objtype, HttpMixin)
107116
return self
108117

109118
# Here's how the request actually happens
110119
def __call__(self, *args, **kwargs):
120+
assert isinstance(self.obj, HttpMixin)
121+
122+
if not self.obj.usable():
123+
raise MissingUrlException('No url is set')
124+
111125
none_on_404 = kwargs.pop('none_on_404', False)
112126
raise_for_status = kwargs.pop('raise_for_status', True)
113127

@@ -131,10 +145,10 @@ def __call__(self, *args, **kwargs):
131145
result = requests.request(method,
132146
url,
133147
data=data,
134-
auth=self.obj._http_options['auth'],
148+
auth=self.obj.http_options['auth'],
135149
headers=self.headers,
136150
params=kwargs,
137-
verify=self.obj._http_options['verify'])
151+
verify=self.obj.http_options['verify'])
138152

139153
# Handle special conditions
140154
if none_on_404 and result.status_code == 404:
@@ -164,10 +178,10 @@ def __call__(self, *args, **kwargs):
164178
next_page = requests.request(method,
165179
next_url,
166180
data=data,
167-
auth=self.obj._http_options['auth'],
181+
auth=self.obj.http_options['auth'],
168182
headers=self.headers,
169183
params=kwargs,
170-
verify=self.obj._http_options['verify']).json()
184+
verify=self.obj.http_options['verify']).json()
171185
res.extend(next_page['results'])
172186
next_url = next_page.get('next')
173187

0 commit comments

Comments
 (0)