1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-08 19:46:02 +01:00

Merge pull request #14471 from obsidiansystems/derivation-options-json-test

FIx `DerivationOptions` JSON and clean up unit tests
This commit is contained in:
Jörg Thalheim 2025-11-06 18:21:15 +00:00 committed by GitHub
commit 34c77ffe38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 578 additions and 300 deletions

View file

@ -0,0 +1,46 @@
{
"additionalSandboxProfile": "sandcastle",
"allowLocalNetworking": true,
"allowSubstitutes": false,
"exportReferencesGraph": {
"refs1": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"refs2": [
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
]
},
"impureEnvVars": [
"UNICORN"
],
"impureHostDeps": [
"/usr/bin/ditto"
],
"noChroot": true,
"outputChecks": {
"forAllOutputs": {
"allowedReferences": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
],
"disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
],
"disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
],
"ignoreSelfRefs": true,
"maxClosureSize": null,
"maxSize": null
}
},
"passAsFile": [],
"preferLocalBuild": true,
"requiredSystemFeatures": [
"rainbow",
"uid-range"
],
"unsafeDiscardReferences": {}
}

View file

@ -0,0 +1,66 @@
{
"additionalSandboxProfile": "sandcastle",
"allowLocalNetworking": true,
"allowSubstitutes": false,
"exportReferencesGraph": {
"refs1": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"refs2": [
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"
]
},
"impureEnvVars": [
"UNICORN"
],
"impureHostDeps": [
"/usr/bin/ditto"
],
"noChroot": true,
"outputChecks": {
"perOutput": {
"bin": {
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [
"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"
],
"disallowedRequisites": [
"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"
],
"ignoreSelfRefs": false,
"maxClosureSize": null,
"maxSize": null
},
"dev": {
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [],
"disallowedRequisites": [],
"ignoreSelfRefs": false,
"maxClosureSize": 5909,
"maxSize": 789
},
"out": {
"allowedReferences": [
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"
],
"allowedRequisites": [
"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"
],
"disallowedReferences": [],
"disallowedRequisites": [],
"ignoreSelfRefs": false,
"maxClosureSize": null,
"maxSize": null
}
}
},
"passAsFile": [],
"preferLocalBuild": true,
"requiredSystemFeatures": [
"rainbow",
"uid-range"
],
"unsafeDiscardReferences": {}
}

View file

@ -0,0 +1,46 @@
{
"additionalSandboxProfile": "sandcastle",
"allowLocalNetworking": true,
"allowSubstitutes": false,
"exportReferencesGraph": {
"refs1": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"refs2": [
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
]
},
"impureEnvVars": [
"UNICORN"
],
"impureHostDeps": [
"/usr/bin/ditto"
],
"noChroot": true,
"outputChecks": {
"forAllOutputs": {
"allowedReferences": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
],
"disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
],
"disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
],
"ignoreSelfRefs": true,
"maxClosureSize": null,
"maxSize": null
}
},
"passAsFile": [],
"preferLocalBuild": true,
"requiredSystemFeatures": [
"rainbow",
"uid-range"
],
"unsafeDiscardReferences": {}
}

View file

@ -0,0 +1,24 @@
{
"additionalSandboxProfile": "",
"allowLocalNetworking": false,
"allowSubstitutes": true,
"exportReferencesGraph": {},
"impureEnvVars": [],
"impureHostDeps": [],
"noChroot": false,
"outputChecks": {
"forAllOutputs": {
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [],
"disallowedRequisites": [],
"ignoreSelfRefs": true,
"maxClosureSize": null,
"maxSize": null
}
},
"passAsFile": [],
"preferLocalBuild": false,
"requiredSystemFeatures": [],
"unsafeDiscardReferences": {}
}

View file

