[haiku-development] Re: Package buildmeister

  • From: "Alexander von Gluck IV" <kallisti5@xxxxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Tue, 21 Jun 2016 13:20:54 +0000

June 20 2016 6:24 PM, "Michael Lotz" <mmlr@xxxxxxxx> wrote:

Hi

This email is rather long and contains mostly technical background and 
opinions about the stated
cons of the buildmaster setup. Feel free to skip it if you're not interested 
in the workings of my
current setup or why I find it sensible.

On 19.06.2016 06:13, Alexander von Gluck IV wrote:

I worked on getting the haikuporter build-master installed in a public VM 
tonight and
ran in to quite a few concerns about it.

Seriously? I mean that is exactly what I've set up and has been running for 
the last couple of
months. I understood the goal was to put it on Haiku infrastructure and make 
it official, not build
it up in private again.

The end goal is making it public infrastructure... however we have yet to get 
hardware, so i'm working
on getting it stood up on my end in a public vm so we understand how to deploy 
it and can document it.
We've been idle on finishing the package building infrastructure... which 
should be something exciting.
I'm trying to figure out why. This is one of the last *big* blockers for R1B1 
and beyond.

The number one issue I saw was the requirement
that each Haiku build-slaves be accessible via IP + SSH

Seriously? It uses SSH. The only dependency here is a TCP connection. TCP can 
be tunnelled and
forwarded at will with whatever tool fits. I personally use reverse SSH 
tunnels to hook up my
builders. But you can use stunnel or just plain netcat as the connection will 
be encrypted anyway.
I find SSL generally to be more of a hassle to set up than SSH, so I just use 
that.

Oh, so the plan for reverse connections is doing ssh tunnels.  If the link gets 
disconnected we
script out a restart... ok.  I guess that'll work... it feels very bubble-gum + 
duct tape though :-)

This is why I couldn't find code around connections coming from the buildslave 
to the build-master.

Given these machines generally run behind user NAT's, and is "single-shot" I 
think the
haikuporter build-master might be *too* simplistic.

The SSH connections are closed when the buildrun is through, yes. That 
doesn't mean that the SSH
servers are suddenly going to disappear and need to be set up again for the 
next buildrun. You make
it sound like there's some kind of manual work involved here, which there 
isn't.

Regarding reverse SSH tunnelling:
I can't find any documentation, arguments, or code on this... it seems this 
should be
the default behavior for the outlines reasons. (Since we need one complete 
haikuporter
build-master per architecture, how would this even work?)

I'm really getting a strange feeling here. You make it sound like I'm hiding 
some dark secret. But
this really is plain basic networking. You need to make a TCP port available 
where an SSH server
can be reached. You can do that through a static IP and a port forward 
through your NAT on your
home router. You can also make that port available by forwarding it through a 
reverse SSH tunnel or
similar setup. This is not something I just invented, this has been used 
since decades.

This wasn't the point.  It means our infrastructure is going to be highly 
dependent on upkeep.
Users managing SSH tunnels, managing reverse proxies through NAT's, dynamic dns 
entries, haiku
buildslaves, haikuports trees.  We collectively can barely find the time to get 
this stuff
set up or handle SMART drive errors on baron... let alone get all this stuff 
configured then
maintain it.

The general approach was to not reinvent stuff. HaikuPorter needs a way to 
remotely execute
commands on the builder. A remote shell like SSH fits that bill, so I used 
that (through paramiko)
to leverage the fact that we ship an SSH server with every Haiku nightly 
ready to use.

Of course you can build remote execution using your own protocol and wrap 
that within SSL to secure
it and implement yet another way to do the same thing. I just didn't find it 
useful to build such a
protocol directly into HaikuPorter as that IMHO would just be bloat that 
would need to be
maintained.

If anyone wants to try and document it let me know and i'll give you access 
to a
buildmaster *and* a remote buildslave.

To make this very obvious, I'm going to insert the full text of everything 
I'm discussing here at
the end of this email.

