Skip to content

Commit 9791088

Browse files
committed
integration-tests: init
1 parent 330f4e4 commit 9791088

File tree

6 files changed

+265
-2
lines changed

6 files changed

+265
-2
lines changed

crane.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
let
2525
version = "0.1.0";
2626

27-
ignoredPaths = [ ".github" "target" "book" "nixos" ];
27+
ignoredPaths = [ ".github" "target" "book" "nixos" "integration-tests" ];
2828

2929
src = lib.cleanSourceWith {
3030
filter = name: type: !(type == "directory" && builtins.elem (baseNameOf name) ignoredPaths);

flake.nix

+12-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
craneLib = crane.mkLib pkgs;
2626
in pkgs.callPackage ./crane.nix { inherit craneLib; };
2727
in flake-utils.lib.eachSystem supportedSystems (system: let
28-
pkgs = import nixpkgs { inherit system; };
28+
pkgs = import nixpkgs {
29+
inherit system;
30+
overlays = [];
31+
};
2932
cranePkgs = makeCranePkgs pkgs;
3033

3134
inherit (pkgs) lib;
@@ -107,6 +110,14 @@
107110
internal = {
108111
inherit (cranePkgs) attic-tests cargoArtifacts;
109112
};
113+
114+
checks = lib.optionalAttrs pkgs.stdenv.isLinux (import ./integration-tests {
115+
pkgs = import nixpkgs {
116+
inherit system;
117+
overlays = [ self.overlays.default ];
118+
};
119+
flake = self;
120+
});
110121
}) // {
111122
overlays = {
112123
default = final: prev: let

integration-tests/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# End-to-End Tests
2+
3+
This directory contains some end-to-end tests for Attic.

integration-tests/basic/default.nix

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
{ pkgs, lib, config, flake, attic, ... }:
2+
let
3+
inherit (lib) types;
4+
5+
serverConfigFile = config.nodes.server.services.atticd.configFile;
6+
7+
cmd = {
8+
atticadm = ". /etc/atticd.env && export ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64 && atticadm -f ${serverConfigFile}";
9+
atticd = ". /etc/atticd.env && export ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64 && atticd -f ${serverConfigFile}";
10+
};
11+
12+
testDrv = pkgs.writeText "test.nix" ''
13+
#!/bin/sh
14+
/*/sh -c "echo hello > $out"; exit 0; */
15+
derivation {
16+
name = "hello.txt";
17+
builder = ./test.nix;
18+
system = builtins.currentSystem;
19+
preferLocalBuild = true;
20+
allowSubstitutes = false;
21+
}
22+
'';
23+
24+
databaseModules = {
25+
sqlite = {};
26+
postgres = {
27+
server = {
28+
services.postgresql = {
29+
enable = true;
30+
ensureDatabases = [ "attic" ];
31+
ensureUsers = [
32+
{
33+
name = "atticd";
34+
ensurePermissions = {
35+
"DATABASE attic" = "ALL PRIVILEGES";
36+
};
37+
}
38+
39+
# For testing only - Don't actually do this
40+
{
41+
name = "root";
42+
ensureClauses = {
43+
superuser = true;
44+
};
45+
}
46+
];
47+
};
48+
49+
services.atticd.settings = {
50+
database.url = "postgresql:///attic?host=/run/postgresql";
51+
};
52+
};
53+
};
54+
};
55+
56+
storageModules = {
57+
local = {};
58+
minio = let
59+
accessKey = "legit";
60+
secretKey = "111-1111111";
61+
in {
62+
server = {
63+
services.minio = {
64+
enable = true;
65+
rootCredentialsFile = "/etc/minio.env";
66+
};
67+
68+
# For testing only - Don't actually do this
69+
environment.etc."minio.env".text = ''
70+
MINIO_ROOT_USER=${accessKey}
71+
MINIO_ROOT_PASSWORD=${secretKey}
72+
'';
73+
74+
networking.firewall.allowedTCPPorts = [ 9000 ];
75+
76+
services.atticd.settings = {
77+
storage = {
78+
type = "s3";
79+
endpoint = "http://server:9000";
80+
region = "us-east-1";
81+
bucket = "attic";
82+
credentials = {
83+
access_key_id = accessKey;
84+
secret_access_key = secretKey;
85+
};
86+
};
87+
};
88+
};
89+
testScript = ''
90+
server.succeed("mkdir /var/lib/minio/data/attic")
91+
server.succeed("chown minio: /var/lib/minio/data/attic")
92+
client.wait_until_succeeds("curl http://server:9000", timeout=10)
93+
'';
94+
};
95+
};
96+
in {
97+
options = {
98+
database = lib.mkOption {
99+
type = types.enum [ "sqlite" "postgres" ];
100+
default = "sqlite";
101+
};
102+
storage = lib.mkOption {
103+
type = types.enum [ "local" "minio" ];
104+
default = "local";
105+
};
106+
};
107+
108+
config = {
109+
name = "basic-${config.database}-${config.storage}";
110+
111+
nodes = {
112+
server = {
113+
imports = [
114+
flake.nixosModules.atticd
115+
(databaseModules.${config.database}.server or {})
116+
(storageModules.${config.storage}.server or {})
117+
];
118+
119+
# For testing only - Don't actually do this
120+
environment.etc."atticd.env".text = ''
121+
ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64="dGVzdCBzZWNyZXQ="
122+
'';
123+
124+
services.atticd = {
125+
enable = true;
126+
credentialsFile = "/etc/atticd.env";
127+
settings = {
128+
listen = "[::]:8080";
129+
};
130+
};
131+
132+
networking.firewall.allowedTCPPorts = [ 8080 ];
133+
};
134+
135+
client = {
136+
environment.systemPackages = [ pkgs.attic ];
137+
};
138+
};
139+
140+
testScript = ''
141+
import time
142+
143+
start_all()
144+
145+
${databaseModules.${config.database}.testScript or ""}
146+
${storageModules.${config.storage}.testScript or ""}
147+
148+
server.wait_for_unit('atticd.service')
149+
client.wait_until_succeeds("curl -sL http://server:8080", timeout=10)
150+
151+
root_token = server.succeed("${cmd.atticadm} make-token --sub 'e2e-root' --validity '1 month' --push '*' --pull '*' --delete '*' --create-cache '*' --destroy-cache '*' --configure-cache '*' --configure-cache-retention '*'")
152+
readonly_token = server.succeed("${cmd.atticadm} make-token --sub 'e2e-root' --validity '1 month' --pull 'test'")
153+
154+
client.succeed(f"attic login --set-default root http://server:8080 {root_token}")
155+
client.succeed(f"attic login readonly http://server:8080 {readonly_token}")
156+
client.succeed("attic login anon http://server:8080")
157+
158+
# TODO: Make sure the correct status codes are returned
159+
# (i.e., 500s shouldn't pass the "should fail" tests)
160+
161+
with subtest("Check that we can create a cache"):
162+
client.succeed("attic cache create test")
163+
164+
with subtest("Check that we can push a path"):
165+
client.succeed("cat ${testDrv} >test.nix && chmod +x test.nix")
166+
test_file = client.succeed("nix-build --no-out-link test.nix")
167+
test_file_hash = test_file.removeprefix("/nix/store/")[:32]
168+
169+
client.succeed(f"attic push test {test_file}")
170+
client.succeed(f"nix-store --delete {test_file}")
171+
client.fail(f"grep hello {test_file}")
172+
173+
with subtest("Check that we can pull a path"):
174+
client.succeed("attic use readonly:test")
175+
client.succeed(f"nix-store -r {test_file}")
176+
client.succeed(f"grep hello {test_file}")
177+
178+
with subtest("Check that we cannot push without required permissions"):
179+
client.fail(f"attic push readonly:test {test_file}")
180+
client.fail(f"attic push anon:test {test_file} 2>&1")
181+
182+
with subtest("Check that we can make the cache public"):
183+
client.fail("curl -sL --fail-with-body http://server:8080/test/nix-cache-info")
184+
client.fail(f"curl -sL --fail-with-body http://server:8080/test/{test_file_hash}.narinfo")
185+
client.succeed("attic cache configure test --public")
186+
client.succeed("curl -sL --fail-with-body http://server:8080/test/nix-cache-info")
187+
client.succeed(f"curl -sL --fail-with-body http://server:8080/test/{test_file_hash}.narinfo")
188+
189+
with subtest("Check that we can trigger garbage collection"):
190+
test_file_hash = test_file.removeprefix("/nix/store/")[:32]
191+
client.succeed(f"curl -sL --fail-with-body http://server:8080/test/{test_file_hash}.narinfo")
192+
client.succeed("attic cache configure test --retention-period 1s")
193+
time.sleep(2)
194+
server.succeed("${cmd.atticd} --mode garbage-collector-once")
195+
client.fail(f"curl -sL --fail-with-body http://server:8080/test/{test_file_hash}.narinfo")
196+
197+
with subtest("Check that we can destroy the cache"):
198+
client.succeed("attic cache info test")
199+
client.succeed("attic cache destroy --no-confirm test")
200+
client.fail("attic cache info test")
201+
client.fail("curl -sL --fail-with-body http://server:8080/test/nix-cache-info")
202+
'';
203+
};
204+
}

integration-tests/default.nix

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{ pkgs ? import ./nixpkgs.nix
2+
, flake ? (import ../flake-compat.nix).defaultNix
3+
}:
4+
5+
let
6+
inherit (pkgs) lib;
7+
8+
nixosLib = import (pkgs.path + "/nixos/lib") { };
9+
10+
runTest = module: (nixosLib.evalTest ({ config, ... }: {
11+
imports = [
12+
module
13+
{
14+
hostPkgs = pkgs;
15+
_module.args.flake = flake;
16+
}
17+
];
18+
result = config.test;
19+
})).config.result;
20+
21+
basicTests = let
22+
matrix = {
23+
database = [ "sqlite" "postgres" ];
24+
storage = [ "local" "minio" ];
25+
};
26+
in builtins.listToAttrs (map (e: {
27+
name = "basic-${e.database}-${e.storage}";
28+
value = runTest {
29+
imports = [
30+
./basic
31+
{
32+
inherit (e) database storage;
33+
}
34+
];
35+
};
36+
}) (lib.cartesianProductOfSets matrix));
37+
in {
38+
} // basicTests

integration-tests/nixpkgs.nix

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let
2+
flake = (import ../flake-compat.nix).defaultNix;
3+
in import flake.inputs.nixpkgs.outPath {
4+
overlays = [
5+
flake.overlays.default
6+
];
7+
}

0 commit comments

Comments
 (0)