コード例 #1
0
def handle_mapping_post(req, name):
    try:
        rc = getIncomingJSON(req, 'prefix', 'service')

        logging.debug("handle_mapping_post %s: got args %s" %
                      (name, rc.toDict()))

        if not rc:
            return rc

        prefix = rc.prefix
        service = rc.service
        rewrite = '/'

        if 'rewrite' in rc:
            rewrite = rc.rewrite

        logging.debug("handle_mapping_post %s: pfx %s => svc %s (rewrite %s)" %
                      (name, prefix, service, rewrite))

        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute(
            'INSERT INTO mappings VALUES(:name, :prefix, :service, :rewrite)',
            locals())
        conn.commit()

        app.reconfigurator.trigger()

        return RichStatus.OK(name=name)
    except pg8000.Error as e:
        return RichStatus.fromError("%s: could not save info: %s" % (name, e))
コード例 #2
0
    def delete_mapping(self, name):
        if not self:
            return self.status

        try:
            # Have to delete modules first since it holds a foreign key
            rc = self._delete_mapping_modules(name)

            if not rc:
                self.conn.rollback()
                return rc

            modules_deleted = rc.modules_deleted

            rc = self._delete_mapping_basics(name)

            if not rc:
                self.conn.rollback()
                return rc

            self.conn.commit()
            return RichStatus.OK(name=name,
                                 deleted=rc.deleted,
                                 modules_deleted=modules_deleted)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "delete_mapping %s: could not delete mapping: %s" % (name, e))
コード例 #3
0
ファイル: ambassador.py プロジェクト: ygmkk/ambassador
def setup():
    try:
        conn = get_db("postgres")
        conn.autocommit = True

        cursor = conn.cursor()
        cursor.execute(
            "SELECT 1 FROM pg_database WHERE datname = 'ambassador'")
        results = cursor.fetchall()

        if not results:
            cursor.execute("CREATE DATABASE ambassador")

        conn.close()
    except pg8000.Error as e:
        return RichStatus.fromError("no ambassador database in setup: %s" % e)

    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()
        cursor.execute(AMBASSADOR_TABLE_SQL)
        cursor.execute(PRINCIPAL_TABLE_SQL)
        conn.commit()
        conn.close()
    except pg8000.Error as e:
        return RichStatus.fromError("no data tables in setup: %s" % e)

    return RichStatus.OK()
コード例 #4
0
    def delete_consumer(self, consumer_id):
        if not self:
            return self.status

        try:
            # Have to delete modules first since it holds a foreign key
            rc = self._delete_consumer_modules(consumer_id)

            if not rc:
                self.conn.rollback()
                return rc

            modules_deleted = rc.modules_deleted

            rc = self._delete_consumer_basics(consumer_id)

            if not rc:
                self.conn.rollback()
                return rc

            self.conn.commit()
            return RichStatus.OK(consumer_id=consumer_id,
                                 deleted=rc.deleted,
                                 modules_deleted=modules_deleted)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "delete_consumer %s: could not delete consumer: %s" %
                (consumer_id, e))
コード例 #5
0
ファイル: ambassador.py プロジェクト: ygmkk/ambassador
def getIncomingJSON(req, *needed):
    """
    Pull incoming arguments from JSON into a RichStatus. 'needed' specifies
    keys that are mandatory, but _all keys are converted_, so for any optional
    things, just check if the key is present in the returned RichStatus.

    If any 'needed' keys are missing, returns a False RichStatus including error
    text. If all 'needed' keys are present, returns a True RichStatus with elements
    copied from the input.
    """

    try:
        incoming = req.get_json()
    except Exception as e:
        return RichStatus.fromError("invalid JSON: %s" % e)

    logging.debug("getIncomingJSON: %s" % incoming)

    if not incoming:
        incoming = {}

    missing = []

    for key in needed:
        if key not in incoming:
            missing.append(key)

    if missing:
        return RichStatus.fromError("Required fields missing: %s" %
                                    " ".join(missing))
    else:
        return RichStatus.OK(**incoming)