For the reverse SSH tunnel I'm using a script [1] with a corresponding 
configuration [2]. This
configuration forwards port 22 of my builder (where the local SSH server 
listens) to port 8124 on
the server where the buildmaster is configured to connect to (see the full 
builder configuration in
[3]). It does that with a weak but fast cipher because it is going to only 
provide a tunnel for
another, more strongly encrypted SSH connection. The builder configuration 
was created with the
createbuilder.sh script, which I've committed to the HaikuPorter repository 
under the buildmaster
directory and which automates the configuration. The only thing I had to do 
to set this builder up
was to name the HaikuPorter and HaikuPorts directory, host, port and user for 
the SSH connection
(using localhost:8124 for the forwarded port) and finally add the 
automatically generated SSH
public key to the authorized_keys of the desired user on my builder.

The ssh_tunnel.sh is symlinked in my ~/config/settings/boot/launch so that it 
start automatically
on boot. For obvious reasons I am going to leave out the private key used by 
that configuration.
The tunnel user on the server is set up with git-shell to prevent normal 
shell access. The
authorized_keys file [4] further limits the possible actions to pretty much 
just port forwarding,
which is all that is needed here.

Why didn't I document that as the official way to set up a builder? Because I 
find this to be an
implementation detail that is entirely up to the operator of the server and 
builder. How the
connection is established does not matter to HaikuPorter and the choice of 
infrastructure to make
it happen is a matter of various factors including security and trust 
concerns, available tools and
personal preference. The described setup can be used for pretty much any port 
forwarding need and
is not in any way specific to HaikuPorter (and wasn't written for this use 
case either, it stems
from the setup I've implemented at work to do most of our remote support).

Another person would maybe prefer not to create a user on the server at all 
and configure stunnel
to do SSL tunnels instead. This would work just as well.

Thank for for the details, i'll put this into the documentation.

I think we're all assuming haikuporter build-master is a lot more magic than 
it actually
is. Some great work has been put into it, but I want to make sure there is 
consensus
that haikuporter build-master is the way to go.

Why are you assuming that everyone's assuming magic here? It's pretty far 
away from magic or being
a black box. It's a single source file within HaikuPorter with ~800 lines of 
code [5]. How much
magic can there be?

This is of course not by accident. Indeed the whole point was for it to do as 
little as possible by
leveraging existing tools (like the dependency logic and recipe handling 
inside HaikuPorter itself,
but also regarding protocols for remote command execution (SSH) and file 
transfers (SFTP) as well
as serving out status (apache httpd in my setup)).

I generally find that if something seems like magic one just doesn't yet 
fully understand how it
works.

I agree, and through these conversations it has been made a lot more clear on 
what the design goals
were. 

https://github.com/haikuports/haikuporter
haikuporter build-master mode (mmlr)
Pro
- Python which has good community knowledge
- Fully leverages haikuporter internal logic for dependencies
- Builds repos
Cons
- SSH's out to slaves and requires user to open ssh port per slave. (and 
static ip)

See above.

- Requires haikuporter + haikuports on master and each slave (does 
haikuports have to be in sync?)

Yes, obviously HaikuPorter is needed because in this setup both the 
buildmaster logic and the
builder are implemented in it. The builder uses HaikuPorter in all of these 
setups, so I don't see
why it's listed as a con for this setup.

HaikuPorts is obviously also needed (in all of the setups as well). 
HaikuPorts needs to be in sync
on the buildmaster and the builder. The buildmaster ensures that 
automatically.

Oh, so the buildmaster does git pull's on the buildslave haikuports trees? I 
bring this up because
the other solutions generally manage their own haikuports and haikuporter 
trees.  I just want to make
sure everyone knows this as this will all need to be common knowledge for 
anyone running a package builder.

- Difficult slave configuration + lots of directory settings per slave

The createbuilder.sh script asks you a couple of questions that you have to 
answer. All questions
that can have a sensible default have one. I don't exactly see how this is 
classified as difficult.
It even automates creation of all the necessary SSH keys and queries the 
remote host key for you.
The only directories that you have to specify is where on the builder 
HaikuPorter and the
HaikuPorts tree can be found. All the other directories have defaults that 
you can just accept and
it will work fine.

