Skip to content

Commit 8b91127

Browse files
authored
Merge pull request #10748 from NixOS/legacy-ssh-extensions-for-hydra
Expose a bunch of things in the Legacy SSH Store for Hydra
2 parents 1068b96 + 5eade48 commit 8b91127

File tree

2 files changed

+140
-14
lines changed

2 files changed

+140
-14
lines changed

src/libstore/legacy-ssh-store.cc

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ ref<LegacySSHStore::Connection> LegacySSHStore::openConnection()
6969
command.push_back("--store");
7070
command.push_back(remoteStore.get());
7171
}
72-
conn->sshConn = master.startCommand(std::move(command));
72+
conn->sshConn = master.startCommand(std::move(command), std::list{extraSshArgs});
7373
conn->to = FdSink(conn->sshConn->in.get());
7474
conn->from = FdSource(conn->sshConn->out.get());
7575

@@ -100,29 +100,38 @@ std::string LegacySSHStore::getUri()
100100
return *uriSchemes().begin() + "://" + host;
101101
}
102102

103+
std::map<StorePath, UnkeyedValidPathInfo> LegacySSHStore::queryPathInfosUncached(
104+
const StorePathSet & paths)
105+
{
106+
auto conn(connections->get());
107+
108+
/* No longer support missing NAR hash */
109+
assert(GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4);
110+
111+
debug("querying remote host '%s' for info on '%s'", host, concatStringsSep(", ", printStorePathSet(paths)));
112+
113+
auto infos = conn->queryPathInfos(*this, paths);
114+
115+
for (const auto & [_, info] : infos) {
116+
if (info.narHash == Hash::dummy)
117+
throw Error("NAR hash is now mandatory");
118+
}
119+
120+
return infos;
121+
}
103122

