diff --git a/fedmsg.d/rida.py b/fedmsg.d/rida.py new file mode 100644 index 00000000..da06b44b --- /dev/null +++ b/fedmsg.d/rida.py @@ -0,0 +1,11 @@ +import socket +hostname = socket.gethostname().split('.')[0] + +config = { + # Just enough fedmsg config to start publishing... + "endpoints": { + "rida.%s" % hostname: [ + "tcp://127.0.0.1:300%i" % i for i in range(10) + ], + }, +} diff --git a/rida.py b/rida.py index e35232bd..a542d81d 100755 --- a/rida.py +++ b/rida.py @@ -36,7 +36,7 @@ This is the implementation of the orchestrator's public RESTful API. # TODO: Emit messages about module submission. from flask import Flask, request -from rida import config, database +from rida import config, database, messaging import json import modulemd @@ -86,8 +86,19 @@ def submit_build(): module.state = "wait" db.session.add(module) db.session.commit() + + # Publish to whatever bus we're configured to connect to. + # This should notify ridad to start doing the work we just scheduled. + messaging.publish( + modname='rida', + topic='module.state.change', + msg=module.json(), + backend=conf.messaging, + ) + # XXX: Okay, we're pretending here... - return json.dumps({"id": module.id}), 201 + return json.dumps(module.json()), 201 + @app.route("/rida/module-builds/", methods=["GET"]) def query_builds(): @@ -95,6 +106,7 @@ def query_builds(): return json.dumps([{"id": x.id, "state": x.state} for x in db.session.query(database.Module).all()]), 200 + @app.route("/rida/module-builds/", methods=["GET"]) def query_build(id): """Lists details for the specified module builds.""" diff --git a/rida/database.py b/rida/database.py index c73f0358..34a30f97 100644 --- a/rida/database.py +++ b/rida/database.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- - - # Copyright (c) 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -22,14 +20,23 @@ # SOFTWARE. # # Written by Petr Šabata +# Ralph Bean """Database handler functions.""" -from sqlalchemy import Column, Integer, String, create_engine -from sqlalchemy.orm import sessionmaker +from sqlalchemy import Column, Integer, String, ForeignKey, create_engine +from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.ext.declarative import declarative_base -Base = declarative_base() + +class RidaBase(object): + # TODO -- we can implement functionality here common to all our model + # classes. + pass + + +Base = declarative_base(cls=RidaBase) + class Database(object): """Class for handling database connections.""" @@ -50,21 +57,48 @@ class Database(object): class Module(Base): __tablename__ = "modules" id = Column(Integer, primary_key=True) - name = Column(String) - version = Column(String) - release = Column(String) + name = Column(String, nullable=False) + version = Column(String, nullable=False) + release = Column(String, nullable=False) # XXX: Consider making this a proper ENUM - state = Column(String) - modulemd = Column(String) + state = Column(String, nullable=False) + modulemd = Column(String, nullable=False) + + def json(self): + return { + 'id': self.id, + 'name': self.name, + 'version': self.version, + 'release': self.release, + 'state': self.state, + + # This is too spammy.. + #'modulemd': self.modulemd, + + # TODO, show their entire .json() ? + 'builds': [build.id for build in self.builds], + } + class Build(Base): __tablename__ = "builds" id = Column(Integer, primary_key=True) - # XXX: Consider making this a proper foreign key - module_id = Column(Integer) - package = Column(String) + package = Column(String, nullable=False) # XXX: Consider making this a proper ENUM - format = Column(String) + format = Column(String, nullable=False) task = Column(Integer) - # XXX: Consider making this a proper ENUM + # XXX: Consider making this a proper ENUM (or an int) state = Column(String) + + module_id = Column(Integer, ForeignKey('modules.id'), nullable=False) + module = relationship('Module', backref='builds', lazy=False) + + def json(self): + return { + 'id': self.id, + 'package': self.package, + 'format': self.format, + 'task': self.task, + 'state': self.state, + 'module': self.module.id, + } diff --git a/rida/messaging.py b/rida/messaging.py index d473a3b0..5b31b1ec 100644 --- a/rida/messaging.py +++ b/rida/messaging.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- - - # Copyright (c) 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,6 +19,27 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # -# Written by Petr Šabata +# Written by Ralph Bean """Generic messaging functions.""" + + +def publish(topic, msg, backend, modname='rida'): + try: + handler = _messaging_backends[backend]['publish'] + except KeyError: + raise KeyError("No messaging backend found for %r" % backend) + return handler(topic, msg, modname=modname) + + +def _fedmsg_publish(topic, msg, modname): + import fedmsg + return fedmsg.publish(topic=topic, msg=msg, modname=modname) + + +_messaging_backends = { + 'fedmsg': { + 'publish': _fedmsg_publish, + #'listen': _fedmsg_listen, # For later... + }, +} diff --git a/schema.sql b/schema.sql deleted file mode 100644 index eb1dd111..00000000 --- a/schema.sql +++ /dev/null @@ -1,18 +0,0 @@ -CREATE TABLE IF NOT EXISTS modules ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, -- e.g. foo, module name - version TEXT NOT NULL, -- e.g. 1.23, module version - release TEXT NOT NULL, -- e.g. 4, module release - state TEXT NOT NULL, -- init, wait, build, done, failed, ready - modulemd TEXT NOT NULL -- the entire modulemd file -); - -CREATE TABLE IF NOT EXISTS builds ( - id INTEGER PRIMARY KEY, - module_id INTEGER NOT NULL, - package TEXT NOT NULL, -- e.g. bar, SRPM name - format TEXT NOT NULL, -- rpm - task INTEGER NOT NULL, -- koji task id - state TEXT NOT NULL, -- koji build states - open, closed, failed - FOREIGN KEY(module_id) REFERENCES modules(id) -);