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