Example #1
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))
Example #2
0
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()
Example #3
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))
Example #4
0
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)
Example #5
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())
Example #6
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))
Example #7
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))
Example #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))
Example #9
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
Example #10
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
Example #11
0
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))
Example #12
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))
Example #13
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))
Example #14
0
    def _autocommit(self, setting):
        if not self:
            return

        if self.conn:
            self.conn.autocommit = setting
        else:
            self.status = RichStatus.fromError(
                "cannot set autocommit with no connection")
Example #15
0
def root():
    rc = RichStatus.fromError("impossible error")
    logging.debug("handle_services: method %s" % request.method)
    
    try:
        rc = setup()

        if rc:
            if request.method == 'PUT':
                app.reconfigurator.trigger()
                rc = RichStatus.OK(msg="reconfigure requested")
            else:
                rc = handle_service_list(request)
    except Exception as e:
        logging.exception(e)
        rc = RichStatus.fromError("handle_services: %s failed: %s" % (request.method, e))

    return jsonify(rc.toDict())
Example #16
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)
Example #17
0
def handle_service(name):
    rc = RichStatus.fromError("impossible error")
    logging.debug("handle_service %s: method %s" % (name, request.method))
    
    try:
        rc = setup()

        if rc:
            if request.method == 'POST':
                rc = handle_service_post(request, name)
            elif request.method == 'DELETE':
                rc = handle_service_del(request, name)
            else:
                rc = handle_service_get(request, name)
    except Exception as e:
        logging.exception(e)
        rc = RichStatus.fromError("%s: %s failed: %s" % (name, request.method, e))

    return jsonify(rc.toDict())
Example #18
0
    def fetch_consumer_module(self, consumer_id, module_name=None):
        if not self:
            return self.status

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

        return self._fetch_consumer_modules(consumer_id,
                                            module_name=module_name)
Example #19
0
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))
Example #20
0
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))
Example #21
0
def getIncomingJSON(req, *needed):
    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)
Example #22
0
    def _get_cursor(self):
        if not self:
            return

        cursor = None

        try:
            cursor = self.conn.cursor()
        except pg8000.Error as e:
            self.status = RichStatus.fromError(
                "could not get database cursor: %s" % e)

        return cursor
Example #23
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))
Example #24
0
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))
Example #25
0
def handle_approved():
    rc = RichStatus.fromError("impossible error")
    logging.debug("handle_principals: method %s" % request.method)

    try:
        rc = setup()

        if rc:
            rc = handle_principal_list(request)

        if rc:
            principals = [{
                "fingerprint_sha256": x['fingerprint']
            } for x in rc.principals]

            rc = RichStatus.OK(certificates=principals)

    except Exception as e:
        logging.exception(e)
        rc = RichStatus.fromError("handle_principals: %s failed: %s" %
                                  (request.method, e))

    return jsonify(rc.toDict())
Example #26
0
    def _verify_database(self):
        if not self:
            return

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

            if not results:
                self.cursor.execute("CREATE DATABASE ambassador")
        except pg8000.Error as e:
            self.status = RichStatus.fromError("no ambassador database: %s" %
                                               e)
Example #27
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)
Example #28
0
    def fetch_module(self, name):
        if not self:
            return self.status

        try:
            self.cursor.execute(
                "SELECT config FROM modules WHERE name = :name", locals())

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

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

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

            config = self.cursor.fetchone()

            return RichStatus.OK(name=name, config=config)
        except pg8000.Error as e:
            return RichStatus.fromError(
                "fetch_module %s: could not fetch info: %s" % (name, e))
Example #29
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))
Example #30
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))