コード例 #6
0
ファイル: ambassador.py プロジェクト: ygmkk/ambassador
def handle_principal_post(req, name):
    try:
        rc = getIncomingJSON(req, 'fingerprint')

        logging.debug("handle_principal_post %s: got args %s" %
                      (name, rc.toDict()))

        if not rc:
            return rc

        fingerprint = rc.fingerprint

        logging.debug("handle_principal_post %s: fingerprint %s" %
                      (name, fingerprint))

        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute('INSERT INTO principals VALUES(:name, :fingerprint)',
                       locals())
        conn.commit()

        return RichStatus.OK(name=name)
    except pg8000.Error as e:
        return RichStatus.fromError("%s: could not save info: %s" % (name, e))
コード例 #7
0
    def _fetch_consumer_basics(self, where):
        if not self:
            return self.status

        try:
            sql = "SELECT consumer_id, username, fullname, shortname FROM consumers WHERE %s" % where.sql

            self.cursor.execute(sql, where.keys)

            if self.cursor.rowcount == 0:
                return RichStatus.fromError("consumer %s not found" % where.hr)

            if self.cursor.rowcount > 1:
                return RichStatus.fromError(
                    "consumer %s matched more than one entry?" % where.hr)

            # We know there's exactly one consumer match. Good.

            consumer_id, username, fullname, shortname = self.cursor.fetchone()

            return RichStatus.OK(consumer_id=consumer_id,
                                 username=username,
                                 fullname=fullname,
                                 shortname=shortname)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "fetch_consumer_basics %s: could not fetch info: %s" %
                (where.hr, e))
コード例 #8
0
    def store_consumer(self, consumer_id, username, fullname, shortname,
                       modules):
        if not self:
            return self.status

        try:
            rc = self._store_consumer_basics(consumer_id, username, fullname,
                                             shortname)

            if not rc:
                self.conn.rollback()
                return rc

            for module_name in modules.keys():
                rc = self._store_consumer_module(consumer_id, module_name,
                                                 modules[module_name])

                if not rc:
                    self.conn.rollback()
                    return rc

            self.conn.commit()
            return RichStatus.OK(consumer_id=consumer_id)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "store_consumer %s: could not store consumer: %s" %
                (consumer_id, e))
コード例 #9
0
    def _fetch_mapping_basics(self, name):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                "SELECT prefix, service, rewrite FROM mappings WHERE name = :name",
                locals())

            if self.cursor.rowcount == 0:
                return RichStatus.fromError("mapping %s not found" % name)

            if self.cursor.rowcount > 1:
                return RichStatus.fromError(
                    "mapping %s matched more than one entry?" % name)

            # We know there's exactly one mapping match. Good.

            prefix, service, rewrite = self.cursor.fetchone()

            return RichStatus.OK(name=name,
                                 prefix=prefix,
                                 service=service,
                                 rewrite=rewrite)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "fetch_mapping_basics %s: could not fetch info: %s" %
                (name, e))
コード例 #10
0
ファイル: ambassador.py プロジェクト: twitface/ambassador
def handle_service_get(req, name):
    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute("SELECT prefix FROM services WHERE name = :name", locals())
        [ prefix ] = cursor.fetchone()

        return RichStatus.OK(name=name, prefix=prefix)
    except pg8000.Error as e:
        return RichStatus.fromError("%s: could not fetch info: %s" % (name, e))
コード例 #11
0
    def wrapper(*args, **kwds):
        rc = RichStatus.fromError("impossible error")
        logging.debug("%s: method %s" % (func_name, request.method))

        try:
            rc = f(*args, **kwds)
        except Exception as e:
            logging.exception(e)
            rc = RichStatus.fromError("%s: %s failed: %s" %
                                      (func_name, request.method, e))

        return jsonify(rc.toDict())
コード例 #12
0
ファイル: ambassador.py プロジェクト: ygmkk/ambassador
def handle_principal_get(req, name):
    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute("SELECT fingerprint FROM principals WHERE name = :name",
                       locals())
        [fingerprint] = cursor.fetchone()

        return RichStatus.OK(name=name, fingerprint=fingerprint)
    except pg8000.Error as e:
        return RichStatus.fromError("%s: could not fetch info: %s" % (name, e))
