From 031d70e5009fcce58afabc9113d5a5de4a16b19a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 1 May 2017 17:35:30 +0200 Subject: [PATCH] Support arbitrary store URIs in nix.machines For backwards compatibility, if the URI is just a hostname, ssh:// (i.e. LegacySSHStore) is prepended automatically. Also, all fields except the URI are now optional. For example, this is a valid nix.machines file: local?root=/tmp/nix This is useful for testing the remote build machinery since you don't have to mess around with ssh. --- src/build-remote/build-remote.cc | 77 ++++++++++++++++++-------------- src/libstore/build.cc | 1 + src/libstore/store-api.cc | 11 ++--- src/libstore/store-api.hh | 23 +++++++--- 4 files changed, 66 insertions(+), 46 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index c41383bcf..a19dac241 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -27,12 +27,12 @@ class Machine { const std::set mandatoryFeatures; public: - const string hostName; + const string storeUri; const std::vector systemTypes; const string sshKey; const unsigned int maxJobs; const unsigned int speedFactor; - bool enabled; + bool enabled = true; bool allSupported(const std::set & features) const { return std::all_of(features.begin(), features.end(), @@ -49,7 +49,7 @@ public: }); } - Machine(decltype(hostName) hostName, + Machine(decltype(storeUri) storeUri, decltype(systemTypes) systemTypes, decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, @@ -58,14 +58,18 @@ public: decltype(mandatoryFeatures) mandatoryFeatures) : supportedFeatures(supportedFeatures), mandatoryFeatures(mandatoryFeatures), - hostName(hostName), + storeUri( + // Backwards compatibility: if the URI is a hostname, + // prepend ssh://. + storeUri.find("://") != std::string::npos || hasPrefix(storeUri, "local") || hasPrefix(storeUri, "remote") || hasPrefix(storeUri, "auto") + ? storeUri + : "ssh://" + storeUri), systemTypes(systemTypes), sshKey(sshKey), maxJobs(maxJobs), - speedFactor(std::max(1U, speedFactor)), - enabled(true) - {}; -};; + speedFactor(std::max(1U, speedFactor)) + {} +}; static std::vector readConf() { @@ -87,13 +91,13 @@ static std::vector readConf() } auto tokens = tokenizeString>(line); auto sz = tokens.size(); - if (sz < 4) + if (sz < 1) throw FormatError("bad machines.conf file ‘%1%’", conf); machines.emplace_back(tokens[0], - tokenizeString>(tokens[1], ","), - tokens[2], - stoull(tokens[3]), - sz >= 5 ? stoull(tokens[4]) : 1LL, + sz >= 2 ? tokenizeString>(tokens[1], ",") : std::vector{settings.thisSystem}, + sz >= 3 ? tokens[2] : "", + sz >= 4 ? std::stoull(tokens[3]) : 1LL, + sz >= 5 ? std::stoull(tokens[4]) : 1LL, sz >= 6 ? tokenizeString>(tokens[5], ",") : std::set{}, @@ -104,31 +108,27 @@ static std::vector readConf() return machines; } +std::string escapeUri(std::string uri) +{ + std::replace(uri.begin(), uri.end(), '/', '_'); + return uri; +} + static string currentLoad; static AutoCloseFD openSlotLock(const Machine & m, unsigned long long slot) { - std::ostringstream fn_stream(currentLoad, std::ios_base::ate | std::ios_base::out); - fn_stream << "/"; - for (auto t : m.systemTypes) { - fn_stream << t << "-"; - } - fn_stream << m.hostName << "-" << slot; - return openLockFile(fn_stream.str(), true); + return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true); } -static char display_env[] = "DISPLAY="; -static char ssh_env[] = "SSH_ASKPASS="; - int main (int argc, char * * argv) { return handleExceptions(argv[0], [&]() { initNix(); /* Ensure we don't get any SSH passphrase or host key popups. */ - if (putenv(display_env) == -1 || - putenv(ssh_env) == -1) - throw SysError("setting SSH env vars"); + unsetenv("DISPLAY"); + unsetenv("SSH_ASKPASS"); if (argc != 5) throw UsageError("called without required arguments"); @@ -151,7 +151,7 @@ int main (int argc, char * * argv) debug("got %d remote builders", machines.size()); string drvPath; - string hostName; + string storeUri; for (string line; getline(cin, line);) { auto tokens = tokenizeString>(line); auto sz = tokens.size(); @@ -178,6 +178,8 @@ int main (int argc, char * * argv) Machine * bestMachine = nullptr; unsigned long long bestLoad = 0; for (auto & m : machines) { + debug("considering building on ‘%s’", m.storeUri); + if (m.enabled && std::find(m.systemTypes.begin(), m.systemTypes.end(), neededSystem) != m.systemTypes.end() && @@ -238,16 +240,21 @@ int main (int argc, char * * argv) lock = -1; try { - sshStore = openStore("ssh-ng://" + bestMachine->hostName, - { {"ssh-key", bestMachine->sshKey }, - {"max-connections", "1" } }); - hostName = bestMachine->hostName; + + Store::Params storeParams{{"max-connections", "1"}}; + if (bestMachine->sshKey != "") + storeParams["ssh-key"] = bestMachine->sshKey; + + sshStore = openStore(bestMachine->storeUri, storeParams); + storeUri = bestMachine->storeUri; + } catch (std::exception & e) { printError("unable to open SSH connection to ‘%s’: %s; trying other available machines...", - bestMachine->hostName, e.what()); + bestMachine->storeUri, e.what()); bestMachine->enabled = false; continue; } + goto connected; } } @@ -257,11 +264,15 @@ connected: string line; if (!getline(cin, line)) throw Error("hook caller didn't send inputs"); + auto inputs = tokenizeString(line); if (!getline(cin, line)) throw Error("hook caller didn't send outputs"); + auto outputs = tokenizeString(line); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + hostName + ".upload-lock", true); + + AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true); + auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); if (!lockFile(uploadLock.get(), ltWrite, true)) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index ec3684632..a9649ea37 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1862,6 +1862,7 @@ void DerivationGoal::startBuilder() dirsInChroot[i] = r; else { Path p = chrootRootDir + i; + debug("linking ‘%1%’ to ‘%2%’", p, r); if (link(r.c_str(), p.c_str()) == -1) { /* Hard-linking fails if we exceed the maximum link count on a file (e.g. 32000 of ext3), diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 850ea211d..75de4c933 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -709,10 +709,11 @@ namespace nix { RegisterStoreImplementation::Implementations * RegisterStoreImplementation::implementations = 0; -ref openStore(const std::string & uri_) +ref openStore(const std::string & uri_, + const Store::Params & extraParams) { auto uri(uri_); - Store::Params params; + Store::Params params(extraParams); auto q = uri.find('?'); if (q != std::string::npos) { for (auto s : tokenizeString(uri.substr(q + 1), "&")) { @@ -722,11 +723,7 @@ ref openStore(const std::string & uri_) } uri = uri_.substr(0, q); } - return openStore(uri, params); -} -ref openStore(const std::string & uri, const Store::Params & params) -{ for (auto fun : *RegisterStoreImplementation::implementations) { auto store = fun(uri, params); if (store) { @@ -735,7 +732,7 @@ ref openStore(const std::string & uri, const Store::Params & params) } } - throw Error(format("don't know how to open Nix store ‘%s’") % uri); + throw Error("don't know how to open Nix store ‘%s’", uri); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index b763849ad..2388558b3 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -668,20 +668,31 @@ void removeTempRoots(); /* Return a Store object to access the Nix store denoted by ‘uri’ (slight misnomer...). Supported values are: - * ‘direct’: The Nix store in /nix/store and database in + * ‘local’: The Nix store in /nix/store and database in /nix/var/nix/db, accessed directly. * ‘daemon’: The Nix store accessed via a Unix domain socket connection to nix-daemon. + * ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on + whether the user has write access to the local Nix + store/database. + * ‘file://’: A binary cache stored in . - If ‘uri’ is empty, it defaults to ‘direct’ or ‘daemon’ depending on - whether the user has write access to the local Nix store/database. - set to true *unless* you're going to collect garbage. */ -ref openStore(const std::string & uri = getEnv("NIX_REMOTE")); + * ‘https://’: A binary cache accessed via HTTP. -ref openStore(const std::string & uri, const Store::Params & params); + * ‘s3://’: A writable binary cache stored on Amazon's Simple + Storage Service. + + * ‘ssh://[user@]’: A remote Nix store accessed by running + ‘nix-store --serve’ via SSH. + + You can pass parameters to the store implementation by appending + ‘?key=value&key=value&...’ to the URI. +*/ +ref openStore(const std::string & uri = getEnv("NIX_REMOTE"), + const Store::Params & extraParams = Store::Params()); void copyPaths(ref from, ref to, const PathSet & storePaths, bool substitute = false);