<div dir="ltr">Yay, the web side catching up!<br><div><div class="gmail_extra"><br><div class="gmail_quote">On 27 July 2017 at 03:50, Monty Taylor <span dir="ltr"><<a href="mailto:mordred@inaugust.com" target="_blank">mordred@inaugust.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Based on having written the puppet to support the apache proxying to both github/status pages and the console-streaming, I believe we should accelerate moving the functions from the old webapp to the new zuul-web.<br>
<br>
While the apache proxy layer keeps the moves from breaking our end-users, doing the moves will break anyone doing deployments of zuul CD from git unless we tightly coordinate with them. As that's the way we prefer to run normally in Infra, it's reasonable to expect other people to desire running that way as well. While we can, in Infra, be careful when we land changes and then update the puppet url mapping before we restart the services, once there is another deployer it's hard to coordinate. So the web functions in the scheduler should be moved to zuul-web at the very least before we cut v3. I'd prefer to just go ahead and move them before Denver, because I'd rather not do the puppet dance after we go live.<br>
<br>
I've written a first pass at doing this that doesn't move anything but that replaces webob with aiohttp. I did this when jlk was having issues with unicode in the github webhook out of curiosity and it wasn't that bad. I'll push that up in just a sec. Before we get to patches though, I keep saying I'm going to write up thoughts on how this wants to look in the end - so here they are:<br>
<br>
The shape of what things want to look like<br>
------------------------------<wbr>------------<br>
<br>
zuul-web wants to serve all http and websocket content. It's a stateless app, so it can be simply scaled-out and load-balanced as desired.<br>
<br>
At the moment the http content is split between zuul-web and webapp and the urls need to be rewritten/proxied to be contiguous.<br>
<br>
static assets want to be in a '/static' path and served by zuul-web - but all prefixed with '/static' so that it's easy for installations to serve static content directly with apache. The path "static" can certainly be debated - but it's working for now. The main point is that we want a portion of the URL space to be easily servable by a static web server for large installs, but also easily servable directly by zuul-web for small ones.<br></blockquote><div><br></div><div>Not to bikeshed here, but also about being able to push static to a CDN, so yea, just don't bake anything in.<br> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
It needs to be possible to run zuul-web on a sub-url, such as <a href="https://ci.example.com/zuul" rel="noreferrer" target="_blank">https://ci.example.com/zuul</a> - We should endeavor to properly detect this from the appropriate http headers which we are not doing right now, but we should continue to provide a config setting so that admin can set it if for some reason we can't detect it properly from headers.<br>
<br>
REST endpoints fall in to two categories - global and per-tenant. Most urls should be per-tenant, but some, like the github webhook listener, need to be global.<br>
<br>
The url scheme served by zuul-web should be:<br>
<br>
/connection/{connection}<br>
/tenant/{tenant}<br>
<br>
so -<br>
<br>
/connection/github/payload<br>
/tenant/openstack/status<br>
<br>
** Question - should we shift them all down a tick to be under /api or similar?<br>
<br>
The tenant namespace has the follwing endpoints:<br>
<br>
/tenant/{tenant}/console-strea<wbr>m<br>
/tenant/{tenant}/status<br>
/tenant/{tenant/{status}/chang<wbr>e/{change}<br>
/tenant/{tenant}/keys/{source}<wbr>{project}.pub<br>
<br>
It needs to be possible to expose the websocket stream on a different url, such as zuul-web being on <a href="https://ci.example.com/zuul" rel="noreferrer" target="_blank">https://ci.example.com/zuul</a> and websocket being on <a href="https://internal.example.com/zuul/console-stream" rel="noreferrer" target="_blank">https://internal.example.com/z<wbr>uul/console-stream</a>.<br>
<br>
We need to be able to allow an admin to configure an override for the websocket location (this is already done)<br>
<br>
Server-side rendering of html should be avoided, preferring instead serving html, css, images and javascript files from /static and having those fetch information from api endpoints on zuul-web. (more on this later)<br>
<br>
For now the html/javascript web-content is in the zuul repo. Depending on how we wind up feeling about javascript tooling we may want to discuss splitting it into its own repo - but it's not actually necessary to do so. (again, more on javascript tooling later)</blockquote><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Actually moving things from scheduler to zuul-web<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
------------------------------<wbr>-------------------<br>
<br>
- make both a register-per-tenant and a register-global function for registering handlers<br>
<br>
Most places want to register per-tenant and only per-tenant. but the github webhook wants to be global, so we at least need the ability. It's also POSSIBLE an admin might want to make an admin status console. We should not implement that until we have an actual need for it - but having the ability at registration time is important.<br>
<br>
- moving status to zuul-web<br>
<br>
We will need a gearman function on the scheduler for getting the status info. We can just ship the entire status info from the scheduler to zuul-web and do caching/filtering there. Should be fairly trivial.<br>
<br>
- move github webhook to zuul-web<br>
<br>
Will need a gearman function in the github driver - essentially splitting the current webhook handler into the thing that receives the webhook and the thing that processes the webhook. The github plugin code is already arranged that way, so putting a gearman call in the middle should be DEAD simple. It just needs to plop the headers and json it gets from the webhook into the gearman payload. However, this will need:<br>
<br>
- add plugin plumbing to zuul-web<br>
<br>
The github plugin will need to register routes with the zuul-web process and not the scheduler. zuul-web should load drivers and connections (as does the scheduler and merger/executor) and its drivers can register the needed methods. Shouldn't be terrible.<br>
<br>
The github plugin will also need to register a gearman worker function, so we probably want to add a registration function to the connection plugin interface to allow plugins to add worker functions.<br>
<br>
- move keys endpoint to zuul-web<br>
<br>
Same as the other two - gearman function to fetch the key data from the scheduler.<br>
<br>
So in all - this is:<br>
<br>
* convert scheduler/github http things to aiohttp in place<br>
* add zuul-web plugin plumbing<br>
* make three gearman functions<br>
* move the web handlers to zuul-web and have them call the gearman functions<br></blockquote><div><br><div>So in general I like the idea of removing the web interface from zuul-scheduler. Some thoughts.<br><br></div><div>1)
Yes, please namespace tenant apis. At the moment the root folder name
on our APIs are the tenant name and this makes it really easy to clash
with other routes. The obvious one currently being if you ever made a
tenant called connection you're broken. It's an easy case to avoid, but
definitely namespaced.<br><br></div><div>2) Regarding zuul-web and
websocket being the same thing - they really don't have to be. At least
for now the streaming url is stored in config and really only shown on
the status page. The actual URL of this really doesn't matter so long as
it's configured correctly. The question is whether you want to decide
that the streaming URL is part of an API, or just something that renders
HTML. We don't need to mandate the URL of clickable links. I can see an argument that it might be easier to keep the streaming piece self contained. <br><br></div><div>3)
The problem with moving the github driver to zuul-web isn't that clean.
You will always have zuul communicating with github both on incoming
message and then later with status updates and merges etc. At it's most
basic putting this in two places is at least distributing the secrets
into multiple places. <br><br></div><div>If we want to go down this
seperation route i think I would prefer to see a standalone
'zuul-github' application and the driver interface be implemented over
gearman/zk. There's a real complexity concern here in that it's already
difficult to trace what's going on in zuul and that this would
complicate things further. It's also going to take a lot longer.<br><br></div><div>Again the actual url of
/connection/github does not matter, so long as you know what that entry
point is and configure it correctly in github this can be anything and
not something to worry about baking into an API if you can separate drivers.<br></div><div></div><div><br><br></div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Javascript toolchain<br>
--------------------<br>
<br>
tl;dr - we should use yarn and webpack. I've got patches written.<br>
<br>
We're growing web things. In addition to the status page, we now have the console streaming page. We have a desire to add something that knows how to render the job-output.json in some nice ways. And Tristan has written a nice Dashboard for historical data based on the SQL Reporter.<br>
<br>
Basically - there is a Web UI, and it feels like it's time to go ahead and start managing it like an application. (we could even start unit-testing!)<br>
<br>
Considerations for selecting javascript/css management tools:<br>
<br>
* must be easy to understand for zuul devs / not assume deep understanding of the node ecosystem<br>
* must not rely on 'vendoring' libraries into our source trees<br>
* should be friendly for folks working from a distro POV<br>
* should support minification/packing<br>
<br>
>From looking in to things, all of the current/recommended tooling assumes a recent node install- just as pretty much all python things assume recent pip.<br>
<br>
In Infra land we have a python/javascript app already, storyboard.<br>
<br>
storyboard currently uses a combination of bower, npm and grunt. bower is for downloading non-javscript depends, npm for javscript, and grunt for building which includes minification and updating files to know how to find the downloaded dependencies.<br>
<br>
The bower folks now recommending not using bower anymore in favor of yarn. yarn also handles the npm side, so it's no longer bower+npm - it's just yarn.<br>
<br>
We don't have a ton of complex tasks we need to do - which is where things like gulp or grunt come in - we mostly need to take javascript, css and html, combine/minify/transpile and publish. The tool 'webpack' is recommended for this - and has the benefit of an actually legitimately excellent set of intro docs. (I read them in not too much time and actually wrote webpack does WITHOUT just complete copy-pasta)<br>
<br>
yarn provides distro package repos for ubuntu/debian, centos/fedora/rhel, arch, solus and alpine making it pretty easy to get started with for those of us used to that flow. HOWEVER - many modules seem to want node >= 5 and xenial only has 4 - so the apt repo from nodesource also needs to be installed.<br>
<br>
yarn then installs things locally into the source dir based on what's in package.json (and can manage the entries in that file as well), and writes a yarn.lock that can be committed to git. package.json and yarn.lock is similar to rust's cargo.toml and cargo.lock file. package.json records the logical depends - yarn.lock records exact versions including transitive depends so that builds are reproducible without needing to save copies of that code.<br>
<br>
webpack itself is just another depend installed via yarn like any of the rest of the javascript depends.<br>
<br>
webpack handles taking the code yarn downloads and "building" it. it provides support for "import" statements that are supported directly in newer javascript but not older, and it transpiles the code at build time so that the code works in older browsers. This means that one can write code that just does:<br>
<br>
import _ from 'lodash';<br>
<br>
In the javascript. It has similar constructs for css and the images even.<br>
<br>
One of the other nice things is that it includes support for a local dev server with file watches and hot updating of changed code - this means you can load the status page in the dev server, change the css in a source code file and have it immediately show up in the live page without even having to refresh the page.<br>
<br>
Some things require a page refresh - the jquery plugin for status is an example - and when you change those things the page gets refreshed for you.<br>
<br>
These tools are only needed for build/dev tooling, not for running anything. Just like storyboard, the story for production is to produce a tarball of html/javascript artifacts and then upload those to the webserver.<br>
<br>
However, this tooling is still potentially an issue at the distro packaging level. I'm going to go out on a limb (what with me working for a distro and all) and say that the tooling makes things nice enough that we should do this 'right' from a web community POV and figure out the packaging fallout. I've sent an email internally at RH to learn more about what the story is for dealing with Javascript ecosystem things, I'll let folks know what I learn.<br>
<br>
Both are solid and not too hard to understand. I wrote patches supporting both status and streaming - and it honestly made it easier for me to hack on the javascript, test it live and debug issues when they arose. Given that there are also unittesting frameworks- I'm personally completely sold.<br></blockquote><div><br></div><div>I'm entirely sold on putting the javascript/html into a 'real' javascript framework. I've played with a couple and they all work reasonably well with their own quirks, my understanding is that webpack+jest is current flavour of the month. Basically so long as i can basically get the entire dependency chain with an npm install I'm happy. <br><br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
______________________________<wbr>_________________<br>
OpenStack-Infra mailing list<br>
<a href="mailto:OpenStack-Infra@lists.openstack.org" target="_blank">OpenStack-Infra@lists.openstac<wbr>k.org</a><br>
<a href="http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-infra" rel="noreferrer" target="_blank">http://lists.openstack.org/cgi<wbr>-bin/mailman/listinfo/openstac<wbr>k-infra</a></blockquote></div><br></div></div></div>