Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check the status of landing jobs #9

Merged
merged 2 commits into from
Jun 23, 2017

Conversation

zalun
Copy link
Contributor

@zalun zalun commented Jun 14, 2017

No description provided.

@zalun zalun force-pushed the check_land_job_state branch from da561ec to 5990c01 Compare June 14, 2017 14:31
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Revision API
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transplant API :)

return transplant.json(), 200


def _format_revision(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed

@zalun zalun force-pushed the check_land_job_state branch 6 times, most recently from f76529e to 6013235 Compare June 16, 2017 12:11
@@ -18,6 +18,7 @@ services:
- PHABRICATOR_URL=https://mozphab.dev.mozaws.net
- PHABRICATOR_UNPRIVILEGED_API_KEY=api-123456789
- TRANSPLANT_URL=https://stub.transplant.example.com
- DATABASE_URL=sqlite:////sqlite.db
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really an issue now, but, at some point we should start using postgres in the development environment since that what will be used in production.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed



def get(revision_id, api_key=None):
""" API endpoint at /revisions/{id} to get revision data. """
def get_revision(revision_id, api_key=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the idea behind this method, but, in my view it would be better to keep this file to 2 things: 1) the api functions for revisions, 2) helper methods for only those api functions.

If the transplants.py needs to get revisions, create a _get_revision method within that file specific to its needs.


