Add an S3-backed binary cache store
[?]
Feb 18, 2016, 3:18 PM
GTUZLZRHJ6GL5BNXOO3GA6Y3GFO7AXLIVPQHSG26LCF42KC2N7LQCDependencies
- [2]
UQYHPQ6URun PostgreSQL during "make check" - [3]
N2NKSKHSRefactor local binary cache code into a subclass - [4]
7VQ4ALFYUpdate "make check" for the new queue runner - [5]
GH4S4AWMRename file - [6]
24BMQDZAStart of single-process hydra-queue-runner - [7]
N4IROACVMove buildRemote() into State - [8]
W2AOTSS6Rename class - [9]
73YR46NJhydra-queue-runner: Write directly to a binary cache - [10]
3FQ65IXOhydra-queue-runner: Compress binary cache NARs using xz - [11]
HJOEIMLRRefactor - [12]
5AIYUMTBBasic remote building - [13]
32HHP5CWhydra-queue-runner: Support generating a signed binary cache - [*]
T4LLYESZ* Nix expression for building Hydra. - [*]
6K5PBUUNUse buildEnv to combine Hydra's Perl dependencies - [*]
ENXUSMSVMake concurrency more robust
Change contents
- edit in release.nix at line 162
(aws-sdk-cpp.override {apis = ["s3"];customMemoryManagement = false;}) - replacement in src/hydra-queue-runner/Makefile.am at line 7
local-binary-cache-store.hh local-binary-cache-store.cclocal-binary-cache-store.hh local-binary-cache-store.cc \s3-binary-cache-store.hh s3-binary-cache-store.cc - replacement in src/hydra-queue-runner/Makefile.am at line 11
AM_CXXFLAGS = $(NIX_CFLAGS) -Wall[4.369]AM_CXXFLAGS = $(NIX_CFLAGS) -Wall -laws-cpp-sdk-s3 - edit in src/hydra-queue-runner/binary-cache-store.cc at line 9
#include <chrono> - edit in src/hydra-queue-runner/binary-cache-store.cc at line 54
printMsg(lvlTalkative, format("copying path ‘%1%’ (%2% bytes) to binary cache")% info.path % info.narSize); - edit in src/hydra-queue-runner/binary-cache-store.cc at line 57
auto now1 = std::chrono::steady_clock::now(); - edit in src/hydra-queue-runner/binary-cache-store.cc at line 59
auto now2 = std::chrono::steady_clock::now(); - edit in src/hydra-queue-runner/binary-cache-store.cc at line 62
printMsg(lvlTalkative, format("copying path ‘%1%’ (%2% bytes, compressed %3$.1f%% in %4% ms) to binary cache")% info.path % info.narSize% ((1.0 - (double) narXz.size() / nar.size()) * 100.0)% std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count()); - edit in src/hydra-queue-runner/hydra-queue-runner.cc at line 11[3.1356][17.19]
#include "s3-binary-cache-store.hh" - edit in src/hydra-queue-runner/hydra-queue-runner.cc at line 37
#if 0 - replacement in src/hydra-queue-runner/hydra-queue-runner.cc at line 39
"/tmp/binary-cache","/home/eelco/Misc/Keys/test.nixos.org/secret","/home/eelco/Misc/Keys/test.nixos.org/public","/tmp/binary-cache");#endifauto store = make_ref<S3BinaryCacheStore>(getLocalStore(), - replacement in src/hydra-queue-runner/hydra-queue-runner.cc at line 45
"/home/eelco/Misc/Keys/test.nixos.org/public");"/home/eelco/Misc/Keys/test.nixos.org/public","nix-test-cache-3"); - replacement in src/hydra-queue-runner/local-binary-cache-store.cc at line 6
const Path & binaryCacheDir, const Path & secretKeyFile, const Path & publicKeyFile)const Path & secretKeyFile, const Path & publicKeyFile,const Path & binaryCacheDir) - replacement in src/hydra-queue-runner/local-binary-cache-store.hh at line 15
LocalBinaryCacheStore(ref<Store> localStore, const Path & binaryCacheDir,const Path & secretKeyFile, const Path & publicKeyFile);LocalBinaryCacheStore(ref<Store> localStore,const Path & secretKeyFile, const Path & publicKeyFile,const Path & binaryCacheDir); - file addition: s3-binary-cache-store.cc[4.187]
#include "s3-binary-cache-store.hh"#include <aws/core/client/ClientConfiguration.h>#include <aws/s3/S3Client.h>#include <aws/s3/model/CreateBucketRequest.h>#include <aws/s3/model/GetBucketLocationRequest.h>#include <aws/s3/model/GetObjectRequest.h>#include <aws/s3/model/HeadObjectRequest.h>#include <aws/s3/model/PutObjectRequest.h>namespace nix {/* Helper: given an Outcome<R, E>, return R in case of success, orthrow an exception in case of an error. */template<typename R, typename E>R && checkAws(Aws::Utils::Outcome<R, E> && outcome){if (!outcome.IsSuccess())throw Error(format("AWS error: %1%") % outcome.GetError().GetMessage());return outcome.GetResultWithOwnership();}S3BinaryCacheStore::S3BinaryCacheStore(ref<Store> localStore,const Path & secretKeyFile, const Path & publicKeyFile,const std::string & bucketName): BinaryCacheStore(localStore, secretKeyFile, publicKeyFile), bucketName(bucketName), config(makeConfig()), client(make_ref<Aws::S3::S3Client>(*config)){}ref<Aws::Client::ClientConfiguration> S3BinaryCacheStore::makeConfig(){auto res = make_ref<Aws::Client::ClientConfiguration>();res->region = Aws::Region::EU_WEST_1;res->requestTimeoutMs = 600 * 1000;return res;}void S3BinaryCacheStore::init(){/* Create the bucket if it doesn't already exists. */// FIXME: HeadBucket would be more appropriate, but doesn't return// an easily parsed 404 message.auto res = client->GetBucketLocation(Aws::S3::Model::GetBucketLocationRequest().WithBucket(bucketName));if (!res.IsSuccess()) {if (res.GetError().GetErrorType() != Aws::S3::S3Errors::NO_SUCH_BUCKET)throw Error(format("AWS error: %1%") % res.GetError().GetMessage());checkAws(client->CreateBucket(Aws::S3::Model::CreateBucketRequest().WithBucket(bucketName).WithCreateBucketConfiguration(Aws::S3::Model::CreateBucketConfiguration().WithLocationConstraint(Aws::S3::Model::BucketLocationConstraint::eu_west_1))));}BinaryCacheStore::init();}bool S3BinaryCacheStore::fileExists(const std::string & path){auto res = client->HeadObject(Aws::S3::Model::HeadObjectRequest().WithBucket(bucketName).WithKey(path));if (!res.IsSuccess()) {auto & error = res.GetError();if (error.GetErrorType() == Aws::S3::S3Errors::UNKNOWN // FIXME&& error.GetMessage().find("404") != std::string::npos)return false;throw Error(format("AWS error: %1%") % error.GetMessage());}return true;}void S3BinaryCacheStore::upsertFile(const std::string & path, const std::string & data){auto request =Aws::S3::Model::PutObjectRequest().WithBucket(bucketName).WithKey(path);auto stream = std::make_shared<std::stringstream>(data);request.SetBody(stream);auto now1 = std::chrono::steady_clock::now();auto result = checkAws(client->PutObject(request));auto now2 = std::chrono::steady_clock::now();printMsg(lvlError, format("uploaded ‘s3://%1%/%2%’ (%3% bytes) in %4% ms")% bucketName % path% data.size()% std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count());}std::string S3BinaryCacheStore::getFile(const std::string & path){auto request =Aws::S3::Model::GetObjectRequest().WithBucket(bucketName).WithKey(path);request.SetResponseStreamFactory([&]() {return Aws::New<std::stringstream>("STRINGSTREAM");});auto now1 = std::chrono::steady_clock::now();auto result = checkAws(client->GetObject(request));auto now2 = std::chrono::steady_clock::now();auto res = dynamic_cast<std::stringstream &>(result.GetBody()).str();printMsg(lvlError, format("downloaded ‘s3://%1%/%2%’ (%3%) in %4% ms")% bucketName % path% res.size()% std::chrono::duration_cast<std::chrono::milliseconds>(now2 - now1).count());return res;}} - file addition: s3-binary-cache-store.hh[4.187]
#pragma once#include "binary-cache-store.hh"namespace Aws { namespace Client { class ClientConfiguration; } }namespace Aws { namespace S3 { class S3Client; } }namespace nix {class S3BinaryCacheStore : public BinaryCacheStore{private:std::string bucketName;ref<Aws::Client::ClientConfiguration> config;ref<Aws::S3::S3Client> client;public:S3BinaryCacheStore(ref<Store> localStore,const Path & secretKeyFile, const Path & publicKeyFile,const std::string & bucketName);void init() override;private:ref<Aws::Client::ClientConfiguration> makeConfig();protected:bool fileExists(const std::string & path) override;void upsertFile(const std::string & path, const std::string & data) override;std::string getFile(const std::string & path) override;};}