at 25.11-pre 18 kB view raw
1# This test runs gitlab and performs the following tests: 2# - Creating users 3# - Pushing commits 4# - over the API 5# - over SSH 6# - Creating Merge Requests and merging them 7# - Opening and closing issues. 8# - Downloading repository archives as tar.gz and tar.bz2 9# Run with 10# [nixpkgs]$ nix-build -A nixosTests.gitlab 11 12{ pkgs, lib, ... }: 13 14let 15 inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; 16 initialRootPassword = "notproduction"; 17 rootProjectId = "2"; 18 19 aliceUsername = "alice"; 20 aliceUserId = "2"; 21 alicePassword = "R5twyCgU0uXC71wT9BBTCqLs6HFZ7h3L"; 22 aliceProjectId = "1"; 23 aliceProjectName = "test-alice"; 24 25 bobUsername = "bob"; 26 bobUserId = "3"; 27 bobPassword = "XwkkBbl2SiIwabQzgcoaTbhsotijEEtF"; 28 bobProjectId = "2"; 29in 30{ 31 name = "gitlab"; 32 meta.maintainers = with lib.maintainers; [ 33 globin 34 yayayayaka 35 ]; 36 37 nodes = { 38 gitlab = 39 { ... }: 40 { 41 imports = [ common/user-account.nix ]; 42 43 environment.systemPackages = with pkgs; [ git ]; 44 45 networking.hosts."127.0.0.1" = [ 46 "registry.localhost" 47 "pages.localhost" 48 ]; 49 virtualisation.memorySize = 6144; 50 virtualisation.cores = 4; 51 virtualisation.useNixStoreImage = true; 52 virtualisation.writableStore = false; 53 54 systemd.services.gitlab.serviceConfig.Restart = lib.mkForce "no"; 55 systemd.services.gitlab-workhorse.serviceConfig.Restart = lib.mkForce "no"; 56 systemd.services.gitaly.serviceConfig.Restart = lib.mkForce "no"; 57 systemd.services.gitlab-sidekiq.serviceConfig.Restart = lib.mkForce "no"; 58 59 services.nginx = { 60 enable = true; 61 recommendedProxySettings = true; 62 virtualHosts = { 63 localhost = { 64 locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket"; 65 }; 66 "pages.localhost" = { 67 locations."/".proxyPass = "http://localhost:8090"; 68 }; 69 "registry.localhost" = { 70 locations."/".proxyPass = "http://localhost:4567"; 71 }; 72 }; 73 }; 74 75 services.openssh.enable = true; 76 77 services.dovecot2 = { 78 enable = true; 79 enableImap = true; 80 }; 81 82 systemd.services.gitlab-backup.environment.BACKUP = "dump"; 83 84 services.gitlab = { 85 enable = true; 86 databasePasswordFile = pkgs.writeText "dbPassword" "xo0daiF4"; 87 initialRootPasswordFile = pkgs.writeText "rootPassword" initialRootPassword; 88 smtp.enable = true; 89 pages = { 90 enable = true; 91 settings.pages-domain = "pages.localhost"; 92 }; 93 extraConfig = { 94 incoming_email = { 95 enabled = true; 96 mailbox = "inbox"; 97 address = "alice@localhost"; 98 user = "alice"; 99 password = "foobar"; 100 host = "localhost"; 101 port = 143; 102 }; 103 }; 104 secrets = { 105 secretFile = pkgs.writeText "secret" "Aig5zaic"; 106 otpFile = pkgs.writeText "otpsecret" "Riew9mue"; 107 dbFile = pkgs.writeText "dbsecret" "we2quaeZ"; 108 jwsFile = pkgs.runCommand "oidcKeyBase" { } "${pkgs.openssl}/bin/openssl genrsa 2048 > $out"; 109 activeRecordPrimaryKeyFile = pkgs.writeText "arprimary" "vsaYPZjTRxcbG7W6gNr95AwBmzFUd4Eu"; 110 activeRecordDeterministicKeyFile = pkgs.writeText "ardeterministic" "kQarv9wb2JVP7XzLTh5f6DFcMHms4nEC"; 111 activeRecordSaltFile = pkgs.writeText "arsalt" "QkgR9CfFU3MXEWGqa7LbP24AntK5ZeYw"; 112 }; 113 114 registry = { 115 enable = true; 116 certFile = "/var/lib/gitlab/registry_auth_cert"; 117 keyFile = "/var/lib/gitlab/registry_auth_key"; 118 externalAddress = "registry.localhost"; 119 externalPort = 443; 120 }; 121 122 # reduce memory usage 123 sidekiq.concurrency = 1; 124 puma.workers = 2; 125 }; 126 }; 127 }; 128 129 testScript = 130 { nodes, ... }: 131 let 132 auth = pkgs.writeText "auth.json" ( 133 builtins.toJSON { 134 grant_type = "password"; 135 username = "root"; 136 password = initialRootPassword; 137 } 138 ); 139 140 createUserAlice = pkgs.writeText "create-user-alice.json" ( 141 builtins.toJSON rec { 142 username = aliceUsername; 143 name = username; 144 email = "alice@localhost"; 145 password = alicePassword; 146 skip_confirmation = true; 147 } 148 ); 149 150 createUserBob = pkgs.writeText "create-user-bob.json" ( 151 builtins.toJSON rec { 152 username = bobUsername; 153 name = username; 154 email = "bob@localhost"; 155 password = bobPassword; 156 skip_confirmation = true; 157 } 158 ); 159 160 aliceAuth = pkgs.writeText "alice-auth.json" ( 161 builtins.toJSON { 162 grant_type = "password"; 163 username = aliceUsername; 164 password = alicePassword; 165 } 166 ); 167 168 bobAuth = pkgs.writeText "bob-auth.json" ( 169 builtins.toJSON { 170 grant_type = "password"; 171 username = bobUsername; 172 password = bobPassword; 173 } 174 ); 175 176 aliceAddSSHKey = pkgs.writeText "alice-add-ssh-key.json" ( 177 builtins.toJSON { 178 id = aliceUserId; 179 title = "snakeoil@nixos"; 180 key = snakeOilPublicKey; 181 } 182 ); 183 184 createProjectAlice = pkgs.writeText "create-project-alice.json" ( 185 builtins.toJSON { 186 name = aliceProjectName; 187 visibility = "public"; 188 } 189 ); 190 191 putFile = pkgs.writeText "put-file.json" ( 192 builtins.toJSON { 193 branch = "master"; 194 author_email = "author@example.com"; 195 author_name = "Firstname Lastname"; 196 content = "some content"; 197 commit_message = "create a new file"; 198 } 199 ); 200 201 mergeRequest = pkgs.writeText "merge-request.json" ( 202 builtins.toJSON { 203 id = bobProjectId; 204 target_project_id = aliceProjectId; 205 source_branch = "master"; 206 target_branch = "master"; 207 title = "Add some other file"; 208 } 209 ); 210 211 newIssue = pkgs.writeText "new-issue.json" ( 212 builtins.toJSON { 213 title = "useful issue title"; 214 } 215 ); 216 217 closeIssue = pkgs.writeText "close-issue.json" ( 218 builtins.toJSON { 219 issue_iid = 1; 220 state_event = "close"; 221 } 222 ); 223 224 # Wait for all GitLab services to be fully started. 225 waitForServices = '' 226 gitlab.wait_for_unit("gitaly.service") 227 gitlab.wait_for_unit("gitlab-workhorse.service") 228 gitlab.wait_for_unit("gitlab-mailroom.service") 229 gitlab.wait_for_unit("gitlab.service") 230 gitlab.wait_for_unit("gitlab-pages.service") 231 gitlab.wait_for_unit("gitlab-sidekiq.service") 232 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/tmp/sockets/gitlab.socket") 233 gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in") 234 gitlab.wait_for_unit("docker-registry.service") 235 ''; 236 237 # The actual test of GitLab. Only push data to GitLab if 238 # `doSetup` is is true. 239 test = 240 doSetup: 241 '' 242 GIT_SSH_COMMAND = "ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null" 243 244 gitlab.succeed( 245 "curl -isSf http://gitlab | grep -i location | grep http://gitlab/users/sign_in" 246 ) 247 gitlab.succeed( 248 "${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2" 249 ) 250 gitlab.succeed( 251 "echo \"Authorization: Bearer $(curl -X POST -H 'Content-Type: application/json' -d @${auth} http://gitlab/oauth/token | ${pkgs.jq}/bin/jq -r '.access_token')\" >/tmp/headers" 252 ) 253 '' 254 + lib.optionalString doSetup '' 255 with subtest("Create user Alice"): 256 gitlab.succeed( 257 """[ "$(curl -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${createUserAlice} http://gitlab/api/v4/users)" = "201" ]""" 258 ) 259 gitlab.succeed( 260 "echo \"Authorization: Bearer $(curl -X POST -H 'Content-Type: application/json' -d @${aliceAuth} http://gitlab/oauth/token | ${pkgs.jq}/bin/jq -r '.access_token')\" >/tmp/headers-alice" 261 ) 262 263 with subtest("Create user Bob"): 264 gitlab.succeed( 265 """ [ "$(curl -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -H @/tmp/headers -d @${createUserBob} http://gitlab/api/v4/users)" = "201" ]""" 266 ) 267 gitlab.succeed( 268 "echo \"Authorization: Bearer $(curl -X POST -H 'Content-Type: application/json' -d @${bobAuth} http://gitlab/oauth/token | ${pkgs.jq}/bin/jq -r '.access_token')\" >/tmp/headers-bob" 269 ) 270 271 with subtest("Setup Git and SSH for Alice"): 272 gitlab.succeed("git config --global user.name Alice") 273 gitlab.succeed("git config --global user.email alice@nixos.invalid") 274 gitlab.succeed("mkdir -p -m 700 /root/.ssh") 275 gitlab.succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa") 276 gitlab.succeed("chmod 600 /root/.ssh/id_ecdsa") 277 gitlab.succeed( 278 """ 279 [ "$(curl \ 280 -o /dev/null \ 281 -w '%{http_code}' \ 282 -X POST \ 283 -H 'Content-Type: application/json' \ 284 -H @/tmp/headers-alice -d @${aliceAddSSHKey} \ 285 http://gitlab/api/v4/user/keys)" = "201" ] 286 """ 287 ) 288 289 with subtest("Create a new repository"): 290 # Alice creates a new repository 291 gitlab.succeed( 292 """ 293 [ "$(curl \ 294 -o /dev/null \ 295 -w '%{http_code}' \ 296 -X POST \ 297 -H 'Content-Type: application/json' \ 298 -H @/tmp/headers-alice \ 299 -d @${createProjectAlice} \ 300 http://gitlab/api/v4/projects)" = "201" ] 301 """ 302 ) 303 304 # Alice commits an initial commit 305 gitlab.succeed( 306 """ 307 [ "$(curl \ 308 -o /dev/null \ 309 -w '%{http_code}' \ 310 -X POST \ 311 -H 'Content-Type: application/json' \ 312 -H @/tmp/headers-alice \ 313 -d @${putFile} \ 314 http://gitlab/api/v4/projects/${aliceProjectId}/repository/files/some-file.txt)" = "201" ]""" 315 ) 316 317 with subtest("git clone over HTTP"): 318 gitlab.succeed( 319 """git clone http://gitlab/alice/${aliceProjectName}.git clone-via-http""", 320 timeout=15 321 ) 322 323 with subtest("Push a commit via SSH"): 324 gitlab.succeed( 325 f"""GIT_SSH_COMMAND="{GIT_SSH_COMMAND}" git clone gitlab@gitlab:alice/${aliceProjectName}.git""", 326 timeout=15 327 ) 328 gitlab.succeed( 329 """echo "a commit sent over ssh" > ${aliceProjectName}/ssh.txt""" 330 ) 331 gitlab.succeed( 332 """ 333 cd ${aliceProjectName} || exit 1 334 git add . 335 """ 336 ) 337 gitlab.succeed( 338 """ 339 cd ${aliceProjectName} || exit 1 340 git commit -m "Add a commit to be sent over ssh" 341 """ 342 ) 343 gitlab.succeed( 344 f""" 345 cd ${aliceProjectName} || exit 1 346 GIT_SSH_COMMAND="{GIT_SSH_COMMAND}" git push --set-upstream origin master 347 """, 348 timeout=15 349 ) 350 351 with subtest("Fork a project"): 352 # Bob forks Alice's project 353 gitlab.succeed( 354 """ 355 [ "$(curl \ 356 -o /dev/null \ 357 -w '%{http_code}' \ 358 -X POST \ 359 -H 'Content-Type: application/json' \ 360 -H @/tmp/headers-bob \ 361 http://gitlab/api/v4/projects/${aliceProjectId}/fork)" = "201" ] 362 """ 363 ) 364 365 # Bob creates a commit 366 gitlab.wait_until_succeeds( 367 """ 368 [ "$(curl \ 369 -o /dev/null \ 370 -w '%{http_code}' \ 371 -X POST \ 372 -H 'Content-Type: application/json' \ 373 -H @/tmp/headers-bob \ 374 -d @${putFile} \ 375 http://gitlab/api/v4/projects/${bobProjectId}/repository/files/some-other-file.txt)" = "201" ] 376 """ 377 ) 378 379 with subtest("Create a Merge Request"): 380 # Bob opens a merge request against Alice's repository 381 gitlab.wait_until_succeeds( 382 """ 383 [ "$(curl \ 384 -o /dev/null \ 385 -w '%{http_code}' \ 386 -X POST \ 387 -H 'Content-Type: application/json' \ 388 -H @/tmp/headers-bob \ 389 -d @${mergeRequest} \ 390 http://gitlab/api/v4/projects/${bobProjectId}/merge_requests)" = "201" ] 391 """ 392 ) 393 394 # Alice merges the MR 395 gitlab.wait_until_succeeds( 396 """ 397 [ "$(curl \ 398 -o /dev/null \ 399 -w '%{http_code}' \ 400 -X PUT \ 401 -H 'Content-Type: application/json' \ 402 -H @/tmp/headers-alice \ 403 -d @${mergeRequest} \ 404 http://gitlab/api/v4/projects/${aliceProjectId}/merge_requests/1/merge)" = "200" ] 405 """ 406 ) 407 408 with subtest("Create an Issue"): 409 # Bob opens an issue on Alice's repository 410 gitlab.succeed( 411 """[ "$(curl \ 412 -o /dev/null \ 413 -w '%{http_code}' \ 414 -X POST \ 415 -H 'Content-Type: application/json' \ 416 -H @/tmp/headers-bob \ 417 -d @${newIssue} \ 418 http://gitlab/api/v4/projects/${aliceProjectId}/issues)" = "201" ] 419 """ 420 ) 421 422 # Alice closes the issue 423 gitlab.wait_until_succeeds( 424 """ 425 [ "$(curl \ 426 -o /dev/null \ 427 -w '%{http_code}' \ 428 -X PUT \ 429 -H 'Content-Type: application/json' \ 430 -H @/tmp/headers-alice -d @${closeIssue} http://gitlab/api/v4/projects/${aliceProjectId}/issues/1)" = "200" ] 431 """ 432 ) 433 '' 434 + '' 435 with subtest("Download archive.tar.gz"): 436 gitlab.succeed( 437 """ 438 [ "$(curl \ 439 -o /dev/null \ 440 -w '%{http_code}' \ 441 -H @/tmp/headers-alice \ 442 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.gz)" = "200" ] 443 """ 444 ) 445 gitlab.succeed( 446 """ 447 curl \ 448 -H @/tmp/headers-alice \ 449 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.gz > /tmp/archive.tar.gz 450 """ 451 ) 452 gitlab.succeed("test -s /tmp/archive.tar.gz") 453 454 with subtest("Download archive.tar.bz2"): 455 gitlab.succeed( 456 """ 457 [ "$(curl \ 458 -o /dev/null \ 459 -w '%{http_code}' \ 460 -H @/tmp/headers-alice \ 461 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.bz2)" = "200" ] 462 """ 463 ) 464 gitlab.succeed( 465 """ 466 curl \ 467 -H @/tmp/headers-alice \ 468 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.bz2 > /tmp/archive.tar.bz2 469 """ 470 ) 471 gitlab.succeed("test -s /tmp/archive.tar.bz2") 472 '' 473 + '' 474 with subtest("Test docker registry http is available"): 475 gitlab.succeed("curl -sSf http://registry.localhost") 476 ''; 477 478 in 479 '' 480 gitlab.start() 481 '' 482 + waitForServices 483 + '' 484 gitlab.succeed("cp /var/gitlab/state/config/secrets.yml /root/gitlab-secrets.yml") 485 '' 486 + test true 487 + '' 488 gitlab.systemctl("start gitlab-backup.service") 489 gitlab.wait_for_unit("gitlab-backup.service") 490 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/backup/dump_gitlab_backup.tar") 491 gitlab.systemctl("stop postgresql.service gitlab-config.service gitlab.target") 492 gitlab.succeed( 493 "find ${nodes.gitlab.services.gitlab.statePath} -mindepth 1 -maxdepth 1 -not -name backup -execdir rm -r {} +" 494 ) 495 gitlab.succeed("systemd-tmpfiles --create") 496 gitlab.succeed("rm -rf ${nodes.gitlab.services.postgresql.dataDir}") 497 gitlab.systemctl("start gitlab-config.service gitaly.service gitlab-postgresql.service") 498 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/tmp/sockets/gitaly.socket") 499 gitlab.succeed( 500 "sudo -u gitlab -H gitlab-rake gitlab:backup:restore RAILS_ENV=production BACKUP=dump force=yes" 501 ) 502 gitlab.systemctl("start gitlab.target") 503 '' 504 + waitForServices 505 + '' 506 with subtest("Check that no secrets were auto-generated as these would be non-persistent"): 507 gitlab.succeed("diff -u /root/gitlab-secrets.yml /var/gitlab/state/config/secrets.yml") 508 '' 509 + test false; 510}