コード例 #13
0
    def store_principal(self, name, fingerprint):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                'INSERT INTO principals VALUES(:name, :fingerprint)', locals())
            self.conn.commit()

            return RichStatus.OK(name=name)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "store_principal %s: could not save info: %s" % (name, e))
コード例 #14
0
ファイル: ambassador.py プロジェクト: twitface/ambassador
def handle_service_del(req, name):
    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute("DELETE FROM services WHERE name = :name", locals())
        conn.commit()

        app.reconfigurator.trigger()

        return RichStatus.OK(name=name)
    except pg8000.Error as e:
        return RichStatus.fromError("%s: could not delete service: %s" % (name, e))
コード例 #15
0
    def _delete_mapping_basics(self, name):
        if not self:
            return self.status

        try:
            self.cursor.execute("DELETE FROM mappings WHERE name = :name",
                                locals())
            deleted = self.cursor.rowcount

            return RichStatus.OK(name=name, deleted=deleted)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "delete_mapping %s: could not delete mapping info: %s" %
                (name, e))
コード例 #16
0
    def delete_module(self, name):
        if not self:
            return self.status

        try:
            self.cursor.execute("DELETE FROM modules WHERE name = :name",
                                locals())
            deleted = self.cursor.rowcount
            self.conn.commit()

            return RichStatus.OK(name=name, deleted=deleted)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "delete_module %s: could not delete module: %s" % (name, e))
コード例 #17
0
    def fetch_all_modules(self):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                "SELECT name, config FROM modules ORDER BY name")

            modules = {name: config for name, config in self.cursor}

            return RichStatus.OK(modules=modules, count=len(modules.keys()))
        except pg8000.Error as e:
            return RichStatus.fromError(
                "fetch_all_modules: could not fetch info: %s" % e)
コード例 #18
0
    def _delete_consumer_basics(self, consumer_id):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                "DELETE FROM consumers WHERE consumer_id = :consumer_id",
                locals())
            deleted = self.cursor.rowcount

            return RichStatus.OK(consumer_id=consumer_id, deleted=deleted)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "delete_consumer %s: could not delete consumer info: %s" %
                (consumer_id, e))
コード例 #19
0
ファイル: ambassador.py プロジェクト: twitface/ambassador
def fetch_all_services():
    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute("SELECT name, prefix FROM services ORDER BY name, prefix")

        services = []

        for name, prefix in cursor:
            services.append({ 'name': name, 'prefix': prefix })

        return RichStatus.OK(services=services, count=len(services))
    except pg8000.Error as e:
        return RichStatus.fromError("services: could not fetch info: %s" % e)
コード例 #20
0
    def generate_envoy_config(self, template=None, template_dir=None):
        # Finally! Render the template to JSON...
        envoy_json = self.to_json(template=template, template_dir=template_dir)
        rc = RichStatus.fromError("impossible")

        # ...and use the JSON parser as a final sanity check.
        try:
            obj = json.loads(envoy_json)
            rc = RichStatus.OK(msg="Envoy configuration OK", envoy_config=obj)
        except json.decoder.JSONDecodeError as e:
            rc = RichStatus.fromError("Invalid Envoy configuration: %s" %
                                      str(e),
                                      raw=envoy_json,
                                      exception=e)

        return rc
コード例 #21
0
ファイル: ambassador.py プロジェクト: twitface/ambassador
def new_config(envoy_base_config=None, envoy_tls_config=None,
               envoy_config_path=None, envoy_restarter_pid=None):
    if not envoy_base_config:
        envoy_base_config = app.envoy_base_config

    if not envoy_tls_config:
        envoy_tls_config = app.envoy_tls_config

    if not envoy_config_path:
        envoy_config_path = app.envoy_config_path

    if not envoy_restarter_pid:
        envoy_restarter_pid = app.envoy_restarter_pid

    config = EnvoyConfig(envoy_base_config, envoy_tls_config)

    rc = fetch_all_services()
    num_services = 0

    if rc and rc.services:
        num_services = len(rc.services)

        for service in rc.services:
            config.add_service(service['name'], service['prefix'])

    config.write_config(envoy_config_path)

    if envoy_restarter_pid > 0:
        os.kill(envoy_restarter_pid, signal.SIGHUP)

    return RichStatus.OK(count=num_services)