Right.  It asks a series of questions about where things will be located at on 
build slaves. Once
again, I bring this up because all of the other tools manage a "workspace" and 
just need a base
directory.

- Doesn't know about architectures of buildslaves (one entire environment 
for each arch)

I don't understand? All setups will need builders for the different 
architectures. Conveniently the
fully host independent chroot in this setup will allow you to run builds for 
different
versions/branches on the same builder (as long as that one is reasonably 
compatible) as no system
packages are used at all. So overall builder count should be reduced compared 
to the other setups.

If you mean there's one *entire environment for each arch*, then yes that is 
true. It consists of a
HaikuPorts checkout and a builder configuration.

Once again, so haikuporter build-master assumes all slaves can build the 
architecture specified, so
we'll need one complete environment for each architecture (x86_gcc2, x86, 
x86_64, 2 or 3 arm targets,
powerpc). I bring this up because the other solutions manage multiple builders 
of multiple
architectures.

- *Basic* html report of each single-shot run.

I give up on this point. I've explained my reasoning for a JSON output 
numerous times. Maybe just
think of it as serving out the "database" that the other approaches also have?

I'll digress, just documenting it for comparison :-)

- Single shot for one package (or a bunch? --do-bootstrap seems broken here) 
and deps

You can do a buildrun for a single package, many packages, the packages that 
were affected by
changes to recipes and referenced files, whatever you want. It is just a 
buildrun, what you put
inside is decided by how you start it.

I've taken great care to make sure that this can run as a git hook or by 
comparing different git
revisions by implementing the functionality to derive a set of affected 
recipes from a set of
changed files. This includes things easily missed by a more simple approach 
like a referenced
license or additional file or patches.

So, we have a number of build slaves for an architecture. A git commit comes in 
and a new job
spins up.  We get another commit and another job can start instantly (and not 
use the "busy"
buildslave?)

The buildmaster/buildmaster.sh frontend script automates most of the common 
tasks (including
updating to a new revision and building everything affected by the changed 
files). For reference
I'm inserting the full text of my magic updateloop script in [6]. That's all 
there is to it for
continuous building of changed/new recipes. The script is run in the 
HaikuPorts checkout on the
server and takes everything it needs from there. Setting it up for a 
different branch means: just
checking out that branch.

Fair. I didn't realize (or likely forgot) build-master was completely designed 
to be triggered via git.

I don't understand the remark about --do-bootstrap. None of the setups are 
meant for bootstrapping.
This is for continuous automated package building and publishing.

Ok, so is there a way to build all packages in a shot across multiple 
buildslaves? We'll likely
need to do something like that pre-r1b1
 
- Lots of requirements on build-master system (package, package_repo, haiku 
repo for licenses)

You actually listed all of them, so "lots" might be a bit of an exaggeration. 
The package and
package_repo tools as well as any build_host libraries needed are a byproduct 
of building a
standard Haiku image. You can also just build the two tools individually if 
you don't want to wait
for a whole image to build.

Fair.  But once again, a comparison of the solutions. The other options don't 
require so much manual
script distribution.

That the licenses aren't duplicated as part of HaikuPorts is a bug in our 
setup IMHO. Relying on
the presence of license files in the Haiku package without explicitly 
declaring that or bringing
the license with your recipe is a bad practice IMO.

Yeah, we need a separate license repo or something, no argument there.