@ -0,0 +1,66 @@
{
"additionalSandboxProfile": "sandcastle",
"allowLocalNetworking": true,
"allowSubstitutes": false,
"exportReferencesGraph": {
"refs1": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"refs2": [
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"
]
},
"impureEnvVars": [
"UNICORN"
],
"impureHostDeps": [
"/usr/bin/ditto"
],
"noChroot": true,
"outputChecks": {
"perOutput": {
"bin": {
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [
"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"
],
"disallowedRequisites": [
"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"
],
"ignoreSelfRefs": false,
"maxClosureSize": null,
"maxSize": null
},
"dev": {
"allowedReferences": null,
"allowedRequisites": null,
"disallowedReferences": [],
"disallowedRequisites": [],
"ignoreSelfRefs": false,
"maxClosureSize": 5909,
"maxSize": 789
},
"out": {
"allowedReferences": [
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"
],
"allowedRequisites": [
"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"
],
"disallowedReferences": [],
"disallowedRequisites": [],
"ignoreSelfRefs": false,
"maxClosureSize": null,
"maxSize": null
}
}
},
"passAsFile": [],
"preferLocalBuild": true,
"requiredSystemFeatures": [
"rainbow",
"uid-range"
],
"unsafeDiscardReferences": {}
}

View file

@ -0,0 +1,16 @@
{
"additionalSandboxProfile": "",
"allowLocalNetworking": false,
"allowSubstitutes": true,
"exportReferencesGraph": {},
"impureEnvVars": [],
"impureHostDeps": [],
"noChroot": false,
"outputChecks": {
"perOutput": {}
},
"passAsFile": [],
"preferLocalBuild": false,
"requiredSystemFeatures": [],
"unsafeDiscardReferences": {}
}

View file

