Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 25da1c64f90b
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2825e05d21ec
Choose a head ref
  • 6 commits
  • 9 files changed
  • 1 contributor

Commits on Aug 3, 2018

  1. LegacySSHStore: Add remote-store option

    This is primarily useful for testing, e.g.
    
      $ nix copy --to 'ssh://localhost?remote-store=/tmp/nix' ...
    edolstra committed Aug 3, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    e268bbc View commit details
  2. SSHMaster: Bypass SSH when connecting to localhost

    This is primarily useful for testing since it removes the need to have
    SSH working.
    edolstra committed Aug 3, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    4e7d5f6 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    848a937 View commit details
  4. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    eeebe4c View commit details
  5. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    34c17fd View commit details
  6. Make adding paths via nix-store --serve run in constant memory

    It adds a new operation, cmdAddToStoreNar, that does the same thing as
    the corresponding nix-daemon operation, i.e. call addToStore(). This
    replaces cmdImportPaths, which has the major issue that it sends the
    NAR first and the store path second, thus requiring us to store the
    incoming NAR either in memory or on disk until we decide what to do
    with it.
    
    For example, this reduces the memory usage of
    
      $ nix copy --to 'ssh://localhost?remote-store=/tmp/nix' /nix/store/95cwv4q54dc6giaqv6q6p4r02ia2km35-blender-2.79
    
    from 267 MiB to 12 MiB.
    
    Probably fixes #1988.
    edolstra committed Aug 3, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    2825e05 View commit details
Showing with 130 additions and 26 deletions.
  1. +48 −14 src/libstore/legacy-ssh-store.cc
  2. +2 −1 src/libstore/serve-protocol.hh
  3. +15 −7 src/libstore/ssh.cc
  4. +1 −0 src/libstore/ssh.hh
  5. +18 −2 src/libstore/store-api.cc
  6. +1 −0 src/nix-daemon/nix-daemon.cc
  7. +23 −1 src/nix-store/nix-store.cc
  8. +2 −1 tests/local.mk
  9. +20 −0 tests/nix-copy-ssh.sh
62 changes: 48 additions & 14 deletions src/libstore/legacy-ssh-store.cc
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ struct LegacySSHStore : public Store
const Setting<Path> sshKey{this, "", "ssh-key", "path to an SSH private key"};
const Setting<bool> compress{this, false, "compress", "whether to compress the connection"};
const Setting<Path> remoteProgram{this, "nix-store", "remote-program", "path to the nix-store executable on the remote system"};
const Setting<std::string> remoteStore{this, "", "remote-store", "URI of the store on the remote system"};

// Hack for getting remote build log output.
const Setting<int> logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"};
@@ -27,6 +28,7 @@ struct LegacySSHStore : public Store
FdSink to;
FdSource from;
int remoteVersion;
bool good = true;
};