return _format_revision(phab, revision, include_parents=True), 200
return _format_revision(phab, revision, include_parents=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do make a helper method for transplants.py remember that setting include_parents=True is very expensive because it will make several network requests. In fact, if you only need to get the revision id, then you won't need to call_format_revision at all.

revision = phab.get_revision(id=revision_id)
def get(revision_id, api_key=None):
""" API endpoint at /revisions/{id} to get revision data. """
revision = get_revision(revision_id, api_key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert this and have transplants.py use its own private helper method.

landoapi/app.py Outdated
return app


def create_tables():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is never used (?)



def check_status(transplant_id):
""" API endpoint at /revisions/{id}/transplants to list landing jobs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 things.

This comment doesn't reflect what this function does. The comment says it lists all landing jobs for a revision, but, infact it only retrieves one landing job based on a given id.

Like I mentioned in the last PR. Having an endpoint to get an individual transplant job based on its id will serve no purpose. This is because external services (e.g. Lando UI) should not (or in the case of Lando UI literally cannot) save the transplant ids. The only way for them to query the status of a landing is by revision id.

I propose these endpoints
/revisions/{id}/land - This lands a revision like you have already implemented. The change here is to remove transplants from the url because it doesn't really matter for external clients to know the specifics.

/revisions/{id}/landings - For each transplant job on that revision returns: ldap username of person that started it, date started, date finished, landing status and target repo.

This is just a proposal, but, I think we should have a discussion about it @zalun & @mars-f.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop the "land" endpoint and just POST directly to /revision/{id}/landings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

•smacleod> mars (/whoever is touching transplant): please see https://docs.google.com/document/d/1cRIdpVqYcn0Mle7vyjJePNw5TcFoZ20N42dLVy5xVEk/edit#heading=h.lteitxoe85mb
2:26:04 PM mars: as someone who has been bit by nesting api endpoints, I highly advise against it (CC imadueme)
2:26:14 PM (re the landings stuff above)
2:26:56 PM e.g. go with /landings/?revision=<blah> vs /revisions/<blah>/landings
2:27:10 PM 
<mars> smacleod: so you would rather have us post to /revision/{id}/landings, and that takes you to /landings/{landing_id} ?
2:27:13 PM 
<•smacleod> keep your ids global, endpoints flat, with query arguments to filter

smacleod proposes that we do not use nested api endpoints.

Copy link
Contributor

@purelogiq purelogiq Jun 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get revision data
/revisions 
	?=id
	?=api-key
	
Land a revision
/revisions/land
	?=revision_id
	?=api-key
	
Return all the current and past landings for a revision
/landings
	?=revision_id
	?=api-key

I propose this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revised:

All endpoints can have an optional Phabricator API Key in Request Header (which will be used when working with secure revisions.)

Get revision data
GET /revisions 
	?=id
	
Return all the current and past landings for a revision
GET /landings
	?=revision_id
	?=filter=(all,inprogress,failed,etc)
	
Land a revision
POST /landings
	?=revision_id

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revised 😤 😃

All endpoints can have an optional Phabricator API Key in Request Header (which will be used when working with secure revisions.)

Get revision data
GET /revisions/{id}
	
Return all the current and past landings for a revision
GET /landings
	?=revision_id
	?=status=(all,inprogress,failed,etc)
	
Land a revision
POST /landings
    Body:
	    revision_id

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id = db.Column(db.Integer, primary_key=True)
request_id = db.Column(db.Integer, unique=True)
revision_id = db.Column(db.String(30))
status = db.Column(db.Integer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I feel using a string here would be much better, e.g 'started', and 'finished'.
In the API it is not clear what 0 or 2 means without looking at the source code, for an external client that is very confusing. In addition if we ever have to debug the database it is nice to see text of the statuses instead of a number.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - I was also thinking that it should be changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the job statuses I suggest we copy the taskcluster task statuses. "pending", "running", and "completed", "failed", and possibly "exception".

@@ -3,8 +3,12 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from urllib.parse import parse_qs

import json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The imports here on line 6, 9, and 11 are never used (?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - leftover

class TestTransplants:
def setup_method(self):
conn = _app.create_app('/version.json')
with conn.app.app_context():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think this is ok, or at least not something we have to worry about for now. Hopefully we find a solution soon :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, I've been also asking around and people are saying it's OK

@purelogiq
Copy link
Contributor

I also tested this in the browser and it worked! So I think we're on the right track.

@zalun zalun force-pushed the check_land_job_state branch from 6013235 to 835f35f Compare June 20, 2017 16:20
TransplantClient is created to handle the requests. It is using
request-mock module. After connecting to the real server only this class and
tests should be changed. POST request to
`/requests/{request_id}/transplants` is calling creating an autoland
job using the data taken from Phabricator.
@zalun zalun force-pushed the check_land_job_state branch 2 times, most recently from 253bdc5 to 41a5363 Compare June 21, 2017 14:15
@zalun zalun changed the title WIP Check land job state Check the status of landing jobs Jun 21, 2017
@zalun zalun force-pushed the check_land_job_state branch from 41a5363 to 536e433 Compare June 21, 2017 14:18
Copy link
Contributor

@purelogiq purelogiq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, but looks good to land to me :)

}


class Landing(db.Model):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should store the ldap_username, date_started, and date_landed. We can make another card for it so that we can land this one quicker.

request_id:
type: integer
description: |
The id of the request in Transplant service
The id of the Request in Transplant service
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should expose the transplant request_id to the public. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not needed, might be useful for debugging.

Copy link
Contributor

@mars-f mars-f left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure looks good; some fixups requested for style, comments and such.

@@ -4,7 +4,10 @@

FROM python:3.5-alpine

RUN apk --update --no-cache add \
sqlite > /dev/null 2>&1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen the bit about coyping stderr to stdout done in Dockerfiles before, especially for 'apk add'. Is that a best practice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copied blindly from another Dockerfile, I'll remove it

ADD requirements.txt /requirements.txt
ADD docker/db/schema.db /sqlite.db
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to worry about database file permissions? In prod the application server runs under a different user.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not ready for production - I guess it would rather link the directory. Also manager is needed to run create_all on an empty database.

return problem(
502,
'Landing not created',
'The requested revision does exist, but landing failed',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if the error message said something like "Please retry your request at a later time."

from landoapi.phabricator_client import PhabricatorClient
from landoapi.transplant_client import TransplantClient

TRANSPLANT_JOB_STARTED = 'started'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment explaining that these strings are known values copied from the Transplant service API?

""" Land revision and create a Transplant item in storage. """
revision = _get_revision(revision_id, phabricator_api_key)
if not revision:
raise RevisionNotFoundException()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will need the missing Revision's ID in the exception message for debugging.

@classmethod
def get(cls, landing_id):
""" Get Landing object from storage. """
t = cls.query.get(landing_id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, could you please expand this variable name?

return '<Landing: %s>' % self.id

def serialize(self):
""" Serialize """
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring needs more detail. What target format are we serializing into? JSON compatible? sqlite compatible?



class LandingNotCreatedException(Exception):
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a docstring to explain that this exception represents downstream service errors.

pass


class LandingNotFoundException(Exception):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a docstring to explain that this exception represents a specific downstream service response (I think?).

@pytest.mark.usefixtures('client_class')
@pytest.mark.usefixtures('phabfactory_class')
@pytest.mark.usefixtures('docker_env_vars')
class TestLanding:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests should be converted from xUnit-style to pytest-style.

@zalun zalun force-pushed the check_land_job_state branch 2 times, most recently from cea0777 to 599038c Compare June 22, 2017 12:45
Copy link
Contributor

@mars-f mars-f left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just needs some minor tweaks and it can land.

_db.drop_all()


def test_landing_revision(db, client, phabfactory):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test name should be something like test_landing_request_is_saved_to_database().

@@ -7,6 +7,10 @@ FROM python:3.5-alpine
RUN addgroup -g 1001 app && \
adduser -D -u 1001 -G app -s /usr/sbin/nologin app

RUN apk --update --no-cache add \
sqlite > /dev/null 2>&1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2>&1 should be removed to match Dockerfile-dev.


@pytest.fixture
def db(app):
"""Monkeypatch environment variables that we'd get running under docker."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring needs rewording, it doesn't match the fixture's purpose.

with app.app_context():
_db.init_app(app)
_db.create_all()
# Yield the connexion app so other fixtures can get the app instance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment needs removal.

Renamed Transplant to Landing in API, models, db schema.
Current API:
  - /landings
    POST - land the revision
    GET - get a list by revision and/or status
  - /landings/{id}
    GET - get a specific landing

Added `manage.py` file with a `create_db` command to create a database on the
container. It is linked in docker-compose to `./.db` directory.
@zalun zalun force-pushed the check_land_job_state branch from 599038c to fb33fcc Compare June 22, 2017 15:06
@zalun zalun merged commit 6d48c6f into mozilla-conduit:master Jun 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants