Separate store configs from the implems

Rework the `Store` hierarchy so that there's now one hierarchy for the
store configs and one for the implementations (where each implementation
extends the corresponding config). So a class hierarchy like

```
StoreConfig-------->Store
    |                 |
    v                 v
SubStoreConfig----->SubStore
    |                 |
    v                 v
SubSubStoreConfig-->SubSubStore
```

(with virtual inheritance to prevent DDD).

The advantage of this architecture is that we can now introspect the configuration of a store without having to instantiate the store itself
This commit is contained in:
regnat 2020-09-10 10:55:51 +02:00
parent aa4eac3788
commit 22afa8fb4d
13 changed files with 120 additions and 83 deletions

View file

@ -22,7 +22,8 @@
namespace nix { namespace nix {
BinaryCacheStore::BinaryCacheStore(const Params & params) BinaryCacheStore::BinaryCacheStore(const Params & params)
: Store(params) : BinaryCacheStoreConfig(params)
, Store(params)
{ {
if (secretKeyFile != "") if (secretKeyFile != "")
secretKey = std::unique_ptr<SecretKey>(new SecretKey(readFile(secretKeyFile))); secretKey = std::unique_ptr<SecretKey>(new SecretKey(readFile(secretKeyFile)));

View file

@ -11,9 +11,9 @@ namespace nix {
struct NarInfo; struct NarInfo;
class BinaryCacheStore : public Store struct BinaryCacheStoreConfig : virtual StoreConfig
{ {
public: using StoreConfig::StoreConfig;
const Setting<std::string> compression{this, "xz", "compression", "NAR compression method ('xz', 'bzip2', or 'none')"}; const Setting<std::string> compression{this, "xz", "compression", "NAR compression method ('xz', 'bzip2', or 'none')"};
const Setting<bool> writeNARListing{this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"}; const Setting<bool> writeNARListing{this, false, "write-nar-listing", "whether to write a JSON file listing the files in each NAR"};
@ -22,6 +22,10 @@ public:
const Setting<Path> localNarCache{this, "", "local-nar-cache", "path to a local cache of NARs"}; const Setting<Path> localNarCache{this, "", "local-nar-cache", "path to a local cache of NARs"};
const Setting<bool> parallelCompression{this, false, "parallel-compression", const Setting<bool> parallelCompression{this, false, "parallel-compression",
"enable multi-threading compression, available for xz only currently"}; "enable multi-threading compression, available for xz only currently"};
};
class BinaryCacheStore : public Store, public virtual BinaryCacheStoreConfig
{
private: private:

View file

@ -2,6 +2,8 @@
namespace nix { namespace nix {
struct DummyStoreConfig : StoreConfig {};
struct DummyStore : public Store struct DummyStore : public Store
{ {
DummyStore(const std::string uri, const Params & params) DummyStore(const std::string uri, const Params & params)
@ -54,6 +56,6 @@ struct DummyStore : public Store
{ unsupported("buildDerivation"); } { unsupported("buildDerivation"); }
}; };
static RegisterStoreImplementation<DummyStore> regStore; static RegisterStoreImplementation<DummyStore, DummyStoreConfig> regStore;
} }

View file

@ -7,7 +7,12 @@ namespace nix {
MakeError(UploadToHTTP, Error); MakeError(UploadToHTTP, Error);
class HttpBinaryCacheStore : public BinaryCacheStore struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
{
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
};
class HttpBinaryCacheStore : public BinaryCacheStore, public HttpBinaryCacheStoreConfig
{ {
private: private:
@ -23,16 +28,11 @@ private:
public: public:
HttpBinaryCacheStore(
const Params & params)
: HttpBinaryCacheStore("dummy", params)
{
}
HttpBinaryCacheStore( HttpBinaryCacheStore(
const Path & _cacheUri, const Path & _cacheUri,
const Params & params) const Params & params)
: BinaryCacheStore(params) : BinaryCacheStore(params)
, HttpBinaryCacheStoreConfig(params)
, cacheUri(_cacheUri) , cacheUri(_cacheUri)
{ {
if (cacheUri.back() == '/') if (cacheUri.back() == '/')
@ -176,6 +176,6 @@ protected:
}; };
static RegisterStoreImplementation<HttpBinaryCacheStore> regStore; static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regStore;
} }

View file

@ -9,15 +9,21 @@
namespace nix { namespace nix {
struct LegacySSHStore : public Store struct LegacySSHStoreConfig : virtual StoreConfig
{ {
using StoreConfig::StoreConfig;
const Setting<int> maxConnections{this, 1, "max-connections", "maximum number of concurrent SSH connections"}; const Setting<int> maxConnections{this, 1, "max-connections", "maximum number of concurrent SSH connections"};
const Setting<Path> sshKey{this, "", "ssh-key", "path to an SSH private key"}; 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<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<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"}; const Setting<std::string> remoteStore{this, "", "remote-store", "URI of the store on the remote system"};
};
struct LegacySSHStore : public Store, public virtual LegacySSHStoreConfig
{
// Hack for getting remote build log output. // Hack for getting remote build log output.
// Intentionally not in `StoreConfig` so that it doesn't appear in the
// documentation
const Setting<int> logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"}; const Setting<int> logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"};
struct Connection struct Connection
@ -37,12 +43,9 @@ struct LegacySSHStore : public Store
static std::vector<std::string> uriPrefixes() { return {"ssh"}; } static std::vector<std::string> uriPrefixes() { return {"ssh"}; }
LegacySSHStore(const Params & params)
: LegacySSHStore("dummy", params)
{}
LegacySSHStore(const string & host, const Params & params) LegacySSHStore(const string & host, const Params & params)
: Store(params) : LegacySSHStoreConfig(params)
, Store(params)
, host(host) , host(host)
, connections(make_ref<Pool<Connection>>( , connections(make_ref<Pool<Connection>>(
std::max(1, (int) maxConnections), std::max(1, (int) maxConnections),
@ -329,6 +332,6 @@ public:
} }
}; };
static RegisterStoreImplementation<LegacySSHStore> regStore; static RegisterStoreImplementation<LegacySSHStore, LegacySSHStoreConfig> regStore;
} }

View file

@ -4,7 +4,12 @@
namespace nix { namespace nix {
class LocalBinaryCacheStore : public BinaryCacheStore struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
{
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
};
class LocalBinaryCacheStore : public BinaryCacheStore, public virtual LocalBinaryCacheStoreConfig
{ {
private: private:
@ -12,16 +17,11 @@ private:
public: public:
LocalBinaryCacheStore(
const Params & params)
: LocalBinaryCacheStore("dummy", params)
{
}
LocalBinaryCacheStore( LocalBinaryCacheStore(
const Path & binaryCacheDir, const Path & binaryCacheDir,
const Params & params) const Params & params)
: BinaryCacheStore(params) : LocalBinaryCacheStoreConfig(params)
, BinaryCacheStore(params)
, binaryCacheDir(binaryCacheDir) , binaryCacheDir(binaryCacheDir)
{ {
} }
@ -102,6 +102,6 @@ std::vector<std::string> LocalBinaryCacheStore::uriPrefixes()
return {"file"}; return {"file"};
} }
static RegisterStoreImplementation<LocalBinaryCacheStore> regStore; static RegisterStoreImplementation<LocalBinaryCacheStore, LocalBinaryCacheStoreConfig> regStore;
} }

View file

@ -95,7 +95,7 @@ public:
private: private:
Setting<bool> requireSigs{(Store*) this, Setting<bool> requireSigs{(StoreConfig*) this,
settings.requireSigs, settings.requireSigs,
"require-sigs", "whether store paths should have a trusted signature on import"}; "require-sigs", "whether store paths should have a trusted signature on import"};

View file

@ -94,6 +94,7 @@ void write(const Store & store, Sink & out, const std::optional<StorePath> & sto
/* TODO: Separate these store impls into different files, give them better names */ /* TODO: Separate these store impls into different files, give them better names */
RemoteStore::RemoteStore(const Params & params) RemoteStore::RemoteStore(const Params & params)
: Store(params) : Store(params)
, RemoteStoreConfig(params)
, connections(make_ref<Pool<Connection>>( , connections(make_ref<Pool<Connection>>(
std::max(1, (int) maxConnections), std::max(1, (int) maxConnections),
[this]() { return openConnectionWrapper(); }, [this]() { return openConnectionWrapper(); },
@ -124,6 +125,7 @@ ref<RemoteStore::Connection> RemoteStore::openConnectionWrapper()
UDSRemoteStore::UDSRemoteStore(const Params & params) UDSRemoteStore::UDSRemoteStore(const Params & params)
: Store(params) : Store(params)
, UDSRemoteStoreConfig(params)
, LocalFSStore(params) , LocalFSStore(params)
, RemoteStore(params) , RemoteStore(params)
{ {
@ -131,11 +133,9 @@ UDSRemoteStore::UDSRemoteStore(const Params & params)
UDSRemoteStore::UDSRemoteStore(std::string socket_path, const Params & params) UDSRemoteStore::UDSRemoteStore(std::string socket_path, const Params & params)
: Store(params) : UDSRemoteStore(params)
, LocalFSStore(params)
, RemoteStore(params)
, path(socket_path)
{ {
path.emplace(socket_path);
} }
@ -982,6 +982,6 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source *
return nullptr; return nullptr;
} }
static RegisterStoreImplementation<UDSRemoteStore> regStore; static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regStore;
} }

View file

@ -16,19 +16,23 @@ struct FdSource;
template<typename T> class Pool; template<typename T> class Pool;
struct ConnectionHandle; struct ConnectionHandle;
struct RemoteStoreConfig : virtual StoreConfig
{
using StoreConfig::StoreConfig;
const Setting<int> maxConnections{(StoreConfig*) this, 1,
"max-connections", "maximum number of concurrent connections to the Nix daemon"};
const Setting<unsigned int> maxConnectionAge{(StoreConfig*) this, std::numeric_limits<unsigned int>::max(),
"max-connection-age", "number of seconds to reuse a connection"};
};
/* FIXME: RemoteStore is a misnomer - should be something like /* FIXME: RemoteStore is a misnomer - should be something like
DaemonStore. */ DaemonStore. */
class RemoteStore : public virtual Store class RemoteStore : public virtual Store, public virtual RemoteStoreConfig
{ {
public: public:
const Setting<int> maxConnections{(Store*) this, 1,
"max-connections", "maximum number of concurrent connections to the Nix daemon"};
const Setting<unsigned int> maxConnectionAge{(Store*) this, std::numeric_limits<unsigned int>::max(),
"max-connection-age", "number of seconds to reuse a connection"};
virtual bool sameMachine() = 0; virtual bool sameMachine() = 0;
RemoteStore(const Params & params); RemoteStore(const Params & params);
@ -141,7 +145,21 @@ private:
}; };
class UDSRemoteStore : public LocalFSStore, public RemoteStore struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig
{
UDSRemoteStoreConfig(const Store::Params & params)
: LocalFSStoreConfig(params)
, RemoteStoreConfig(params)
{
}
UDSRemoteStoreConfig()
: UDSRemoteStoreConfig(Store::Params({}))
{
}
};
class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig
{ {
public: public:

View file

@ -172,8 +172,9 @@ S3Helper::FileTransferResult S3Helper::getObject(
return res; return res;
} }
struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
{ {
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
const Setting<std::string> profile{this, "", "profile", "The name of the AWS configuration profile to use."}; const Setting<std::string> profile{this, "", "profile", "The name of the AWS configuration profile to use."};
const Setting<std::string> region{this, Aws::Region::US_EAST_1, "region", {"aws-region"}}; const Setting<std::string> region{this, Aws::Region::US_EAST_1, "region", {"aws-region"}};
const Setting<std::string> scheme{this, "", "scheme", "The scheme to use for S3 requests, https by default."}; const Setting<std::string> scheme{this, "", "scheme", "The scheme to use for S3 requests, https by default."};
@ -185,20 +186,21 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
this, false, "multipart-upload", "whether to use multi-part uploads"}; this, false, "multipart-upload", "whether to use multi-part uploads"};
const Setting<uint64_t> bufferSize{ const Setting<uint64_t> bufferSize{
this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"}; this, 5 * 1024 * 1024, "buffer-size", "size (in bytes) of each part in multi-part uploads"};
};
struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore, virtual S3BinaryCacheStoreConfig
{
std::string bucketName; std::string bucketName;
Stats stats; Stats stats;
S3Helper s3Helper; S3Helper s3Helper;
S3BinaryCacheStoreImpl(const Params & params)
: S3BinaryCacheStoreImpl("dummy-bucket", params) {}
S3BinaryCacheStoreImpl( S3BinaryCacheStoreImpl(
const std::string & bucketName, const std::string & bucketName,
const Params & params) const Params & params)
: S3BinaryCacheStore(params) : S3BinaryCacheStoreConfig(params)
, S3BinaryCacheStore(params)
, bucketName(bucketName) , bucketName(bucketName)
, s3Helper(profile, region, scheme, endpoint) , s3Helper(profile, region, scheme, endpoint)
{ {
@ -434,7 +436,7 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore
}; };
static RegisterStoreImplementation<S3BinaryCacheStoreImpl> regStore; static RegisterStoreImplementation<S3BinaryCacheStoreImpl, S3BinaryCacheStoreConfig> regStore;
} }

View file

@ -8,23 +8,22 @@
namespace nix { namespace nix {
class SSHStore : public RemoteStore struct SSHStoreConfig : virtual RemoteStoreConfig
{
using RemoteStoreConfig::RemoteStoreConfig;
const Setting<Path> sshKey{(StoreConfig*) this, "", "ssh-key", "path to an SSH private key"};
const Setting<bool> compress{(StoreConfig*) this, false, "compress", "whether to compress the connection"};
const Setting<Path> remoteProgram{(StoreConfig*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"};
const Setting<std::string> remoteStore{(StoreConfig*) this, "", "remote-store", "URI of the store on the remote system"};
};
class SSHStore : public RemoteStore, public virtual SSHStoreConfig
{ {
public: public:
const Setting<Path> sshKey{(Store*) this, "", "ssh-key", "path to an SSH private key"};
const Setting<bool> compress{(Store*) this, false, "compress", "whether to compress the connection"};
const Setting<Path> remoteProgram{(Store*) this, "nix-daemon", "remote-program", "path to the nix-daemon executable on the remote system"};
const Setting<std::string> remoteStore{(Store*) this, "", "remote-store", "URI of the store on the remote system"};
SSHStore(
const Params & params)
: SSHStore("dummy", params)
{
}
SSHStore(const std::string & host, const Params & params) SSHStore(const std::string & host, const Params & params)
: Store(params) : Store(params)
, RemoteStoreConfig(params)
, RemoteStore(params) , RemoteStore(params)
, host(host) , host(host)
, master( , master(
@ -82,6 +81,6 @@ ref<RemoteStore::Connection> SSHStore::openConnection()
return conn; return conn;
} }
static RegisterStoreImplementation<SSHStore> regStore; static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regStore;
} }

View file

@ -346,7 +346,7 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
Store::Store(const Params & params) Store::Store(const Params & params)
: Config(params) : StoreConfig(params)
, state({(size_t) pathInfoCacheSize}) , state({(size_t) pathInfoCacheSize})
{ {
} }

View file

@ -145,12 +145,9 @@ struct BuildResult
} }
}; };
struct StoreConfig : public Config
class Store : public std::enable_shared_from_this<Store>, public Config
{ {
public: using Config::Config;
typedef std::map<std::string, std::string> Params;
const PathSetting storeDir_{this, false, settings.nixStore, const PathSetting storeDir_{this, false, settings.nixStore,
"store", "path to the Nix store"}; "store", "path to the Nix store"};
@ -168,6 +165,14 @@ public:
"system-features", "system-features",
"Optional features that the system this store builds on implements (like \"kvm\")."}; "Optional features that the system this store builds on implements (like \"kvm\")."};
};
class Store : public std::enable_shared_from_this<Store>, public virtual StoreConfig
{
public:
typedef std::map<std::string, std::string> Params;
protected: protected:
struct PathInfoCacheValue { struct PathInfoCacheValue {
@ -632,22 +637,25 @@ protected:
}; };
struct LocalFSStoreConfig : virtual StoreConfig
class LocalFSStore : public virtual Store
{ {
public: using StoreConfig::StoreConfig;
// FIXME: the (StoreConfig*) cast works around a bug in gcc that causes
// FIXME: the (Store*) cast works around a bug in gcc that causes
// it to omit the call to the Setting constructor. Clang works fine // it to omit the call to the Setting constructor. Clang works fine
// either way. // either way.
const PathSetting rootDir{(Store*) this, true, "", const PathSetting rootDir{(StoreConfig*) this, true, "",
"root", "directory prefixed to all other paths"}; "root", "directory prefixed to all other paths"};
const PathSetting stateDir{(Store*) this, false, const PathSetting stateDir{(StoreConfig*) this, false,
rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir, rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir,
"state", "directory where Nix will store state"}; "state", "directory where Nix will store state"};
const PathSetting logDir{(Store*) this, false, const PathSetting logDir{(StoreConfig*) this, false,
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir, rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
"log", "directory where Nix will store state"}; "log", "directory where Nix will store state"};
};
class LocalFSStore : public virtual Store, public LocalFSStoreConfig
{
public:
const static string drvsLogDir; const static string drvsLogDir;
@ -753,13 +761,13 @@ std::list<ref<Store>> getDefaultSubstituters();
struct StoreFactory struct StoreFactory
{ {
std::function<std::shared_ptr<Store> (const std::string & uri, const Store::Params & params)> create; std::function<std::shared_ptr<Store> (const std::string & uri, const Store::Params & params)> create;
std::function<std::shared_ptr<Store> (const Store::Params & params)> createDummy; std::function<std::shared_ptr<StoreConfig> ()> getConfig;
}; };
struct Implementations struct Implementations
{ {
static std::vector<StoreFactory> * registered; static std::vector<StoreFactory> * registered;
template<typename T> template<typename T, typename TConfig>
static void add() static void add()
{ {
if (!registered) registered = new std::vector<StoreFactory>(); if (!registered) registered = new std::vector<StoreFactory>();
@ -768,21 +776,21 @@ struct Implementations
([](const std::string & uri, const Store::Params & params) ([](const std::string & uri, const Store::Params & params)
-> std::shared_ptr<Store> -> std::shared_ptr<Store>
{ return std::make_shared<T>(uri, params); }), { return std::make_shared<T>(uri, params); }),
.createDummy = .getConfig =
([](const Store::Params & params) ([]()
-> std::shared_ptr<Store> -> std::shared_ptr<StoreConfig>
{ return std::make_shared<T>(params); }) { return std::make_shared<TConfig>(); })
}; };
registered->push_back(factory); registered->push_back(factory);
} }
}; };
template<typename T> template<typename T, typename TConfig>
struct RegisterStoreImplementation struct RegisterStoreImplementation
{ {
RegisterStoreImplementation() RegisterStoreImplementation()
{ {
Implementations::add<T>(); Implementations::add<T, TConfig>();
} }
}; };