@ -10,13 +10,15 @@
#include "nix/util/json-utils.hh" #include "nix/util/json-utils.hh"
#include "nix/store/tests/libstore.hh" #include "nix/store/tests/libstore.hh"
#include "nix/util/tests/characterization.hh" #include "nix/util/tests/json-characterization.hh"
namespace nix { namespace nix {
using namespace nlohmann; using namespace nlohmann;
class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest class DerivationAdvancedAttrsTest : public JsonCharacterizationTest<Derivation>,
public JsonCharacterizationTest<DerivationOptions>,
public LibStoreTest
{ {
protected: protected:
std::filesystem::path unitTestData = getUnitTestData() / "derivation" / "ia"; std::filesystem::path unitTestData = getUnitTestData() / "derivation" / "ia";
@ -32,6 +34,33 @@ public:
* to worry about race conditions if the tests run concurrently. * to worry about race conditions if the tests run concurrently.
*/ */
ExperimentalFeatureSettings mockXpSettings; ExperimentalFeatureSettings mockXpSettings;
/**
* Helper function to test getRequiredSystemFeatures for a given derivation file
*/
void testRequiredSystemFeatures(const std::string & fileName, const StringSet & expectedFeatures)
{
this->readTest(fileName, [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedFeatures);
});
}
/**
* Helper function to test DerivationOptions parsing and comparison
*/
void testDerivationOptions(
const std::string & fileName, const DerivationOptions & expected, const StringSet & expectedSystemFeatures)
{
this->readTest(fileName, [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options, expected);
EXPECT_EQ(options.getRequiredSystemFeatures(got), expectedSystemFeatures);
});
}
}; };
class CaDerivationAdvancedAttrsTest : public DerivationAdvancedAttrsTest class CaDerivationAdvancedAttrsTest : public DerivationAdvancedAttrsTest
@ -100,33 +129,35 @@ TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attribute
using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph); using ExportReferencesMap = decltype(DerivationOptions::exportReferencesGraph);
static const DerivationOptions advancedAttributes_defaults = {
.outputChecks =
DerivationOptions::OutputChecks{
.ignoreSelfRefs = true,
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.exportReferencesGraph = {},
.additionalSandboxProfile = "",
.noChroot = false,
.impureHostDeps = {},
.impureEnvVars = {},
.allowLocalNetworking = false,
.requiredSystemFeatures = {},
.preferLocalBuild = false,
.allowSubstitutes = true,
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
{ {
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(!got.structuredAttrs); EXPECT_TRUE(!got.structuredAttrs);
EXPECT_EQ(options.additionalSandboxProfile, ""); EXPECT_EQ(options, advancedAttributes_defaults);
EXPECT_EQ(options.noChroot, false);
EXPECT_EQ(options.impureHostDeps, StringSet{});
EXPECT_EQ(options.impureEnvVars, StringSet{});
EXPECT_EQ(options.allowLocalNetworking, false);
EXPECT_EQ(options.exportReferencesGraph, ExportReferencesMap{});
{
auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
ASSERT_TRUE(checksForAllOutputs_ != nullptr);
auto & checksForAllOutputs = *checksForAllOutputs_;
EXPECT_EQ(checksForAllOutputs.allowedReferences, std::nullopt);
EXPECT_EQ(checksForAllOutputs.allowedRequisites, std::nullopt);
EXPECT_EQ(checksForAllOutputs.disallowedReferences, StringSet{});
EXPECT_EQ(checksForAllOutputs.disallowedRequisites, StringSet{});
}
EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.substitutesAllowed(), true); EXPECT_EQ(options.substitutesAllowed(), true);
@ -136,152 +167,124 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_defaults) TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_defaults)
{ {
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) { testRequiredSystemFeatures("advanced-attributes-defaults.drv", {});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
});
}; };
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults) TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults)
{ {
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) { testRequiredSystemFeatures("advanced-attributes-defaults.drv", {"ca-derivations"});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
});
}; };
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
{ {
DerivationOptions expected = {
.outputChecks =
DerivationOptions::OutputChecks{
.ignoreSelfRefs = true,
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.additionalSandboxProfile = "sandcastle",
.noChroot = true,
.impureHostDeps = {"/usr/bin/ditto"},
.impureEnvVars = {"UNICORN"},
.allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
.preferLocalBuild = true,
.allowSubstitutes = false,
};
this->readTest("advanced-attributes.drv", [&](auto encoded) { this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(!got.structuredAttrs); EXPECT_TRUE(!got.structuredAttrs);
EXPECT_EQ(options.additionalSandboxProfile, "sandcastle"); // Reset fields that vary between test cases to enable whole-object comparison
EXPECT_EQ(options.noChroot, true); options.outputChecks = DerivationOptions::OutputChecks{.ignoreSelfRefs = true};
EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"}); options.exportReferencesGraph = {};
EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
EXPECT_EQ(options.allowLocalNetworking, true); EXPECT_EQ(options, expected);
EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.substitutesAllowed(), false); EXPECT_EQ(options.substitutesAllowed(), false);
EXPECT_EQ(options.useUidRange(got), true); EXPECT_EQ(options.useUidRange(got), true);
}); });
}; };
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes) DerivationOptions advancedAttributes_ia = {
.outputChecks =
DerivationOptions::OutputChecks{
.ignoreSelfRefs = true,
.allowedReferences = StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"},
.disallowedReferences = StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"},
.allowedRequisites = StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"},
.disallowedRequisites = StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"},
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.exportReferencesGraph{
{"refs1", {"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}},
{"refs2", {"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}},
},
.additionalSandboxProfile = "sandcastle",
.noChroot = true,
.impureHostDeps = {"/usr/bin/ditto"},
.impureEnvVars = {"UNICORN"},
.allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
.preferLocalBuild = true,
.allowSubstitutes = false,
};
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_ia)
{ {
this->readTest("advanced-attributes.drv", [&](auto encoded) { testDerivationOptions("advanced-attributes.drv", advancedAttributes_ia, {"rainbow", "uid-range"});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); };
auto drvPath = writeDerivation(*this->store, got, NoRepair, true); DerivationOptions advancedAttributes_ca = {
.outputChecks =
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); DerivationOptions::OutputChecks{
.ignoreSelfRefs = true,
EXPECT_EQ( .allowedReferences = StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"},
options.exportReferencesGraph, .disallowedReferences = StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"},
(ExportReferencesMap{ .allowedRequisites = StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"},
{ .disallowedRequisites = StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"},
"refs1", },
{ .unsafeDiscardReferences = {},
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo", .passAsFile = {},
}, .exportReferencesGraph{
}, {"refs1", {"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"}},
{ {"refs2", {"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}},
"refs2", },
{ .additionalSandboxProfile = "sandcastle",
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv", .noChroot = true,
}, .impureHostDeps = {"/usr/bin/ditto"},
}, .impureEnvVars = {"UNICORN"},
})); .allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
{ .preferLocalBuild = true,
auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks); .allowSubstitutes = false,
ASSERT_TRUE(checksForAllOutputs_ != nullptr);
auto & checksForAllOutputs = *checksForAllOutputs_;
EXPECT_EQ(
checksForAllOutputs.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"});
EXPECT_EQ(
checksForAllOutputs.allowedRequisites,
StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"});
EXPECT_EQ(
checksForAllOutputs.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
EXPECT_EQ(
checksForAllOutputs.disallowedRequisites,
StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
}
StringSet systemFeatures{"rainbow", "uid-range"};
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
}; };
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes) TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes)
{ {
this->readTest("advanced-attributes.drv", [&](auto encoded) { testDerivationOptions("advanced-attributes.drv", advancedAttributes_ca, {"rainbow", "uid-range", "ca-derivations"});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); };
auto drvPath = writeDerivation(*this->store, got, NoRepair, true); DerivationOptions advancedAttributes_structuredAttrs_defaults = {
.outputChecks = std::map<std::string, DerivationOptions::OutputChecks>{},
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); .unsafeDiscardReferences = {},
.passAsFile = {},
EXPECT_EQ( .exportReferencesGraph = {},
options.exportReferencesGraph, .additionalSandboxProfile = "",
(ExportReferencesMap{ .noChroot = false,
{ .impureHostDeps = {},
"refs1", .impureEnvVars = {},
{ .allowLocalNetworking = false,
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9", .requiredSystemFeatures = {},
}, .preferLocalBuild = false,
}, .allowSubstitutes = true,
{
"refs2",
{
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
},
},
}));
{
auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
ASSERT_TRUE(checksForAllOutputs_ != nullptr);
auto & checksForAllOutputs = *checksForAllOutputs_;
EXPECT_EQ(
checksForAllOutputs.allowedReferences,
StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
EXPECT_EQ(
checksForAllOutputs.allowedRequisites,
StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
EXPECT_EQ(
checksForAllOutputs.disallowedReferences,
StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
EXPECT_EQ(
checksForAllOutputs.disallowedRequisites,
StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
}
StringSet systemFeatures{"rainbow", "uid-range"};
systemFeatures.insert("ca-derivations");
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
}; };
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_defaults) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_defaults)
@ -289,26 +292,11 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(got.structuredAttrs); EXPECT_TRUE(got.structuredAttrs);
EXPECT_EQ(options.additionalSandboxProfile, ""); EXPECT_EQ(options, advancedAttributes_structuredAttrs_defaults);
EXPECT_EQ(options.noChroot, false);
EXPECT_EQ(options.impureHostDeps, StringSet{});
EXPECT_EQ(options.impureEnvVars, StringSet{});
EXPECT_EQ(options.allowLocalNetworking, false);
EXPECT_EQ(options.exportReferencesGraph, ExportReferencesMap{});
{
auto * checksPerOutput_ = std::get_if<1>(&options.outputChecks);
ASSERT_TRUE(checksPerOutput_ != nullptr);
auto & checksPerOutput = *checksPerOutput_;
EXPECT_EQ(checksPerOutput.size(), 0u);
}
EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
@ -319,55 +307,61 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_d
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults) TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{ {
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { testRequiredSystemFeatures("advanced-attributes-structured-attrs-defaults.drv", {});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
});
}; };
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults) TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{ {
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { testRequiredSystemFeatures("advanced-attributes-structured-attrs-defaults.drv", {"ca-derivations"});
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
});
}; };
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
{ {
DerivationOptions expected = {
.outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{
{"dev",
DerivationOptions::OutputChecks{
.maxSize = 789,
.maxClosureSize = 5909,
}},
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.exportReferencesGraph = {},
.additionalSandboxProfile = "sandcastle",
.noChroot = true,
.impureHostDeps = {"/usr/bin/ditto"},
.impureEnvVars = {"UNICORN"},
.allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
.preferLocalBuild = true,
.allowSubstitutes = false,
};
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs); DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_TRUE(got.structuredAttrs); EXPECT_TRUE(got.structuredAttrs);
EXPECT_EQ(options.additionalSandboxProfile, "sandcastle"); // Reset fields that vary between test cases to enable whole-object comparison
EXPECT_EQ(options.noChroot, true);
EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"});
EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
EXPECT_EQ(options.allowLocalNetworking, true);
{ {
auto output_ = get(std::get<1>(options.outputChecks), "dev"); // Delete all keys but "dev" in options.outputChecks
ASSERT_TRUE(output_); auto * outputChecksMapP =
auto & output = *output_; std::get_if<std::map<std::string, DerivationOptions::OutputChecks>>(&options.outputChecks);
ASSERT_TRUE(outputChecksMapP);
EXPECT_EQ(output.maxSize, 789); auto & outputChecksMap = *outputChecksMapP;
EXPECT_EQ(output.maxClosureSize, 5909); auto devEntry = outputChecksMap.find("dev");
ASSERT_TRUE(devEntry != outputChecksMap.end());
auto devChecks = devEntry->second;
outputChecksMap.clear();
outputChecksMap.emplace("dev", std::move(devChecks));
} }
options.exportReferencesGraph = {};
EXPECT_EQ(options, expected);
EXPECT_EQ(options.canBuildLocally(*this->store, got), false); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
@ -376,112 +370,109 @@ TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
}); });
}; };
DerivationOptions advancedAttributes_structuredAttrs_ia = {
.outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{
{"out",
DerivationOptions::OutputChecks{
.allowedReferences = StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"},
.allowedRequisites = StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"},
}},
{"bin",
DerivationOptions::OutputChecks{
.disallowedReferences = StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"},
.disallowedRequisites = StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"},
}},
{"dev",
DerivationOptions::OutputChecks{
.maxSize = 789,
.maxClosureSize = 5909,
}},
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.exportReferencesGraph =
{
{"refs1", {"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}},
{"refs2", {"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv"}},
},
.additionalSandboxProfile = "sandcastle",
.noChroot = true,
.impureHostDeps = {"/usr/bin/ditto"},
.impureEnvVars = {"UNICORN"},
.allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
.preferLocalBuild = true,
.allowSubstitutes = false,
};
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{ {
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { testDerivationOptions(
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); "advanced-attributes-structured-attrs.drv", advancedAttributes_structuredAttrs_ia, {"rainbow", "uid-range"});
};
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(
options.exportReferencesGraph,
(ExportReferencesMap{
{
"refs1",
{
"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo",
},
},
{
"refs2",
{
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",
},
},
}));
DerivationOptions advancedAttributes_structuredAttrs_ca = {
.outputChecks =
std::map<std::string, DerivationOptions::OutputChecks>{
{"out",
DerivationOptions::OutputChecks{
.allowedReferences = StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"},
.allowedRequisites = StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"},
}},
{"bin",
DerivationOptions::OutputChecks{
.disallowedReferences = StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"},
.disallowedRequisites = StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"},
}},
{"dev",
DerivationOptions::OutputChecks{
.maxSize = 789,
.maxClosureSize = 5909,
}},
},
.unsafeDiscardReferences = {},
.passAsFile = {},
.exportReferencesGraph =
{ {
{ {"refs1", {"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"}},
auto output_ = get(std::get<1>(options.outputChecks), "out"); {"refs2", {"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv"}},
ASSERT_TRUE(output_); },
auto & output = *output_; .additionalSandboxProfile = "sandcastle",
.noChroot = true,
EXPECT_EQ(output.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"}); .impureHostDeps = {"/usr/bin/ditto"},
EXPECT_EQ(output.allowedRequisites, StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"}); .impureEnvVars = {"UNICORN"},
} .allowLocalNetworking = true,
.requiredSystemFeatures = {"rainbow", "uid-range"},
{ .preferLocalBuild = true,
auto output_ = get(std::get<1>(options.outputChecks), "bin"); .allowSubstitutes = false,
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
EXPECT_EQ(
output.disallowedRequisites, StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
}
}
StringSet systemFeatures{"rainbow", "uid-range"};
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
}; };
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs) TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{ {
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { testDerivationOptions(
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings); "advanced-attributes-structured-attrs.drv",
advancedAttributes_structuredAttrs_ca,
auto drvPath = writeDerivation(*this->store, got, NoRepair, true); {"rainbow", "uid-range", "ca-derivations"});
DerivationOptions options = DerivationOptions::fromStructuredAttrs(got.env, got.structuredAttrs);
EXPECT_EQ(
options.exportReferencesGraph,
(ExportReferencesMap{
{
"refs1",
{
"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9",
},
},
{
"refs2",
{
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",
},
},
}));
{
{
auto output_ = get(std::get<1>(options.outputChecks), "out");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.allowedReferences, StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
EXPECT_EQ(output.allowedRequisites, StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
}
{
auto output_ = get(std::get<1>(options.outputChecks), "bin");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(
output.disallowedReferences, StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
EXPECT_EQ(
output.disallowedRequisites, StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
}
}
StringSet systemFeatures{"rainbow", "uid-range"};
systemFeatures.insert("ca-derivations");
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
}; };
#define TEST_JSON_OPTIONS(FIXUTURE, VAR, VAR2) \
TEST_F(FIXUTURE, DerivationOptions_##VAR##_from_json) \
{ \
this->JsonCharacterizationTest<DerivationOptions>::readJsonTest(#VAR, advancedAttributes_##VAR2); \
} \
TEST_F(FIXUTURE, DerivationOptions_##VAR##_to_json) \
{ \
this->JsonCharacterizationTest<DerivationOptions>::writeJsonTest(#VAR, advancedAttributes_##VAR2); \
}
TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, defaults, defaults)
TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, all_set, ia)
TEST_JSON_OPTIONS(CaDerivationAdvancedAttrsTest, all_set, ca)
TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, structuredAttrs_defaults, structuredAttrs_defaults)
TEST_JSON_OPTIONS(DerivationAdvancedAttrsTest, structuredAttrs_all_set, structuredAttrs_ia)
TEST_JSON_OPTIONS(CaDerivationAdvancedAttrsTest, structuredAttrs_all_set, structuredAttrs_ca)
#undef TEST_JSON_OPTIONS
} // namespace nix } // namespace nix

View file

@ -176,13 +176,26 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
return {}; return {};
}; };
checks.allowedReferences = get_("allowedReferences"); res.insert_or_assign(
checks.allowedRequisites = get_("allowedRequisites"); outputName,
checks.disallowedReferences = get_("disallowedReferences").value_or(StringSet{}); OutputChecks{
checks.disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{}); .maxSize = [&]() -> std::optional<uint64_t> {
; if (auto maxSize = get(output, "maxSize"))
return maxSize->get<uint64_t>();
res.insert_or_assign(outputName, std::move(checks)); else
return std::nullopt;
}(),
.maxClosureSize = [&]() -> std::optional<uint64_t> {
if (auto maxClosureSize = get(output, "maxClosureSize"))
return maxClosureSize->get<uint64_t>();
else
return std::nullopt;
}(),
.allowedReferences = get_("allowedReferences"),
.disallowedReferences = get_("disallowedReferences").value_or(StringSet{}),
.allowedRequisites = get_("allowedRequisites"),
.disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{}),
});
} }
} }
return res; return res;
@ -364,6 +377,7 @@ DerivationOptions adl_serializer<DerivationOptions>::from_json(const json & json
.unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"), .unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"),
.passAsFile = getStringSet(valueAt(json, "passAsFile")), .passAsFile = getStringSet(valueAt(json, "passAsFile")),
.exportReferencesGraph = getMap<StringSet>(getObject(valueAt(json, "exportReferencesGraph")), getStringSet),
.additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")), .additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")),
.noChroot = getBoolean(valueAt(json, "noChroot")), .noChroot = getBoolean(valueAt(json, "noChroot")),
@ -396,6 +410,7 @@ void adl_serializer<DerivationOptions>::to_json(json & json, const DerivationOpt
json["unsafeDiscardReferences"] = o.unsafeDiscardReferences; json["unsafeDiscardReferences"] = o.unsafeDiscardReferences;
json["passAsFile"] = o.passAsFile; json["passAsFile"] = o.passAsFile;
json["exportReferencesGraph"] = o.exportReferencesGraph;
json["additionalSandboxProfile"] = o.additionalSandboxProfile; json["additionalSandboxProfile"] = o.additionalSandboxProfile;
json["noChroot"] = o.noChroot; json["noChroot"] = o.noChroot;
@ -423,6 +438,8 @@ DerivationOptions::OutputChecks adl_serializer<DerivationOptions::OutputChecks>:
return { return {
.ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")), .ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")),
.maxSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxSize"))),
.maxClosureSize = ptrToOwned<uint64_t>(getNullable(valueAt(json, "maxClosureSize"))),
.allowedReferences = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedReferences"))), .allowedReferences = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedReferences"))),
.disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")), .disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")),
.allowedRequisites = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedRequisites"))), .allowedRequisites = ptrToOwned<StringSet>(getNullable(valueAt(json, "allowedRequisites"))),
@ -433,6 +450,8 @@ DerivationOptions::OutputChecks adl_serializer<DerivationOptions::OutputChecks>:
void adl_serializer<DerivationOptions::OutputChecks>::to_json(json & json, const DerivationOptions::OutputChecks & c) void adl_serializer<DerivationOptions::OutputChecks>::to_json(json & json, const DerivationOptions::OutputChecks & c)
{ {
json["ignoreSelfRefs"] = c.ignoreSelfRefs; json["ignoreSelfRefs"] = c.ignoreSelfRefs;
json["maxSize"] = c.maxSize;
json["maxClosureSize"] = c.maxClosureSize;
json["allowedReferences"] = c.allowedReferences; json["allowedReferences"] = c.allowedReferences;
json["disallowedReferences"] = c.disallowedReferences; json["disallowedReferences"] = c.disallowedReferences;
json["allowedRequisites"] = c.allowedRequisites; json["allowedRequisites"] = c.allowedRequisites;

View file

@ -1404,7 +1404,7 @@ void adl_serializer<Derivation>::to_json(json & res, const Derivation & d)
{ {
auto & inputsList = res["inputSrcs"]; auto & inputsList = res["inputSrcs"];
inputsList = nlohmann::json ::array(); inputsList = nlohmann::json::array();
for (auto & input : d.inputSrcs) for (auto & input : d.inputSrcs)
inputsList.emplace_back(input); inputsList.emplace_back(input);
} }

View file

@ -59,6 +59,17 @@ auto getInteger(const nlohmann::json & value) -> std::enable_if_t<std::is_signed
throw Error("Out of range: JSON value '%s' cannot be casted to %d-bit integer", value.dump(), 8 * sizeof(T)); throw Error("Out of range: JSON value '%s' cannot be casted to %d-bit integer", value.dump(), 8 * sizeof(T));
} }
template<typename... Args>
std::map<std::string, Args...> getMap(const nlohmann::json::object_t & jsonObject, auto && f)
{
std::map<std::string, Args...> map;
for (const auto & [key, value] : jsonObject)
map.insert_or_assign(key, f(value));
return map;
}
const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value); const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value);
Strings getStringList(const nlohmann::json & value); Strings getStringList(const nlohmann::json & value);
StringMap getStringMap(const nlohmann::json & value); StringMap getStringMap(const nlohmann::json & value);

View file

@ -91,14 +91,7 @@ Strings getStringList(const nlohmann::json & value)
StringMap getStringMap(const nlohmann::json & value) StringMap getStringMap(const nlohmann::json & value)
{ {
auto & jsonObject = getObject(value); return getMap<std::string, std::less<>>(getObject(value), getString);
StringMap stringMap;
for (const auto & [key, value] : jsonObject)
stringMap[getString(key)] = getString(value);
return stringMap;
} }
StringSet getStringSet(const nlohmann::json & value) StringSet getStringSet(const nlohmann::json & value)