From 79a59c52f00876997e59ab17f88c8a1c7c3b8f41 Mon Sep 17 00:00:00 2001 From: Dmitry Nikishov Date: Tue, 15 Nov 2016 08:53:20 +0000 Subject: [PATCH] Added changes whitelists to API Change-Id: I470caeaf0145ad1f967a9ad71fc4b90908509a44 --- fuel_external_git/extension.py | 6 +- fuel_external_git/handlers.py | 91 ++++++++++++++++++- fuel_external_git/json_schema.py | 26 +++++- .../8736ad38ca31_add_whitelist_table.py | 45 +++++++++ fuel_external_git/models.py | 7 ++ fuel_external_git/objects.py | 27 ++++++ 6 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 fuel_external_git/migrations/versions/8736ad38ca31_add_whitelist_table.py diff --git a/fuel_external_git/extension.py b/fuel_external_git/extension.py index f9547ed..81db400 100644 --- a/fuel_external_git/extension.py +++ b/fuel_external_git/extension.py @@ -168,7 +168,11 @@ class ExternalGit(BaseExtension): 'handler': handlers.GitRepoHandler}, {'uri': r'/clusters/(?P\d+)/git-repos/(?P\d+)/init?$', - 'handler': handlers.GitRepoInit}] + 'handler': handlers.GitRepoInit}, + {'uri': r'/clusters/(?P\d+)/changes-whitelist/$', + 'handler': handlers.ChangesWhitelistRuleCollectionHandler}, + {'uri': r'/clusters/changes-whitelist/(?P\d+)?$', + 'handler': handlers.ChangesWhitelistRuleHandler}] data_pipelines = [ OpenStackConfigPipeline, diff --git a/fuel_external_git/handlers.py b/fuel_external_git/handlers.py index 8587ca9..7e7363b 100644 --- a/fuel_external_git/handlers.py +++ b/fuel_external_git/handlers.py @@ -11,6 +11,8 @@ # under the License. from fuel_external_git import json_schema +from fuel_external_git.objects import ChangesWhitelistRule +from fuel_external_git.objects import ChangesWhitelistRuleCollection from fuel_external_git.objects import GitRepo from fuel_external_git.objects import GitRepoCollection @@ -30,8 +32,8 @@ REPOS_DIR = '/var/lib/fuel_repos' class GitRepoValidator(base.BasicValidator): - single_schema = json_schema.single_schema - collection_schema = json_schema.collection_schema + single_schema = json_schema.gitrepo_single_schema + collection_schema = json_schema.gitrepo_collection_schema _blocked_for_update = ( 'env_id', @@ -77,6 +79,44 @@ class GitRepoValidator(base.BasicValidator): return d +class ChangesWhitelistRuleValidator(base.BasicValidator): + + single_schema = json_schema.changeswhitelistrule_single_schema + collection_schema = json_schema.changeswhitelistrule_collection_schema + + _blocked_for_update = ( + 'env_id', + ) + + @classmethod + def validate_update(self, data, instance): + d = self.validate_json(data) + for k in self._blocked_for_update: + if k in d and getattr(instance, k) != d[k]: + raise errors.InvalidData( + u"Changing '{0}' for white list is prohibited".format(k), + log_message=True + ) + + return d + + # TODO(dnikishov): investigate if there's a more simple way to do this + @classmethod + def validate_one_or_multiple(self, data): + d = self.validate_json(data) + if not isinstance(d, list): + d = [d] + for item in d: + self.validate_schema(item, self.single_schema) + + return d + + # This is required for inherited handlers to work + @classmethod + def validate_delete(self, *args, **kwargs): + pass + + class GitRepoCollectionHandler(CollectionHandler): collection = GitRepoCollection validator = GitRepoValidator @@ -157,3 +197,50 @@ class GitRepoInit(BaseHandler): obj = GitRepo.get_by_cluster_id(obj.env_id) GitRepo.init(obj) raise self.http(200, "{}") + + +class ChangesWhitelistRuleHandler(SingleHandler): + single = ChangesWhitelistRule + validator = ChangesWhitelistRuleValidator + + +class ChangesWhitelistRuleCollectionHandler(CollectionHandler): + collection = ChangesWhitelistRuleCollection + validator = ChangesWhitelistRuleValidator + + @handle_errors + @validate + @serialize + def GET(self, env_id): + """:returns: JSONized REST object. + + :http: * 200 (OK) + * 404 (dashboard entry not found in db) + """ + self.get_object_or_404(objects.Cluster, env_id) + rules = self.collection.get_by_env_id(env_id) + return self.collection.to_list(rules) + + @handle_errors + @serialize + def POST(self, env_id): + """:returns: JSONized REST object. + + :http: * 201 (object successfully created) + * 400 (invalid object data specified) + * 409 (object with such parameters already exists) + """ + data = self.checked_data( + validate_method=self.validator.validate_one_or_multiple + ) + for item in data: + item['env_id'] = env_id + + new_objs = [] + try: + for item in data: + new_objs.append(self.collection.create(item)) + except errors.CannotCreate as exc: + raise self.http(400, exc.message) + + raise self.http(201, self.collection.to_json(new_objs)) diff --git a/fuel_external_git/json_schema.py b/fuel_external_git/json_schema.py index 064dd29..8eea7dd 100644 --- a/fuel_external_git/json_schema.py +++ b/fuel_external_git/json_schema.py @@ -10,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -single_schema = { +gitrepo_single_schema = { "$schema": "http://json-schema.org/draft-04/schema#", "title": "GitRepo", "description": "Serialized GitRepo object", @@ -25,10 +25,30 @@ single_schema = { } } -collection_schema = { +gitrepo_collection_schema = { "$schema": "http://json-schema.org/draft-04/schema#", "title": "GitRepo Collection", "description": "Serialized GitRepo collection", "type": "object", - "items": single_schema["properties"] + "items": gitrepo_single_schema["properties"] +} + +changeswhitelistrule_single_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "ChangesWhitelistRule", + "description": "Serialized ChangesWhitelistRule object", + "type": "object", + "properties": { + "id": {"type": "number"}, + "env_id": {"type": "number"}, + "rule": {"type": "string"}, + } +} + +changeswhitelistrule_collection_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "ChangesWhitelistRule Collection", + "description": "Serialized ChangesWhitelistRule collection", + "type": "object", + "items": changeswhitelistrule_single_schema["properties"] } diff --git a/fuel_external_git/migrations/versions/8736ad38ca31_add_whitelist_table.py b/fuel_external_git/migrations/versions/8736ad38ca31_add_whitelist_table.py new file mode 100644 index 0000000..4b121d6 --- /dev/null +++ b/fuel_external_git/migrations/versions/8736ad38ca31_add_whitelist_table.py @@ -0,0 +1,45 @@ +# 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. + +"""add whitelist table + +Revision ID: 8736ad38ca31 +Revises: adb78f70605d +Create Date: 2016-11-07 10:50:38.168018 + +""" + +# revision identifiers, used by Alembic. +revision = '8736ad38ca31' +down_revision = 'adb78f70605d' +branch_labels = None +depends_on = None + +from alembic import context +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + table_prefix = context.config.get_main_option('table_prefix') + op.create_table( + table_prefix + 'changes_whitelist', + sa.Column('id', sa.Integer(), nullable=False, primary_key=True), + sa.Column('env_id', sa.Integer(), nullable=False), + sa.Column('rule', sa.String(255), + server_default='', nullable=False) + ) + + +def downgrade(): + table_prefix = context.config.get_main_option('table_prefix') + op.drop_table(table_prefix + 'changes_whitelist') diff --git a/fuel_external_git/models.py b/fuel_external_git/models.py index 5bca03c..963bea4 100644 --- a/fuel_external_git/models.py +++ b/fuel_external_git/models.py @@ -30,3 +30,10 @@ class GitRepo(Base): user_key = Column(String(255), default='', server_default='', nullable=False) manage_master = Column(Boolean(), nullable=False) + + +class ChangesWhitelistRule(Base): + __tablename__ = 'fuel_external_git_changes_whitelist' + id = Column(Integer, primary_key=True) + env_id = Column(Integer, nullable=False) + rule = Column(String(255), server_default='', nullable=False) diff --git a/fuel_external_git/objects.py b/fuel_external_git/objects.py index f9061f3..b6e786c 100644 --- a/fuel_external_git/objects.py +++ b/fuel_external_git/objects.py @@ -18,6 +18,7 @@ import yaml from distutils.dir_util import copy_tree from fuel_external_git import const +from fuel_external_git.models import ChangesWhitelistRule from fuel_external_git.models import GitRepo from git import exc @@ -45,6 +46,14 @@ class GitRepoSerializer(BasicSerializer): ) +class ChangesWhitelistRuleSerializer(BasicSerializer): + fields = ( + "id", + "env_id", + "rule" + ) + + class GitRepo(NailgunObject): model = GitRepo serializer = GitRepoSerializer @@ -198,3 +207,21 @@ class GitRepo(NailgunObject): class GitRepoCollection(NailgunCollection): single = GitRepo + + +class ChangesWhitelistRule(NailgunObject): + model = ChangesWhitelistRule + serializer = ChangesWhitelistRuleSerializer + + +class ChangesWhitelistRuleCollection(NailgunCollection): + single = ChangesWhitelistRule + + @classmethod + def get_by_env_id(self, env_id): + whitelist = set() + for rule in ChangesWhitelistRuleCollection.all(): + if env_id == rule.env_id: + whitelist.add(rule) + + return whitelist