std::string host;
@@ -41,7 +43,7 @@ struct LegacySSHStore : public Store
, connections(make_ref<Pool<Connection>>(
std::max(1, (int) maxConnections),
[this]() { return openConnection(); },
[](const ref<Connection> & r) { return true; }
[](const ref<Connection> & r) { return r->good; }
))
, master(
host,
@@ -56,7 +58,9 @@ struct LegacySSHStore : public Store
ref<Connection> openConnection()
{
auto conn = make_ref<Connection>();
conn->sshConn = master.startCommand(fmt("%s --serve --write", remoteProgram));
conn->sshConn = master.startCommand(
fmt("command time %s --serve --write", remoteProgram)
+ (remoteStore.get() == "" ? "" : " --store " + shellEscape(remoteStore.get())));
conn->to = FdSink(conn->sshConn->in.get());
conn->from = FdSource(conn->sshConn->out.get());

@@ -127,18 +131,48 @@ struct LegacySSHStore : public Store

auto conn(connections->get());

conn->to
<< cmdImportPaths
<< 1;
copyNAR(source, conn->to);
conn->to
<< exportMagic
<< info.path
<< info.references
<< info.deriver
<< 0
<< 0;
conn->to.flush();
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 4) {

conn->to
<< cmdAddToStoreNar
<< info.path
<< info.deriver
<< info.narHash.to_string(Base16, false)
<< info.references
<< info.registrationTime
<< info.narSize
<< info.ultimate
<< info.sigs
<< info.ca;
try {
copyNAR(source, conn->to);
} catch (...) {
conn->good = false;
throw;
}
conn->to.flush();

} else {

conn->to
<< cmdImportPaths
<< 1;
try {
copyNAR(source, conn->to);
} catch (...) {
conn->good = false;
throw;
}
conn->to
<< exportMagic
<< info.path
<< info.references
<< info.deriver
<< 0
<< 0;
conn->to.flush();

}

if (readInt(conn->from) != 1)
throw Error("failed to add path '%s' to remote host '%s', info.path, host");
3 changes: 2 additions & 1 deletion src/libstore/serve-protocol.hh
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ namespace nix {
#define SERVE_MAGIC_1 0x390c9deb
#define SERVE_MAGIC_2 0x5452eecb

#define SERVE_PROTOCOL_VERSION 0x204
#define SERVE_PROTOCOL_VERSION 0x205
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)

@@ -18,6 +18,7 @@ typedef enum {
cmdBuildPaths = 6,
cmdQueryClosure = 7,
cmdBuildDerivation = 8,
cmdAddToStoreNar = 9,
} ServeCommand;

}
22 changes: 15 additions & 7 deletions src/libstore/ssh.cc
Original file line number Diff line number Diff line change
@@ -4,8 +4,9 @@ namespace nix {

SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, bool useMaster, bool compress, int logFD)
: host(host)
, fakeSSH(host == "localhost")
, keyFile(keyFile)
, useMaster(useMaster)
, useMaster(useMaster && !fakeSSH)
, compress(compress)
, logFD(logFD)
{
@@ -45,12 +46,19 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(const std::string
if (logFD != -1 && dup2(logFD, STDERR_FILENO) == -1)
throw SysError("duping over stderr");

Strings args = { "ssh", host.c_str(), "-x", "-a" };
addCommonSSHOpts(args);
if (socketPath != "")
args.insert(args.end(), {"-S", socketPath});
if (verbosity >= lvlChatty)
args.push_back("-v");
Strings args;

if (fakeSSH) {
args = { "bash", "-c" };
} else {
args = { "ssh", host.c_str(), "-x", "-a" };
addCommonSSHOpts(args);
if (socketPath != "")
args.insert(args.end(), {"-S", socketPath});
if (verbosity >= lvlChatty)
args.push_back("-v");
}

args.push_back(command);
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());

1 change: 1 addition & 0 deletions src/libstore/ssh.hh
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ class SSHMaster
private:

const std::string host;
bool fakeSSH;
const std::string keyFile;
const bool useMaster;
const bool compress;
20 changes: 18 additions & 2 deletions src/libstore/store-api.cc
Original file line number Diff line number Diff line change
@@ -844,8 +844,24 @@ ref<Store> openStore(const std::string & uri_,
if (q != std::string::npos) {
for (auto s : tokenizeString<Strings>(uri.substr(q + 1), "&")) {
auto e = s.find('=');
if (e != std::string::npos)
params[s.substr(0, e)] = s.substr(e + 1);
if (e != std::string::npos) {
auto value = s.substr(e + 1);
std::string decoded;
for (size_t i = 0; i < value.size(); ) {
if (value[i] == '%') {
if (i + 2 >= value.size())
throw Error("invalid URI parameter '%s'", value);
try {
decoded += std::stoul(std::string(value, i + 1, 2), 0, 16);
i += 3;
} catch (...) {
throw Error("invalid URI parameter '%s'", value);
}
} else
decoded += value[i++];
}
params[s.substr(0, e)] = decoded;
}
}
uri = uri_.substr(0, q);
}
1 change: 1 addition & 0 deletions src/nix-daemon/nix-daemon.cc
Original file line number Diff line number Diff line change
@@ -707,6 +707,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,

logger->startWork();

// FIXME: race if addToStore doesn't read source?
store.cast<Store>()->addToStore(info, *source, (RepairFlag) repair,
dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);

24 changes: 23 additions & 1 deletion src/nix-store/nix-store.cc
Original file line number Diff line number Diff line change
@@ -860,7 +860,7 @@ static void opServe(Strings opFlags, Strings opArgs)
}

case cmdDumpStorePath:
dumpPath(readStorePath(*store, in), out);
store->narFromPath(readStorePath(*store, in), out);
break;

case cmdImportPaths: {
@@ -924,6 +924,28 @@ static void opServe(Strings opFlags, Strings opArgs)
break;
}

case cmdAddToStoreNar: {
if (!writeAllowed) throw Error("importing paths is not allowed");

ValidPathInfo info;
info.path = readStorePath(*store, in);
in >> info.deriver;
if (!info.deriver.empty())
store->assertStorePath(info.deriver);
info.narHash = Hash(readString(in), htSHA256);
info.references = readStorePaths<PathSet>(*store, in);
in >> info.registrationTime >> info.narSize >> info.ultimate;
info.sigs = readStrings<StringSet>(in);
in >> info.ca;

// FIXME: race if addToStore doesn't read source?
store->addToStore(info, in, NoRepair, NoCheckSigs);

out << 1; // indicate success

break;
}

default:
throw Error(format("unknown serve command %1%") % cmd);
}
3 changes: 2 additions & 1 deletion tests/local.mk
Original file line number Diff line number Diff line change
@@ -25,7 +25,8 @@ nix_tests = \
pure-eval.sh \
check.sh \
plugins.sh \
search.sh
search.sh \
nix-copy-ssh.sh
# parallel.sh

install-tests += $(foreach x, $(nix_tests), tests/$(x))
20 changes: 20 additions & 0 deletions tests/nix-copy-ssh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
source common.sh

clearStore
clearCache

remoteRoot=$TEST_ROOT/store2
chmod -R u+w "$remoteRoot" || true
rm -rf "$remoteRoot"

outPath=$(nix-build dependencies.nix)

nix copy --to "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath

[ -f $remoteRoot$outPath/foobar ]

clearStore

nix copy --no-check-sigs --from "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath

[ -f $outPath/foobar ]