- Poor documentation (I've written whats out there now)

I've tried to outline the concepts a couple of times in my emails. In my job 
I am partly a sysadmin
for various servers and set up a lot of machines and services, so obviously 
the tools used here
don't seem strange to me at all. I understand that this doesn't necessarily 
apply to other people.
However I would expect some sort of sysadmin background from a person running 
official Haiku
servers and services as well.

I'm all about microservices, but my main concern is this whole thing sounds
like it is going to be held together via 30 cron jobs, 10 scripts in 
/usr/local/bin,
and a few old men to log in and manually fix stuff every other day.

I wouldn't really say 30 is old and don't see why one would need to tend to 
an automated system
every other day, but the rest sounds about right (maybe lower numbers, say 1 
cron job or git hook
and 3 or 4 scripts chained together).

I'm almost 31 and was included in the old men comment :P

The difference seems to be in the interpretation of whether
this as a good or a bad thing. I find shell scripts, at least reasonably 
structured ones, pretty
obvious. Large do-it-all servers on the other hand can quickly wander in the 
"blackbox/magic"
direction.

That's where we differ. I do DevOps by day and "automate all the things" :-D  
I've used a lot of
build automation tools in production environments, so I might have some unfair 
cynicisms about
this stuff. Jenkins does connect out to buildslaves over ssh, however it also 
handles "setting
up and managing all the things on the slaves".  Windows build slaves in Jenkins 
generally run a
java agent as a system service and connect to the build master.

I also deploy + manage a cloud of servers + storage (6 PiB so far, yay!) in my 
day job and learned
manual steps will kill a huge amount of free time at a large scale. Sure, 1 
cron job, a git hook,
3-5 scripts might work... but then likely a variety of people will need to 
replicate that
configuration (in functional form) to ~6+ other environments (one for each 
architecture). The
chances of things being 'different' and non-functional are quite high.

In my opinion this is still just a very modular and flexible setup that can 
easily be hooked into,
just as I personally would expect from such a system.

I do better understand where you're coming from now with this stuff.  Given a 
lot of work has gone
into the other two build systems as well, it wouldn't be fair to jump to yours 
without the same
level of analysis we (+I) gave the other two.

I want what everyone else wants:

 * A release as soon as possible
 * Recent + quality package builds
 * Haiku to not become stagnant.

 -- Alex


[1] - ssh_tunnel.sh

#!/bin/bash

cd "$(dirname "$0")"

exec 1>> ssh_tunnel.log 2>&1

function log {
echo "$(date +%Y%m%d_%H%M) $1"
}

log "Starting SSH tunnel loop from $0 in $(pwd)"

while true
do
log "Starting SSH process"
ssh -nNT -F ssh_tunnel.config ssh_tunnel
log "SSH process quit with status $?"
sleep 5
done

[2] - ssh_tunnel.config

Host ssh_tunnel
HostName hpkg.mlotz.ch
User tunnel
BatchMode yes
ConnectionAttempts 3
ConnectTimeout 15
ExitOnForwardFailure yes
IdentityFile ssh_tunnel.key
IdentitiesOnly yes
UserKnownHostsFile ssh_tunnel.hostkey
LogLevel VERBOSE
Protocol 2
RemoteForward 8124 localhost:22
ServerAliveInterval 15
ServerAliveCountMax 3
Cipher arcfour

[3] - mmlr_htpc_x86_gcc2.json
{
"name": "mmlr_htpc_x86_gcc2",
"ssh": {
"host": "localhost",
"port": "8124",
"user": "mmlr",
"privateKeyFile": "keydir/mmlr_htpc_x86_gcc2.key",
"hostKeyFile": "keydir/mmlr_htpc_x86_gcc2.hostkey"
},
"portstree": {
"path": "/Media/Source/builder/x86_gcc2",
"packagesPath": "/Media/Source/builder/x86_gcc2/packages",
"packagesCachePath": "/Media/Source/builder/x86_gcc2/packages/.cache"
},
"haikuporter": {
"path": "/Media/Source/builder/haikuporter/haikuporter",
"args": "-j2"
}
}

[4] - authorized_keys of the tunnel user on the server
no-pty,no-X11-forwarding,permitopen=":1",command="/bin/echo tunnelonly" 
ssh-rsa
AAAA...publickey...== mmlr_htpc_x86_gcc2

[5] - 
https://github.com/haikuports/haikuporter/blob/master/HaikuPorter/BuildMaster.py

[6] - updateloop.sh
#!/bin/sh
while true
do
date -u
~/haikuporter/buildmaster/buildmaster.sh update \
&& ~/haikuporter/buildmaster/createrepo.sh
sleep 180
done >> update.log 2>&1

Other related posts: