diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2021-03-16 20:21:59 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2021-03-26 20:00:31 +0300 |
commit | 2af2c4f092aa7efffe839ec615c06d22cf43cc3b (patch) | |
tree | e84676dbf273602fdf1f3541171df9dad7060daf /bbot/agent | |
parent | 392c6003321047421467e07eac31e12875377ead (diff) |
Add support for interactive builds
Diffstat (limited to 'bbot/agent')
-rw-r--r-- | bbot/agent/agent.cli | 18 | ||||
-rw-r--r-- | bbot/agent/agent.cxx | 80 | ||||
-rw-r--r-- | bbot/agent/agent.hxx | 1 | ||||
-rw-r--r-- | bbot/agent/machine.cxx | 39 | ||||
-rw-r--r-- | bbot/agent/machine.hxx | 9 |
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 |