at 23.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 = "2"; 23 aliceProjectName = "test-alice"; 24 25 bobUsername = "bob"; 26 bobUserId = "3"; 27 bobPassword = "XwkkBbl2SiIwabQzgcoaTbhsotijEEtF"; 28 bobProjectId = "3"; 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 = if pkgs.stdenv.is64bit then 4096 else 2047; 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 }; 94 }; 95 96 testScript = { nodes, ... }: 97 let 98 auth = pkgs.writeText "auth.json" (builtins.toJSON { 99 grant_type = "password"; 100 username = "root"; 101 password = initialRootPassword; 102 }); 103 104 createUserAlice = pkgs.writeText "create-user-alice.json" (builtins.toJSON rec { 105 username = aliceUsername; 106 name = username; 107 email = "alice@localhost"; 108 password = alicePassword; 109 skip_confirmation = true; 110 }); 111 112 createUserBob = pkgs.writeText "create-user-bob.json" (builtins.toJSON rec { 113 username = bobUsername; 114 name = username; 115 email = "bob@localhost"; 116 password = bobPassword; 117 skip_confirmation = true; 118 }); 119 120 aliceAuth = pkgs.writeText "alice-auth.json" (builtins.toJSON { 121 grant_type = "password"; 122 username = aliceUsername; 123 password = alicePassword; 124 }); 125 126 bobAuth = pkgs.writeText "bob-auth.json" (builtins.toJSON { 127 grant_type = "password"; 128 username = bobUsername; 129 password = bobPassword; 130 }); 131 132 aliceAddSSHKey = pkgs.writeText "alice-add-ssh-key.json" (builtins.toJSON { 133 id = aliceUserId; 134 title = "snakeoil@nixos"; 135 key = snakeOilPublicKey; 136 }); 137 138 createProjectAlice = pkgs.writeText "create-project-alice.json" (builtins.toJSON { 139 name = aliceProjectName; 140 visibility = "public"; 141 }); 142 143 putFile = pkgs.writeText "put-file.json" (builtins.toJSON { 144 branch = "master"; 145 author_email = "author@example.com"; 146 author_name = "Firstname Lastname"; 147 content = "some content"; 148 commit_message = "create a new file"; 149 }); 150 151 mergeRequest = pkgs.writeText "merge-request.json" (builtins.toJSON { 152 id = bobProjectId; 153 target_project_id = aliceProjectId; 154 source_branch = "master"; 155 target_branch = "master"; 156 title = "Add some other file"; 157 }); 158 159 newIssue = pkgs.writeText "new-issue.json" (builtins.toJSON { 160 title = "useful issue title"; 161 }); 162 163 closeIssue = pkgs.writeText "close-issue.json" (builtins.toJSON { 164 issue_iid = 1; 165 state_event = "close"; 166 }); 167 168 # Wait for all GitLab services to be fully started. 169 waitForServices = '' 170 gitlab.wait_for_unit("gitaly.service") 171 gitlab.wait_for_unit("gitlab-workhorse.service") 172 gitlab.wait_for_unit("gitlab-mailroom.service") 173 gitlab.wait_for_unit("gitlab.service") 174 gitlab.wait_for_unit("gitlab-pages.service") 175 gitlab.wait_for_unit("gitlab-sidekiq.service") 176 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/tmp/sockets/gitlab.socket") 177 gitlab.wait_until_succeeds("curl -sSf http://gitlab/users/sign_in") 178 ''; 179 180 # The actual test of GitLab. Only push data to GitLab if 181 # `doSetup` is is true. 182 test = doSetup: '' 183 GIT_SSH_COMMAND = "ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null" 184 185 gitlab.succeed( 186 "curl -isSf http://gitlab | grep -i location | grep http://gitlab/users/sign_in" 187 ) 188 gitlab.succeed( 189 "${pkgs.sudo}/bin/sudo -u gitlab -H gitlab-rake gitlab:check 1>&2" 190 ) 191 gitlab.succeed( 192 "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" 193 ) 194 '' + lib.optionalString doSetup '' 195 with subtest("Create user Alice"): 196 gitlab.succeed( 197 """[ "$(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" ]""" 198 ) 199 gitlab.succeed( 200 "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" 201 ) 202 203 with subtest("Create user Bob"): 204 gitlab.succeed( 205 """ [ "$(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" ]""" 206 ) 207 gitlab.succeed( 208 "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" 209 ) 210 211 with subtest("Setup Git and SSH for Alice"): 212 gitlab.succeed("git config --global user.name Alice") 213 gitlab.succeed("git config --global user.email alice@nixos.invalid") 214 gitlab.succeed("mkdir -m 700 /root/.ssh") 215 gitlab.succeed("cat ${snakeOilPrivateKey} > /root/.ssh/id_ecdsa") 216 gitlab.succeed("chmod 600 /root/.ssh/id_ecdsa") 217 gitlab.succeed( 218 """ 219 [ "$(curl \ 220 -o /dev/null \ 221 -w '%{http_code}' \ 222 -X POST \ 223 -H 'Content-Type: application/json' \ 224 -H @/tmp/headers-alice -d @${aliceAddSSHKey} \ 225 http://gitlab/api/v4/user/keys)" = "201" ] 226 """ 227 ) 228 229 with subtest("Create a new repository"): 230 # Alice creates a new repository 231 gitlab.succeed( 232 """ 233 [ "$(curl \ 234 -o /dev/null \ 235 -w '%{http_code}' \ 236 -X POST \ 237 -H 'Content-Type: application/json' \ 238 -H @/tmp/headers-alice \ 239 -d @${createProjectAlice} \ 240 http://gitlab/api/v4/projects)" = "201" ] 241 """ 242 ) 243 244 # Alice commits an initial commit 245 gitlab.succeed( 246 """ 247 [ "$(curl \ 248 -o /dev/null \ 249 -w '%{http_code}' \ 250 -X POST \ 251 -H 'Content-Type: application/json' \ 252 -H @/tmp/headers-alice \ 253 -d @${putFile} \ 254 http://gitlab/api/v4/projects/${aliceProjectId}/repository/files/some-file.txt)" = "201" ]""" 255 ) 256 257 with subtest("git clone over HTTP"): 258 gitlab.succeed( 259 """git clone http://gitlab/alice/${aliceProjectName}.git clone-via-http""", 260 timeout=15 261 ) 262 263 with subtest("Push a commit via SSH"): 264 gitlab.succeed( 265 f"""GIT_SSH_COMMAND="{GIT_SSH_COMMAND}" git clone gitlab@gitlab:alice/${aliceProjectName}.git""", 266 timeout=15 267 ) 268 gitlab.succeed( 269 """echo "a commit sent over ssh" > ${aliceProjectName}/ssh.txt""" 270 ) 271 gitlab.succeed( 272 """ 273 cd ${aliceProjectName} || exit 1 274 git add . 275 """ 276 ) 277 gitlab.succeed( 278 """ 279 cd ${aliceProjectName} || exit 1 280 git commit -m "Add a commit to be sent over ssh" 281 """ 282 ) 283 gitlab.succeed( 284 f""" 285 cd ${aliceProjectName} || exit 1 286 GIT_SSH_COMMAND="{GIT_SSH_COMMAND}" git push --set-upstream origin master 287 """, 288 timeout=15 289 ) 290 291 with subtest("Fork a project"): 292 # Bob forks Alice's project 293 gitlab.succeed( 294 """ 295 [ "$(curl \ 296 -o /dev/null \ 297 -w '%{http_code}' \ 298 -X POST \ 299 -H 'Content-Type: application/json' \ 300 -H @/tmp/headers-bob \ 301 http://gitlab/api/v4/projects/${aliceProjectId}/fork)" = "201" ] 302 """ 303 ) 304 305 # Bob creates a commit 306 gitlab.wait_until_succeeds( 307 """ 308 [ "$(curl \ 309 -o /dev/null \ 310 -w '%{http_code}' \ 311 -X POST \ 312 -H 'Content-Type: application/json' \ 313 -H @/tmp/headers-bob \ 314 -d @${putFile} \ 315 http://gitlab/api/v4/projects/${bobProjectId}/repository/files/some-other-file.txt)" = "201" ] 316 """ 317 ) 318 319 with subtest("Create a Merge Request"): 320 # Bob opens a merge request against Alice's repository 321 gitlab.wait_until_succeeds( 322 """ 323 [ "$(curl \ 324 -o /dev/null \ 325 -w '%{http_code}' \ 326 -X POST \ 327 -H 'Content-Type: application/json' \ 328 -H @/tmp/headers-bob \ 329 -d @${mergeRequest} \ 330 http://gitlab/api/v4/projects/${bobProjectId}/merge_requests)" = "201" ] 331 """ 332 ) 333 334 # Alice merges the MR 335 gitlab.wait_until_succeeds( 336 """ 337 [ "$(curl \ 338 -o /dev/null \ 339 -w '%{http_code}' \ 340 -X PUT \ 341 -H 'Content-Type: application/json' \ 342 -H @/tmp/headers-alice \ 343 -d @${mergeRequest} \ 344 http://gitlab/api/v4/projects/${aliceProjectId}/merge_requests/1/merge)" = "200" ] 345 """ 346 ) 347 348 with subtest("Create an Issue"): 349 # Bob opens an issue on Alice's repository 350 gitlab.succeed( 351 """[ "$(curl \ 352 -o /dev/null \ 353 -w '%{http_code}' \ 354 -X POST \ 355 -H 'Content-Type: application/json' \ 356 -H @/tmp/headers-bob \ 357 -d @${newIssue} \ 358 http://gitlab/api/v4/projects/${aliceProjectId}/issues)" = "201" ] 359 """ 360 ) 361 362 # Alice closes the issue 363 gitlab.wait_until_succeeds( 364 """ 365 [ "$(curl \ 366 -o /dev/null \ 367 -w '%{http_code}' \ 368 -X PUT \ 369 -H 'Content-Type: application/json' \ 370 -H @/tmp/headers-alice -d @${closeIssue} http://gitlab/api/v4/projects/${aliceProjectId}/issues/1)" = "200" ] 371 """ 372 ) 373 '' + '' 374 with subtest("Download archive.tar.gz"): 375 gitlab.succeed( 376 """ 377 [ "$(curl \ 378 -o /dev/null \ 379 -w '%{http_code}' \ 380 -H @/tmp/headers-alice \ 381 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.gz)" = "200" ] 382 """ 383 ) 384 gitlab.succeed( 385 """ 386 curl \ 387 -H @/tmp/headers-alice \ 388 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.gz > /tmp/archive.tar.gz 389 """ 390 ) 391 gitlab.succeed("test -s /tmp/archive.tar.gz") 392 393 with subtest("Download archive.tar.bz2"): 394 gitlab.succeed( 395 """ 396 [ "$(curl \ 397 -o /dev/null \ 398 -w '%{http_code}' \ 399 -H @/tmp/headers-alice \ 400 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.bz2)" = "200" ] 401 """ 402 ) 403 gitlab.succeed( 404 """ 405 curl \ 406 -H @/tmp/headers-alice \ 407 http://gitlab/api/v4/projects/${aliceProjectId}/repository/archive.tar.bz2 > /tmp/archive.tar.bz2 408 """ 409 ) 410 gitlab.succeed("test -s /tmp/archive.tar.bz2") 411 ''; 412 413 in '' 414 gitlab.start() 415 '' 416 + waitForServices 417 + test true 418 + '' 419 gitlab.systemctl("start gitlab-backup.service") 420 gitlab.wait_for_unit("gitlab-backup.service") 421 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/backup/dump_gitlab_backup.tar") 422 gitlab.systemctl("stop postgresql.service gitlab.target") 423 gitlab.succeed( 424 "find ${nodes.gitlab.services.gitlab.statePath} -mindepth 1 -maxdepth 1 -not -name backup -execdir rm -r {} +" 425 ) 426 gitlab.succeed("systemd-tmpfiles --create") 427 gitlab.succeed("rm -rf ${nodes.gitlab.services.postgresql.dataDir}") 428 gitlab.systemctl("start gitlab-config.service gitaly.service gitlab-postgresql.service") 429 gitlab.wait_for_file("${nodes.gitlab.services.gitlab.statePath}/tmp/sockets/gitaly.socket") 430 gitlab.succeed( 431 "sudo -u gitlab -H gitlab-rake gitlab:backup:restore RAILS_ENV=production BACKUP=dump force=yes" 432 ) 433 gitlab.systemctl("start gitlab.target") 434 '' 435 + waitForServices 436 + test false; 437}