104123
void LegacySSHStore::queryPathInfoUncached(const StorePath & path,
105124
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
106125
{
107126
try {
108-
auto conn(connections->get());
109-
110-
/* No longer support missing NAR hash */
111-
assert(GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4);
112-
113-
debug("querying remote host '%s' for info on '%s'", host, printStorePath(path));
114-
115-
auto infos = conn->queryPathInfos(*this, {path});
127+
auto infos = queryPathInfosUncached({path});
116128

117129
switch (infos.size()) {
118130
case 0:
119131
return callback(nullptr);
120132
case 1: {
121133
auto & [path2, info] = *infos.begin();
122134

123-
if (info.narHash == Hash::dummy)
124-
throw Error("NAR hash is now mandatory");
125-
126135
assert(path == path2);
127136
return callback(std::make_shared<ValidPathInfo>(
128137
std::move(path),
@@ -193,13 +202,19 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source,
193202

194203
void LegacySSHStore::narFromPath(const StorePath & path, Sink & sink)
195204
{
196-
auto conn(connections->get());
197-
conn->narFromPath(*this, path, [&](auto & source) {
205+
narFromPath(path, [&](auto & source) {
198206
copyNAR(source, sink);
199207
});
200208
}
201209

202210

211+
void LegacySSHStore::narFromPath(const StorePath & path, std::function<void(Source &)> fun)
212+
{
213+
auto conn(connections->get());
214+
conn->narFromPath(*this, path, fun);
215+
}
216+
217+
203218
static ServeProto::BuildOptions buildSettings()
204219
{
205220
return {
@@ -223,6 +238,19 @@ BuildResult LegacySSHStore::buildDerivation(const StorePath & drvPath, const Bas
223238
return conn->getBuildDerivationResponse(*this);
224239
}
225240

241+
std::function<BuildResult()> LegacySSHStore::buildDerivationAsync(
242+
const StorePath & drvPath, const BasicDerivation & drv,
243+
const ServeProto::BuildOptions & options)
244+
{
245+
// Until we have C++23 std::move_only_function
246+
auto conn = std::make_shared<Pool<Connection>::Handle>(connections->get());
247+
(*conn)->putBuildDerivationRequest(*this, drvPath, drv, options);
248+
249+
return [this,conn]() -> BuildResult {
250+
return (*conn)->getBuildDerivationResponse(*this);
251+
};
252+
}
253+
226254

227255
void LegacySSHStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
228256
{
@@ -294,6 +322,32 @@ StorePathSet LegacySSHStore::queryValidPaths(const StorePathSet & paths,
294322
}
295323

296324

325+
StorePathSet LegacySSHStore::queryValidPaths(const StorePathSet & paths,
326+
bool lock, SubstituteFlag maybeSubstitute)
327+
{
328+
auto conn(connections->get());
329+
return conn->queryValidPaths(*this,
330+
lock, paths, maybeSubstitute);
331+
}
332+
333+
334+
void LegacySSHStore::addMultipleToStoreLegacy(Store & srcStore, const StorePathSet & paths)
335+
{
336+
auto conn(connections->get());
337+
conn->to << ServeProto::Command::ImportPaths;
338+
try {
339+
srcStore.exportPaths(paths, conn->to);
340+
} catch (...) {
341+
conn->good = false;
342+
throw;
343+
}
344+
conn->to.flush();
345+
346+
if (readInt(conn->from) != 1)
347+
throw Error("remote machine failed to import closure");
348+
}
349+
350+
297351
void LegacySSHStore::connect()
298352
{
299353
auto conn(connections->get());
@@ -307,6 +361,23 @@ unsigned int LegacySSHStore::getProtocol()
307361
}
308362

309363

364+
pid_t LegacySSHStore::getConnectionPid()
365+
{
366+
auto conn(connections->get());
367+
return conn->sshConn->sshPid;
368+
}
369+
370+
371+
LegacySSHStore::ConnectionStats LegacySSHStore::getConnectionStats()
372+
{
373+
auto conn(connections->get());
374+
return {
375+
.bytesReceived = conn->from.read,
376+
.bytesSent = conn->to.written,
377+
};
378+
}
379+
380+
310381
/**
311382
* The legacy ssh protocol doesn't support checking for trusted-user.
312383
* Try using ssh-ng:// instead if you want to know.

src/libstore/legacy-ssh-store.hh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ssh.hh"
77
#include "callback.hh"
88
#include "pool.hh"
9+
#include "serve-protocol.hh"
910

1011
namespace nix {
1112

@@ -24,6 +25,11 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig
2425
const Setting<int> maxConnections{this, 1, "max-connections",
2526
"Maximum number of concurrent SSH connections."};
2627

28+
/**
29+
* Hack for hydra
30+
*/
31+
Strings extraSshArgs = {};
32+
2733
const std::string name() override { return "SSH Store"; }
2834

2935
static std::set<std::string> uriSchemes() { return {"ssh"}; }
@@ -60,11 +66,24 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
6066
void queryPathInfoUncached(const StorePath & path,
6167
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override;
6268

69+
std::map<StorePath, UnkeyedValidPathInfo> queryPathInfosUncached(
70+
const StorePathSet & paths);
71+
6372
void addToStore(const ValidPathInfo & info, Source & source,
6473
RepairFlag repair, CheckSigsFlag checkSigs) override;
6574

6675
void narFromPath(const StorePath & path, Sink & sink) override;
6776

77+
/**
78+
* Hands over the connection temporarily as source to the given
79+
* function. The function must not consume beyond the NAR; it can
80+
* not just blindly try to always read more bytes until it is
81+
* cut-off.
82+
*
83+
* This is exposed for sake of Hydra.
84+
*/
85+
void narFromPath(const StorePath & path, std::function<void(Source &)> fun);
86+
6887
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
6988
{ unsupported("queryPathFromHashPart"); }
7089

@@ -93,6 +112,16 @@ public:
93112
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
94113
BuildMode buildMode) override;
95114

115+
/**
116+
* Note, the returned function must only be called once, or we'll
117+
* try to read from the connection twice.
118+
*
119+
* @todo Use C++23 `std::move_only_function`.
120+
*/
121+
std::function<BuildResult()> buildDerivationAsync(
122+
const StorePath & drvPath, const BasicDerivation & drv,
123+
const ServeProto::BuildOptions & options);
124+
96125
void buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
97126

98127
void ensurePath(const StorePath & path) override
@@ -119,10 +148,36 @@ public:
119148
StorePathSet queryValidPaths(const StorePathSet & paths,
120149
SubstituteFlag maybeSubstitute = NoSubstitute) override;
121150

151+
/**
152+
* Custom variation that atomically creates temp locks on the remote
153+
* side.
154+
*
155+
* This exists to prevent a race where the remote host
156+
* garbage-collects paths that are already there. Optionally, ask
157+
* the remote host to substitute missing paths.
158+
*/
159+
StorePathSet queryValidPaths(const StorePathSet & paths,
160+
bool lock,
161+
SubstituteFlag maybeSubstitute = NoSubstitute);
162+
163+
/**
164+
* Just exists because this is exactly what Hydra was doing, and we
165+
* don't yet want an algorithmic change.
166+
*/
167+
void addMultipleToStoreLegacy(Store & srcStore, const StorePathSet & paths);
168+
122169
void connect() override;
123170

124171
unsigned int getProtocol() override;
125172

173+
struct ConnectionStats {
174+
size_t bytesReceived, bytesSent;
175+
};
176+
177+
ConnectionStats getConnectionStats();
178+
179+
pid_t getConnectionPid();
180+
126181
/**
127182
* The legacy ssh protocol doesn't support checking for trusted-user.
128183
* Try using ssh-ng:// instead if you want to know.

0 commit comments

Comments
 (0)