コード例 #22
0
    def __init__(self):
        pg8000.paramstyle = 'named'

        self.status = RichStatus.OK()

        # Make sure we have tables and such.
        #
        # All of these functions update self.status if something goes wrong, and they're
        # no-ops if not self.status.

        self.conn = self._get_connection()
        logging.info("storage_postgres: conn %sset, status %s" %
                     ("NOT " if not self.conn else "", self.status))

        # Get a cursor and verify our database.
        self.cursor = self._get_cursor()
        self._verify_database()

        # Switch autocommit off...
        self._autocommit(False)

        # ...grab a new cursor...
        self.cursor = self._get_cursor()

        # ...and make sure our tables are OK.
        self._verify_tables()
コード例 #23
0
    def generate_envoy_config(self,
                              template=None,
                              template_dir=None,
                              **kwargs):
        # Finally! Render the template to JSON...
        envoy_json = self.to_json(template=template, template_dir=template_dir)

        # We used to use the JSON parser as a final sanity check here. That caused
        # Forge some issues, so it's turned off for now.

        # rc = RichStatus.fromError("impossible")

        # # ...and use the JSON parser as a final sanity check.
        # try:
        #     obj = json.loads(envoy_json)
        #     rc = RichStatus.OK(msg="Envoy configuration OK", envoy_config=obj)
        # except json.decoder.JSONDecodeError as e:
        #     rc = RichStatus.fromError("Invalid Envoy configuration: %s" % str(e),
        #                               raw=envoy_json, exception=e)

        # Go ahead and report that we generated an Envoy config, if we can.
        scout_result = AmbassadorConfig.scout_report(action="config",
                                                     result=True,
                                                     generated=True,
                                                     **kwargs)

        rc = RichStatus.OK(envoy_config=envoy_json, scout_result=scout_result)

        self.logger.debug("Scout reports %s" % json.dumps(rc.scout_result))

        return rc
コード例 #24
0
    def _get_connection(self, autocommit=False):
        # Figure out where the DB lives...

        self.db_name = "postgres"
        self.db_host = "ambassador-store"
        self.db_port = 5432

        if "AMBASSADOR_DB_NAME" in os.environ:
            self.db_name = os.environ["AMBASSADOR_DB_NAME"]

        if "AMBASSADOR_DB_HOST" in os.environ:
            self.db_host = os.environ["AMBASSADOR_DB_HOST"]

        if "AMBASSADOR_DB_PORT" in os.environ:
            self.db_port = int(os.environ["AMBASSADOR_DB_PORT"])

        conn = None

        try:
            conn = pg8000.connect(user="******",
                                  password="******",
                                  database=self.db_name,
                                  host=self.db_host,
                                  port=self.db_port)

            # Start with autocommit on.
            conn.autocommit = True
        except pg8000.Error as e:
            self.status = RichStatus.fromError(
                "could not connect to db %s:%d - %s" %
                (self.db_host, self.db_port, e))

        return conn
コード例 #25
0
    def process_object(self, obj):
        obj_version = obj['apiVersion']
        obj_kind = obj['kind']
        obj_name = obj['name']

        # ...save the source info...
        source_key = "%s.%d" % (self.filename, self.ocount)
        self.sources[source_key] = {
            'kind': obj_kind,
            'version': obj_version,
            'name': obj_name,
            'filename': self.filename,
            'index': self.ocount,
            'yaml': yaml.safe_dump(obj, default_flow_style=False)
        }

        source_map = self.source_map.setdefault(self.filename, {})
        source_map[source_key] = True

        # OK. What is this thing?
        rc = self.validate_object(obj)

        if not rc:
            # Well that's no good.
            return rc

        # OK, so far so good. Grab the handler for this object type.
        handler_name = "handle_%s" % obj_kind.lower()
        handler = getattr(self, handler_name, None)

        if not handler:
            handler = self.save_object
            self.logger.warning("%s[%d]: no handler for %s, just saving" %
                                (self.filename, self.ocount, obj_kind))
        else:
            self.logger.debug("%s[%d]: handling %s..." %
                              (self.filename, self.ocount, obj_kind))

        try:
            handler(source_key, obj, obj_name, obj_kind, obj_version)
        except Exception as e:
            # Bzzzt.
            return RichStatus.fromError("could not process %s object: %s" %
                                        (obj_kind, e))

        # OK, all's well.
        return RichStatus.OK(msg="%s object processed successfully" % obj_kind)
コード例 #26
0
    def _store_mapping_basics(self, name, prefix, service, rewrite):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                '''
                INSERT INTO mappings VALUES(:name, :prefix, :service, :rewrite)
                    ON CONFLICT (name) DO UPDATE SET
                        name=EXCLUDED.name, prefix=EXCLUDED.prefix, 
                        service=EXCLUDED.service, rewrite=EXCLUDED.rewrite
            ''', locals())

            return RichStatus.OK(name=name)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "store_mapping %s: could not save info: %s" % (name, e))
コード例 #27
0
    def validate_object(self, obj):
        # Each object must be a dict, and must include "apiVersion"
        # and "type" at toplevel.

        if not isinstance(obj, collections.Mapping):
            return RichStatus.fromError("not a dictionary")

        if not (("apiVersion" in obj) and ("kind" in obj) and ("name" in obj)):
            return RichStatus.fromError("must have apiVersion, kind, and name")

        obj_version = obj['apiVersion']
        obj_kind = obj['kind']
        obj_name = obj['name']

        if obj_version.startswith("ambassador/"):
            obj_version = obj_version.split('/')[1]
        else:
            return RichStatus.fromError("apiVersion %s unsupported" %
                                        obj_version)

        schema_key = "%s-%s" % (obj_version, obj_kind)

        schema = self.schemas.get(schema_key, None)

        if not schema:
            schema_path = os.path.join(self.schema_dir_path, obj_version,
                                       "%s.schema" % obj_kind)

            try:
                schema = json.load(open(schema_path, "r"))
            except OSError:
                self.logger.debug("no schema at %s, skipping" % schema_path)
            except json.decoder.JSONDecodeError as e:
                self.logger.warning("corrupt schema at %s, skipping (%s)" %
                                    (schema_path, e))

        if schema:
            self.schemas[schema_key] = schema
            try:
                jsonschema.validate(obj, schema)
            except jsonschema.exceptions.ValidationError as e:
                return RichStatus.fromError("not a valid %s: %s" %
                                            (obj_kind, e))

        return RichStatus.OK(msg="valid %s" % obj_kind,
                             details=(obj_kind, obj_version, obj_name))
コード例 #28
0
ファイル: ambassador.py プロジェクト: ygmkk/ambassador
def fetch_all_principals():
    try:
        conn = get_db("ambassador")
        cursor = conn.cursor()

        cursor.execute(
            "SELECT name, fingerprint FROM principals ORDER BY name, fingerprint"
        )

        principals = []

        for name, fingerprint in cursor:
            principals.append({'name': name, 'fingerprint': fingerprint})

        return RichStatus.OK(principals=principals, count=len(principals))
    except pg8000.Error as e:
        return RichStatus.fromError("principals: could not fetch info: %s" % e)
コード例 #29
0
    def fetch_mapping_module(self, name, module_name=None):
        if not self:
            return self.status

        if not module_name:
            return RichStatus.fromError(
                "fetch_mapping_module: module_name is required")

        return self._fetch_mapping_modules(name, module_name=module_name)
コード例 #30
0
    def fetch_all_principals(self):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                "SELECT name, fingerprint FROM principals ORDER BY name, fingerprint"
            )

            principals = []

            for name, fingerprint in self.cursor:
                principals.append({'name': name, 'fingerprint': fingerprint})

            return RichStatus.OK(principals=principals, count=len(principals))
        except pg8000.Error as e:
            return RichStatus.fromError(
                "fetch_all_principals: could not fetch info: %s" % e)