From a309aa4bddb093e43634cd89ff607a323518dc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 09:50:11 +0100 Subject: [PATCH 01/13] add doc badge to readme --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 65240f80..1f1dfb64 100644 --- a/README.rst +++ b/README.rst @@ -11,6 +11,9 @@ KQueen - Kubernetes cluster manager .. image:: https://coveralls.io/repos/github/Mirantis/kqueen/badge.svg?branch=master :target: https://coveralls.io/github/Mirantis/kqueen?branch=master +.. image:: https://readthedocs.org/projects/kqueen/badge/?version=master + :target: http://kqueen.readthedocs.io/en/master/?badge=master + Overview -------- From 417208f9cabfd6f35a9907281a859e36da87560e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 10:04:29 +0100 Subject: [PATCH 02/13] pass user id in X-User test header --- kqueen/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kqueen/conftest.py b/kqueen/conftest.py index 8d7a670f..da708f10 100644 --- a/kqueen/conftest.py +++ b/kqueen/conftest.py @@ -130,7 +130,7 @@ def auth_header(client): token=token, ), 'X-Test-Namespace': _user.namespace, - 'X-User': str(_user), + 'X-User': str(_user.id), } From 908c41df841c2ef2ff61de7b31b2ae12269b88fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 11:34:46 +0100 Subject: [PATCH 03/13] fix error with non-existing user ``` AttributeError: 'NoneType' object has no attribute 'password ``` --- kqueen/auth.py | 11 ++++++----- kqueen/tests/test_auth.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 kqueen/tests/test_auth.py diff --git a/kqueen/auth.py b/kqueen/auth.py index 1eebb879..24105e48 100644 --- a/kqueen/auth.py +++ b/kqueen/auth.py @@ -16,7 +16,7 @@ def authenticate(username, password): Args: username (str): Username to login - password (str): Passwore + password (str): Password Returns: user: authenticated user @@ -25,10 +25,11 @@ def authenticate(username, password): users = list(User.list(None, return_objects=True).values()) username_table = {u.username: u for u in users} user = username_table.get(username) - user_password = user.password.encode('utf-8') - given_password = password.encode('utf-8') - if user and user.active and bcrypt.checkpw(given_password, user_password): - return user + if user: + user_password = user.password.encode('utf-8') + given_password = password.encode('utf-8') + if user.active and bcrypt.checkpw(given_password, user_password): + return user def identity(payload): diff --git a/kqueen/tests/test_auth.py b/kqueen/tests/test_auth.py new file mode 100644 index 00000000..0353f1d7 --- /dev/null +++ b/kqueen/tests/test_auth.py @@ -0,0 +1,16 @@ +from kqueen.auth import authenticate + +import pytest + +def test_nonexisting_user(): + """ + Try authenticate with non-existing user + It is expected to return None but not fail + """ + + username = "non_existing_user" + password = "password" + + result = authenticate(username, password) + + assert result is None From ae4e5b9407378fafcbc29eff9c961a3b04c7091e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 12:21:09 +0100 Subject: [PATCH 04/13] add hint to set prometheus_multiproc_dir --- kqueen/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kqueen/middleware.py b/kqueen/middleware.py index 1895ba01..f2fd06c9 100644 --- a/kqueen/middleware.py +++ b/kqueen/middleware.py @@ -36,7 +36,7 @@ def setup_metrics(app): if 'prometheus_multiproc_dir' in os.environ: os.makedirs(os.environ['prometheus_multiproc_dir'], exist_ok=True) else: - raise Exception('Please set prometheus_multiproc_dir variable') + raise Exception('Please set prometheus_multiproc_dir variable using `export prometheus_multiproc_dir=$(mktemp -d)`') app.before_request(start_timer) app.after_request(record_request_data) From c8ae585f9a0a17d63d4835f4b045dc64bc5bc869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 12:53:55 +0100 Subject: [PATCH 05/13] don't prometheus_multiproc_dir when gunicorn isn't used --- kqueen/middleware.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kqueen/middleware.py b/kqueen/middleware.py index f2fd06c9..1552a441 100644 --- a/kqueen/middleware.py +++ b/kqueen/middleware.py @@ -30,13 +30,21 @@ def record_request_data(response): return response +def check_prometheus(): + is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "") + + if not is_gunicorn: + return True -def setup_metrics(app): - # TODO: detect multiprocess mode and raise only when needed if 'prometheus_multiproc_dir' in os.environ: os.makedirs(os.environ['prometheus_multiproc_dir'], exist_ok=True) else: raise Exception('Please set prometheus_multiproc_dir variable using `export prometheus_multiproc_dir=$(mktemp -d)`') + +def setup_metrics(app): + + check_prometheus() + app.before_request(start_timer) app.after_request(record_request_data) From 2deff5d4c24d2802f23cbe65a777d677613d9d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 13:51:53 +0100 Subject: [PATCH 06/13] add test for failing serialization refs #185 --- docker-compose.demo.yml | 43 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml index b71f84a6..db6a7aab 100644 --- a/docker-compose.demo.yml +++ b/docker-compose.demo.yml @@ -1,32 +1,33 @@ version: '2' services: - api: - image: kqueen/api:v0.11 - ports: - - 127.0.0.1:5000:5000 - depends_on: - - etcd - environment: - KQUEEN_CONFIG_FILE: config/prod.py - KQUEEN_ETCD_HOST: etcd - #KQUEEN_DEBUG: 'True' - BOOTSTRAP_ADMIN: 1 - BOOTSTRAP_ADMIN_USERNAME: admin - BOOTSTRAP_ADMIN_PASSWORD: default - BOOTSTRAP_ADMIN_ORGANIZATION: DemoOrg - BOOTSTRAP_ADMIN_NAMESPACE: demoorg - extra_hosts: - - "ci.mcp.mirantis.net:172.16.48.254" +# api: +# image: kqueen/api:master +# ports: +# - 127.0.0.1:5000:5000 +# depends_on: +# - etcd +# environment: +# KQUEEN_CONFIG_FILE: config/prod.py +# KQUEEN_ETCD_HOST: etcd +# KQUEEN_DEBUG: 'True' +# BOOTSTRAP_ADMIN: 1 +# BOOTSTRAP_ADMIN_USERNAME: admin +# BOOTSTRAP_ADMIN_PASSWORD: default +# BOOTSTRAP_ADMIN_ORGANIZATION: DemoOrg +# BOOTSTRAP_ADMIN_NAMESPACE: demoorg +# extra_hosts: +# - "ci.mcp.mirantis.net:172.16.48.254" ui: image: kqueen/ui:v0.1 + network_mode: host ports: - 127.0.0.1:5080:5080 - depends_on: - - api +# depends_on: +# - api environment: KQUEEN_UI_CONFIG_FILE: config/prod.py - KQUEENUI_KQUEEN_API_URL: http://api:5000/api/v1/ - KQUEENUI_KQUEEN_AUTH_URL: http://api:5000/api/v1/auth + KQUEENUI_KQUEEN_API_URL: http://172.16.0.65:5000/api/v1/ + KQUEENUI_KQUEEN_AUTH_URL: http://172.16.0.65:5000/api/v1/auth KQUEENUI_KQUEEN_SERVICE_USER_USERNAME: admin KQUEENUI_KQUEEN_SERVICE_USER_PASSWORD: default KQUEENUI_MAIL_SERVER: mail From 0b76c843fd5aec45a82290910a0cab4dc1247ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 16:43:58 +0100 Subject: [PATCH 07/13] fix failing serialization of empty JSONFields fixes #185 --- kqueen/blueprints/api/generic_views.py | 2 ++ kqueen/models.py | 1 + kqueen/storages/etcd.py | 5 +-- kqueen/storages/test_model_fields.py | 46 +++++++++++++++++++++++--- kqueen/tests/test_models.py | 20 +++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/kqueen/blueprints/api/generic_views.py b/kqueen/blueprints/api/generic_views.py index 7679a180..28e81077 100644 --- a/kqueen/blueprints/api/generic_views.py +++ b/kqueen/blueprints/api/generic_views.py @@ -68,6 +68,7 @@ def check_authorization(self): def set_object(self, *args, **kwargs): self.obj = get_object(self.get_class(), kwargs['pk'], current_identity) + # check authorization for given object self.check_authorization() @@ -143,6 +144,7 @@ def set_object(self, *args, **kwargs): namespace = current_identity.namespace except AttributeError: namespace = None + self.obj = list(self.get_class().list(namespace, return_objects=True).values()) self.check_authorization() diff --git a/kqueen/models.py b/kqueen/models.py index d6f1e2d1..430535d8 100644 --- a/kqueen/models.py +++ b/kqueen/models.py @@ -323,6 +323,7 @@ def get_engine_cls(self): except Exception as e: logger.error(repr(e)) _class = None + return _class def engine_status(self, save=True): diff --git a/kqueen/storages/etcd.py b/kqueen/storages/etcd.py index 69b657eb..803a8375 100644 --- a/kqueen/storages/etcd.py +++ b/kqueen/storages/etcd.py @@ -137,7 +137,7 @@ def encrypt(self): if not self.encrypted: return serialized - if self.value is not None: + if serialized is not None: key = self._get_encryption_key() padded = self._pad(str(serialized)) @@ -162,6 +162,7 @@ def decrypt(self, crypted, **kwargs): decrypted_decoded = decrypted.decode('utf-8') serialized = self._unpad(decrypted_decoded) + self.deserialize(serialized, **kwargs) def __str__(self): @@ -264,7 +265,7 @@ def set_value(self, value, **kwargs): self.value = value def serialize(self): - if self.value and isinstance(self.value, dict): + if isinstance(self.value, dict): return json.dumps(self.value) else: return None diff --git a/kqueen/storages/test_model_fields.py b/kqueen/storages/test_model_fields.py index a40a8949..36990bd5 100644 --- a/kqueen/storages/test_model_fields.py +++ b/kqueen/storages/test_model_fields.py @@ -37,6 +37,12 @@ class TestModel(Model, metaclass=ModelMeta): else: _namespace = namespace + print('Creating model with: required: {}, global_ns: {}, encrypted: {}'.format( + required, + global_ns, + encrypted + )) + return TestModel @@ -79,12 +85,13 @@ def get_model(request): def create_object(required=False, global_ns=False, encrypted=False): - model = create_model() + model = create_model(required, global_ns, encrypted) obj1 = model(namespace, **model_kwargs) obj2 = model(namespace, **model_kwargs) - obj2.save() + # don't validate first object because we don't have relation field + obj2.save(False) obj1.relation = obj2 @@ -188,6 +195,9 @@ class TestSerialization: def test_serizalization(self, get_object): serialized = get_object.serialize() + if get_object.__class__._encrypted: + pytest.skip('Unable to check serialization for encrypted class') + assert serialized == model_serialized(related=get_object.relation) def test_deserialization(self, get_object, monkeypatch): @@ -200,7 +210,7 @@ def fake(self, class_name): get_object.save() new_object = object_class.deserialize(get_object.serialize(), namespace=namespace) - assert new_object.get_dict() == get_object.get_dict() + assert new_object.get_dict(True) == get_object.get_dict(True) class TestGetDict: @@ -449,7 +459,7 @@ def test_encrypt_none(self, field_name): assert field.encrypt() is None @pytest.mark.parametrize('field_value', [i * 'a' for i in range(35)]) - def test_string_various_lenght(self, field_value): + def test_string_various_length(self, field_value): field_name = 'string' cls = create_model(False, False, True) obj = cls(namespace, **model_kwargs) @@ -475,3 +485,31 @@ def test_serialized_model_dont_contain_value(self, field_name, field_value): assert '"{}"'.format(field.value) not in serialized assert '"{}"'.format(field.serialize()) not in serialized + + +class TestModelEncryptionWithNone: + def test_serialization(self, monkeypatch, get_object): + def fake(self, class_name): + return get_object.__class__ + + monkeypatch.setattr(RelationField, '_get_related_class', fake) + + # load information about test setup + namespace = get_object.__class__._namespace + required = get_object.__class__._required + encrypted = get_object.__class__._encrypted + + + # set None to fields if possible + if not required: + for field_name in get_object.__class__.get_fields().keys(): + field = getattr(get_object, '_{}'.format(field_name)) + field.value = None + + assert field.value is None + + get_object.save() + + loaded = get_object.__class__.load(namespace, get_object.id) + + assert get_object.get_dict(True) == loaded.get_dict(True) diff --git a/kqueen/tests/test_models.py b/kqueen/tests/test_models.py index 867d44f4..ccafbe7f 100644 --- a/kqueen/tests/test_models.py +++ b/kqueen/tests/test_models.py @@ -1,3 +1,4 @@ +from datetime import datetime from kqueen.engines import __all__ as all_engines from kqueen.models import Cluster from kqueen.models import Provisioner @@ -178,3 +179,22 @@ def test_list_engines(self): engines = Provisioner.list_engines() assert engines == all_engines + + +class TestProvisionerSerialization: + def test_load_provisioner(self, user): + user.save() + provisioner = Provisioner( + user.namespace, + name='Manual provisioner', + state='OK', + engine='kqueen.engines.ManualEngine', + parameters={}, + created_at=datetime.utcnow().replace(microsecond=0), + owner=user + ) + provisioner.save() + + loaded = Provisioner.load(user.namespace, provisioner.id) + + assert loaded.get_dict(True) == provisioner.get_dict(True) From 299128e5b10af062a8f05a20a4b00ee10c539726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 17:02:49 +0100 Subject: [PATCH 08/13] fix pep8 --- kqueen/middleware.py | 1 + kqueen/storages/test_model_fields.py | 2 -- kqueen/tests/test_auth.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/kqueen/middleware.py b/kqueen/middleware.py index 1552a441..16034198 100644 --- a/kqueen/middleware.py +++ b/kqueen/middleware.py @@ -30,6 +30,7 @@ def record_request_data(response): return response + def check_prometheus(): is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "") diff --git a/kqueen/storages/test_model_fields.py b/kqueen/storages/test_model_fields.py index 36990bd5..4fdd9866 100644 --- a/kqueen/storages/test_model_fields.py +++ b/kqueen/storages/test_model_fields.py @@ -497,8 +497,6 @@ def fake(self, class_name): # load information about test setup namespace = get_object.__class__._namespace required = get_object.__class__._required - encrypted = get_object.__class__._encrypted - # set None to fields if possible if not required: diff --git a/kqueen/tests/test_auth.py b/kqueen/tests/test_auth.py index 0353f1d7..1bb1f0d8 100644 --- a/kqueen/tests/test_auth.py +++ b/kqueen/tests/test_auth.py @@ -1,6 +1,5 @@ from kqueen.auth import authenticate -import pytest def test_nonexisting_user(): """ From 7d61a52513569234582333f04d7823dde19df813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 18:51:06 +0100 Subject: [PATCH 09/13] create prometheus dir --- kqueen/middleware.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kqueen/middleware.py b/kqueen/middleware.py index 16034198..00127bb4 100644 --- a/kqueen/middleware.py +++ b/kqueen/middleware.py @@ -34,12 +34,11 @@ def record_request_data(response): def check_prometheus(): is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "") - if not is_gunicorn: - return True if 'prometheus_multiproc_dir' in os.environ: os.makedirs(os.environ['prometheus_multiproc_dir'], exist_ok=True) - else: + + elif is_gunicorn: raise Exception('Please set prometheus_multiproc_dir variable using `export prometheus_multiproc_dir=$(mktemp -d)`') From 6d4a8946fd4112d39941a80b962249fc52d68afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 20:58:49 +0100 Subject: [PATCH 10/13] print message for failed model validation closes #114 --- kqueen/auth.py | 10 ++++++++-- kqueen/blueprints/api/generic_views.py | 1 + kqueen/engines/test_manual.py | 3 ++- kqueen/storages/etcd.py | 11 ++++++----- kqueen/storages/test_model_fields.py | 7 +++++-- kqueen/tests/test_models.py | 4 +++- 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/kqueen/auth.py b/kqueen/auth.py index 24105e48..922f4fd4 100644 --- a/kqueen/auth.py +++ b/kqueen/auth.py @@ -91,19 +91,25 @@ def is_authorized(_user, policy_value, resource=None): ORGANIZATION = user['organization'].id # noqa: F841 ROLE = user['role'] if resource: - if not resource.validate(): + validation, _ = resource.validate() + if not validation: invalid = True + # test if we are validating on create view and if so, patch missing object id if not resource.id: resource.id = uuid4() - if resource.validate(): + validation, _ = resource.validate() + if validation: invalid = False resource.id = None + # if invalid resource is passed, let's just continue dispatch_request # so it can properly fail with 500 response code if invalid: logger.error('Cannot evaluate policy for invalid object: {}'.format(str(resource.get_dict()))) return True + + # TODO: check owner has id and, organization ... if hasattr(resource, 'owner'): OWNER = resource.owner.id # noqa: F841 OWNER_ORGANIZATION = resource.owner.organization.id # noqa: F841 diff --git a/kqueen/blueprints/api/generic_views.py b/kqueen/blueprints/api/generic_views.py index 28e81077..85207c96 100644 --- a/kqueen/blueprints/api/generic_views.py +++ b/kqueen/blueprints/api/generic_views.py @@ -177,6 +177,7 @@ def get_content(self, *args, **kwargs): def dispatch_request(self, *args, **kwargs): self.check_authentication() + if not request.json: abort(400, description='JSON data expected') else: diff --git a/kqueen/engines/test_manual.py b/kqueen/engines/test_manual.py index 7521b9bd..04f60a2e 100644 --- a/kqueen/engines/test_manual.py +++ b/kqueen/engines/test_manual.py @@ -107,7 +107,8 @@ def test_create_over_api(self): # load cluster_id = response.json['id'] obj = Cluster.load(self.namespace, cluster_id) - assert obj.validate() + validation, _ = obj.validate() + assert validation # check parameters assert obj.kubeconfig == data['kubeconfig'] diff --git a/kqueen/storages/etcd.py b/kqueen/storages/etcd.py index 803a8375..e34c83f3 100644 --- a/kqueen/storages/etcd.py +++ b/kqueen/storages/etcd.py @@ -564,8 +564,9 @@ def save(self, validate=True, assign_id=True): if assign_id: self.verify_id() - if validate and not self.validate(): - raise ValueError('Validation for model failed') + validation_status, validation_msg = self.validate() + if validate and not validation_status: + raise ValueError('Validation for model failed with: {}'.format(validation_msg)) key = self.get_db_key() logger.debug('Writing {} to {}'.format(self, key)) @@ -601,12 +602,12 @@ def validate(self): # validation # TODO: move to validate method of Field if field_object.required and field_object.value is None: - return False + return False, 'Required field {} is None'.format(field) if field_object.value and not field_object.validate(): - return False + return False, 'Field {} validation failed'.format(field) - return True + return True, None def _expand(self, obj): expanded = obj.get_dict() diff --git a/kqueen/storages/test_model_fields.py b/kqueen/storages/test_model_fields.py index 4fdd9866..5f1327f6 100644 --- a/kqueen/storages/test_model_fields.py +++ b/kqueen/storages/test_model_fields.py @@ -126,7 +126,9 @@ def setup(self): self.obj = model(namespace) def test_model_invalid(self): - assert not self.obj.validate() + validation, _ = self.obj.validate() + + assert not validation def test_save_raises(self): with pytest.raises(ValueError, match='Validation for model failed'): @@ -153,7 +155,8 @@ def test_required(self, required): model = create_model(required=required) obj = model(namespace, **model_kwargs) - assert obj.validate() != required + validation, _ = obj.validate() + assert validation != required class TestGetFieldNames: diff --git a/kqueen/tests/test_models.py b/kqueen/tests/test_models.py index ccafbe7f..c22ae758 100644 --- a/kqueen/tests/test_models.py +++ b/kqueen/tests/test_models.py @@ -23,7 +23,9 @@ def test_get_model_name(self, model_class, req): class TestClusterModel: def test_create(self, cluster): - assert cluster.validate() + validation, _ = cluster.validate() + + assert validation assert cluster.save() def test_load(self, cluster): From eb7c26499c1dbf44a49b14eee78fe7a8cff1da73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 21:27:11 +0100 Subject: [PATCH 11/13] add integration test for manual cluster --- kqueen/tests/test_manual_cluster.py | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 kqueen/tests/test_manual_cluster.py diff --git a/kqueen/tests/test_manual_cluster.py b/kqueen/tests/test_manual_cluster.py new file mode 100644 index 00000000..48086e0b --- /dev/null +++ b/kqueen/tests/test_manual_cluster.py @@ -0,0 +1,82 @@ +from flask import url_for +from kqueen.conftest import auth_header +from kqueen.models import User + +import json +import pytest +import yaml + + +@pytest.mark.usefixtures('client_class') +class TestInsertManualCluster: + def setup(self): + self.auth_header = auth_header(self.client) + self.namespace = self.auth_header['X-Test-Namespace'] + self.user = User.load(None, self.auth_header['X-User']) + + self.provisioner_id = None + + def test_run(self): + self.create_provisioner() + self.get_provisioners() + self.create_cluster() + self.get_cluster() + + def create_provisioner(self): + data = { + 'name': 'Manual provisioner', + 'engine': 'kqueen.engines.ManualEngine', + 'owner': 'User:{}'.format(self.user.id), + } + + response = self.client.post( + url_for('api.provisioner_create'), + data=json.dumps(data), + headers=self.auth_header, + content_type='application/json', + ) + + assert response.status_code == 200 + + self.provisioner_id = response.json['id'] + + def get_provisioners(self): + response = self.client.get( + url_for('api.provisioner_list'), + headers=self.auth_header, + content_type='application/json', + ) + + assert response.status_code == 200 + + content = response.data.decode(response.charset) + assert self.provisioner_id in content + + def create_cluster(self): + data = { + 'name': 'Manual cluster', + 'provisioner': 'Provisioner:{}'.format(self.provisioner_id), + 'kubeconfig': yaml.load(open('kubeconfig_localhost', 'r').read()), + 'owner': 'User:{}'.format(self.user.id), + } + + response = self.client.post( + url_for('api.cluster_create'), + data=json.dumps(data), + headers=self.auth_header, + content_type='application/json', + ) + + content = response.data.decode(response.charset) + + assert response.status_code == 200 + self.cluster_id = response.json['id'] + + def get_cluster(self): + response = self.client.get( + url_for('api.cluster_get', pk=self.cluster_id), + headers=self.auth_header, + content_type='application/json', + ) + + assert response.status_code == 200 From f06adee7d52dd8a1c2c605aa53177f8ca9348622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 21:28:30 +0100 Subject: [PATCH 12/13] fix pep8 --- kqueen/middleware.py | 1 - kqueen/tests/test_manual_cluster.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/kqueen/middleware.py b/kqueen/middleware.py index 00127bb4..f1985472 100644 --- a/kqueen/middleware.py +++ b/kqueen/middleware.py @@ -34,7 +34,6 @@ def record_request_data(response): def check_prometheus(): is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "") - if 'prometheus_multiproc_dir' in os.environ: os.makedirs(os.environ['prometheus_multiproc_dir'], exist_ok=True) diff --git a/kqueen/tests/test_manual_cluster.py b/kqueen/tests/test_manual_cluster.py index 48086e0b..7b3e9377 100644 --- a/kqueen/tests/test_manual_cluster.py +++ b/kqueen/tests/test_manual_cluster.py @@ -67,8 +67,6 @@ def create_cluster(self): content_type='application/json', ) - content = response.data.decode(response.charset) - assert response.status_code == 200 self.cluster_id = response.json['id'] From 2f9c260cb36da3179795f517febb3a6727ae6ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kukr=C3=A1l?= Date: Wed, 27 Dec 2017 23:01:53 +0100 Subject: [PATCH 13/13] revert to previous demo configuration --- docker-compose.demo.yml | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml index db6a7aab..b71f84a6 100644 --- a/docker-compose.demo.yml +++ b/docker-compose.demo.yml @@ -1,33 +1,32 @@ version: '2' services: -# api: -# image: kqueen/api:master -# ports: -# - 127.0.0.1:5000:5000 -# depends_on: -# - etcd -# environment: -# KQUEEN_CONFIG_FILE: config/prod.py -# KQUEEN_ETCD_HOST: etcd -# KQUEEN_DEBUG: 'True' -# BOOTSTRAP_ADMIN: 1 -# BOOTSTRAP_ADMIN_USERNAME: admin -# BOOTSTRAP_ADMIN_PASSWORD: default -# BOOTSTRAP_ADMIN_ORGANIZATION: DemoOrg -# BOOTSTRAP_ADMIN_NAMESPACE: demoorg -# extra_hosts: -# - "ci.mcp.mirantis.net:172.16.48.254" + api: + image: kqueen/api:v0.11 + ports: + - 127.0.0.1:5000:5000 + depends_on: + - etcd + environment: + KQUEEN_CONFIG_FILE: config/prod.py + KQUEEN_ETCD_HOST: etcd + #KQUEEN_DEBUG: 'True' + BOOTSTRAP_ADMIN: 1 + BOOTSTRAP_ADMIN_USERNAME: admin + BOOTSTRAP_ADMIN_PASSWORD: default + BOOTSTRAP_ADMIN_ORGANIZATION: DemoOrg + BOOTSTRAP_ADMIN_NAMESPACE: demoorg + extra_hosts: + - "ci.mcp.mirantis.net:172.16.48.254" ui: image: kqueen/ui:v0.1 - network_mode: host ports: - 127.0.0.1:5080:5080 -# depends_on: -# - api + depends_on: + - api environment: KQUEEN_UI_CONFIG_FILE: config/prod.py - KQUEENUI_KQUEEN_API_URL: http://172.16.0.65:5000/api/v1/ - KQUEENUI_KQUEEN_AUTH_URL: http://172.16.0.65:5000/api/v1/auth + KQUEENUI_KQUEEN_API_URL: http://api:5000/api/v1/ + KQUEENUI_KQUEEN_AUTH_URL: http://api:5000/api/v1/auth KQUEENUI_KQUEEN_SERVICE_USER_USERNAME: admin KQUEENUI_KQUEEN_SERVICE_USER_PASSWORD: default KQUEENUI_MAIL_SERVER: mail