aboutsummaryrefslogtreecommitdiff
path: root/bbot/agent
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2021-03-16 20:21:59 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-03-26 20:00:31 +0300
commit2af2c4f092aa7efffe839ec615c06d22cf43cc3b (patch)
treee84676dbf273602fdf1f3541171df9dad7060daf /bbot/agent
parent392c6003321047421467e07eac31e12875377ead (diff)
Add support for interactive builds
Diffstat (limited to 'bbot/agent')
-rw-r--r--bbot/agent/agent.cli18
-rw-r--r--bbot/agent/agent.cxx80
-rw-r--r--bbot/agent/agent.hxx1
-rw-r--r--bbot/agent/machine.cxx39
-rw-r--r--bbot/agent/machine.hxx9
5 files changed, 130 insertions, 17 deletions
diff --git a/bbot/agent/agent.cli b/bbot/agent/agent.cli
index 5d6cc9d..b50a43a 100644
--- a/bbot/agent/agent.cli
+++ b/bbot/agent/agent.cli
@@ -1,6 +1,8 @@
// file : bbot/agent.cli
// license : TBC; see accompanying LICENSE file
+include <libbbot/manifest.hxx>;
+
include <bbot/common.cli>;
"\section=1"
@@ -78,6 +80,15 @@ namespace bbot
testing)."
}
+ interactive_mode --interactive = interactive_mode::false_
+ {
+ "<mode>",
+ "Interactive build support. Valid values for this option are \cb{false}
+ (only non-interactive), \cb{true} (only interactive), and \cb{both}.
+ If this option is not specified, then only non-interactive builds
+ are supported."
+ }
+
// We reserve 0 in case in the future we want to distinguish a single-
// instance mode or some such.
//
@@ -182,6 +193,13 @@ namespace bbot
default."
}
+ size_t --intactive-timeout = 10800
+ {
+ "<sec>",
+ "Maximum number of seconds to wait for interactive build completion,
+ 10800 (3 hours) by default."
+ }
+
size_t --connect-timeout = 60
{
"<sec>",
diff --git a/bbot/agent/agent.cxx b/bbot/agent/agent.cxx
index 6c2e0d9..d85ecf5 100644
--- a/bbot/agent/agent.cxx
+++ b/bbot/agent/agent.cxx
@@ -8,6 +8,7 @@
#include <signal.h> // signal()
#include <stdlib.h> // rand_r()
#include <unistd.h> // sleep(), getuid(), fsync(), [f]stat()
+#include <ifaddrs.h> // getifaddrs(), freeifaddrs()
#include <sys/types.h> // stat
#include <sys/stat.h> // [f]stat()
#include <sys/file.h> // flock()
@@ -62,6 +63,7 @@ namespace bbot
uint16_t offset;
string hname;
+ string hip;
uid_t uid;
string uname;
}
@@ -179,7 +181,8 @@ bootstrap_machine (const dir_path& md,
mm,
obmm ? obmm->machine.mac : nullopt,
ops.bridge (),
- tftpd.port ()));
+ tftpd.port (),
+ false /* pub_vnc */));
{
// If we are terminating with an exception then force the machine down.
@@ -888,7 +891,8 @@ try
mm.machine,
mm.machine.mac,
ops.bridge (),
- tftpd.port ()));
+ tftpd.port (),
+ tm.interactive.has_value ()));
// Note: the machine handling logic is similar to bootstrap.
//
@@ -947,7 +951,9 @@ try
//
size_t to;
const size_t startup_to (120);
- const size_t build_to (ops.build_timeout ());
+ const size_t build_to (tm.interactive
+ ? ops.intactive_timeout ()
+ : ops.build_timeout ());
// Wait periodically making sure the machine is still alive.
//
@@ -1093,6 +1099,8 @@ try
uid = getuid ();
uname = getpwuid (uid)->pw_name;
+ // Obtain our hostname.
+ //
{
char buf[HOST_NAME_MAX + 1];
@@ -1103,6 +1111,44 @@ try
hname = buf;
}
+ // Obtain our IP address as a first discovered non-loopback IPv4 address.
+ //
+ // Note: Linux-specific implementation.
+ //
+ {
+ ifaddrs* i;
+ if (getifaddrs (&i) == -1)
+ fail << "unable to obtain IP addresses: "
+ << system_error (errno, std::generic_category ()); // Sanitize.
+
+ unique_ptr<ifaddrs, void (*)(ifaddrs*)> deleter (i, freeifaddrs);
+
+ for (; i != nullptr; i = i->ifa_next)
+ {
+ sockaddr* sa (i->ifa_addr);
+
+ if (sa != nullptr && // Configured.
+ (i->ifa_flags & IFF_LOOPBACK) == 0 && // Not a loopback interface.
+ (i->ifa_flags & IFF_UP) != 0 && // Up.
+ sa->sa_family == AF_INET) // Ignore IPv6 for now.
+ {
+ char buf[INET_ADDRSTRLEN]; // IPv4 address.
+ if (inet_ntop (AF_INET,
+ &reinterpret_cast<sockaddr_in*> (sa)->sin_addr,
+ buf,
+ sizeof (buf)) == nullptr)
+ fail << "unable to obtain IPv4 address: "
+ << system_error (errno, std::generic_category ()); // Sanitize.
+
+ hip = buf;
+ break;
+ }
+ }
+
+ if (hip.empty ())
+ fail << "no IPv4 address configured";
+ }
+
// On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if
// the pipe reading end is closed. Note that by default this signal
// terminates a process. Also note that there is no way to disable this
@@ -1246,6 +1292,15 @@ try
return std::uniform_int_distribution<unsigned int> (50, 60) (g);
};
+ optional<interactive_mode> imode;
+ optional<string> ilogin;
+
+ if (ops.interactive () != interactive_mode::false_)
+ {
+ imode = ops.interactive ();
+ ilogin = machine_vnc (true /* public */);
+ }
+
for (unsigned int sleep (0);; ::sleep (sleep), sleep = 0)
{
bootstrapped_machines ms (enumerate_machines (ops.machines ()));
@@ -1256,6 +1311,8 @@ try
hname,
tc_name,
tc_ver,
+ imode,
+ ilogin,
fingerprint,
machine_header_manifests {}
};
@@ -1379,6 +1436,19 @@ try
continue;
}
+ // Make sure that the task interactivity matches the requested mode.
+ //
+ if (( t.interactive && !imode) ||
+ (!t.interactive && imode && *imode == interactive_mode::true_))
+ {
+ if (t.interactive)
+ error << "interactive task from " << u << ": " << *t.interactive;
+ else
+ error << "non-interactive task from " << u;
+
+ continue;
+ }
+
l2 ([&]{trace << "task for " << t.name << '/' << t.version << " "
<< "on " << t.machine << " "
<< "from " << u;});
@@ -1558,7 +1628,7 @@ namespace bbot
iface_addr (const string& i)
{
if (i.size () >= IFNAMSIZ)
- throw invalid_argument ("interface nama too long");
+ throw invalid_argument ("interface name too long");
auto_fd fd (socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
@@ -1572,7 +1642,7 @@ namespace bbot
if (ioctl (fd.get (), SIOCGIFADDR, &ifr) == -1)
throw_system_error (errno);
- char buf[3 * 4 + 3 + 1]; // IPv4 address.
+ char buf[INET_ADDRSTRLEN]; // IPv4 address.
if (inet_ntop (AF_INET,
&reinterpret_cast<sockaddr_in*> (&ifr.ifr_addr)->sin_addr,
buf,
diff --git a/bbot/agent/agent.hxx b/bbot/agent/agent.hxx
index 93c4b56..ba3719e 100644
--- a/bbot/agent/agent.hxx
+++ b/bbot/agent/agent.hxx
@@ -25,6 +25,7 @@ namespace bbot
extern uint16_t inst; // Instance number.
extern string hname; // Our host name.
+ extern string hip; // Our IP address.
extern uid_t uid; // Our effective user id.
extern string uname; // Our effective user name.
diff --git a/bbot/agent/machine.cxx b/bbot/agent/machine.cxx
index c884f8c..3768971 100644
--- a/bbot/agent/machine.cxx
+++ b/bbot/agent/machine.cxx
@@ -171,7 +171,8 @@ namespace bbot
const machine_manifest&,
const optional<string>& mac,
const string& br_iface,
- uint16_t tftp_port);
+ uint16_t tftp_port,
+ bool pub_vnc);
virtual bool
shutdown (size_t& seconds) override;
@@ -214,18 +215,14 @@ namespace bbot
const machine_manifest& mm,
const optional<string>& omac,
const string& br,
- uint16_t port)
+ uint16_t port,
+ bool pub_vnc)
: machine (mm.mac ? *mm.mac : // Fixed mac from machine manifest.
omac ? *omac : // Generated mac from previous bootstrap.
generate_mac ()),
kvm ("kvm"),
net (br, port),
- //
- // QEMU's -vnc option (see below) expects the port offset from 5900
- // rather than the absolute value. The low 5901+, 6001+, and 6101+
- // ports all look good collision-wise with anything useful.
- //
- vnc ("127.0.0.1:" + to_string (5900 + offset)),
+ vnc (machine_vnc (pub_vnc)),
monitor ("/tmp/monitor-" + tc_name + '-' + to_string (inst))
{
tracer trace ("kvm_machine", md.string ().c_str ());
@@ -402,7 +399,15 @@ namespace bbot
// VNC.
//
- "-vnc", "127.0.0.1:" + to_string (offset), // 5900 + offset
+ // We listen on all IPs for a public VNC session and only on localhost
+ // for private.
+ //
+ // QEMU's -vnc option expects the port offset from 5900 rather than the
+ // absolute value. The low 5901+, 6001+, and 6101+ ports all look good
+ // collision-wise with anything useful.
+ //
+ "-vnc",
+ (pub_vnc ? ":" : "127.0.0.1:") + to_string (offset), // 5900 + offset
// QMP.
//
@@ -636,16 +641,28 @@ namespace bbot
const machine_manifest& mm,
const optional<string>& mac,
const string& br_iface,
- uint16_t tftp_port)
+ uint16_t tftp_port,
+ bool pub_vnc)
{
switch (mm.type)
{
case machine_type::kvm:
- return make_unique<kvm_machine> (md, mm, mac, br_iface, tftp_port);
+ return make_unique<kvm_machine> (
+ md, mm, mac, br_iface, tftp_port, pub_vnc);
+
case machine_type::nspawn:
assert (false); //@@ TODO
}
return nullptr;
}
+
+ string
+ machine_vnc (bool pub)
+ {
+ string r (pub ? hip : "127.0.0.1");
+ r += ':';
+ r += to_string (5900 + offset);
+ return r;
+ }
}
diff --git a/bbot/agent/machine.hxx b/bbot/agent/machine.hxx
index 44c7480..9a47d12 100644
--- a/bbot/agent/machine.hxx
+++ b/bbot/agent/machine.hxx
@@ -83,7 +83,14 @@ namespace bbot
const machine_manifest&,
const optional<string>& mac,
const string& br_iface,
- uint16_t tftp_port);
+ uint16_t tftp_port,
+ bool pub_vnc);
+
+ // Return the machine's public or private VNC session endpoint in the
+ // '<ip>:<port>' form.
+ //
+ string
+ machine_vnc (bool pub_vnc);
}
#endif // BBOT_AGENT_MACHINE_HXX