builds_spec.rb 12.6 KB
Newer Older
1 2
require 'spec_helper'

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
3
describe Ci::API::API do
4 5
  include ApiHelpers

6
  let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) }
7
  let(:project) { FactoryGirl.create(:empty_project) }
8

Kamil Trzcinski's avatar
Kamil Trzcinski committed
9 10 11 12
  before do
    stub_ci_commit_to_return_yaml_file
  end

13
  describe "Builds API for runners" do
14
    let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
15
    let(:shared_project) { FactoryGirl.create(:empty_project, name: "SharedProject") }
16 17

    before do
18
      FactoryGirl.create :ci_runner_project, project: project, runner: runner
19 20 21 22
    end

    describe "POST /builds/register" do
      it "should start a build" do
23
        commit = FactoryGirl.create(:ci_commit, project: project)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
24
        commit.create_builds('master', false, nil)
25 26
        build = commit.builds.first

Valery Sizov's avatar
Valery Sizov committed
27
        post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
28

29 30 31
        expect(response.status).to eq(201)
        expect(json_response['sha']).to eq(build.sha)
        expect(runner.reload.platform).to eq("darwin")
32 33 34
      end

      it "should return 404 error if no pending build found" do
Valery Sizov's avatar
Valery Sizov committed
35
        post ci_api("/builds/register"), token: runner.token
36

37
        expect(response.status).to eq(404)
38 39 40
      end

      it "should return 404 error if no builds for specific runner" do
41
        commit = FactoryGirl.create(:ci_commit, project: shared_project)
42
        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
43

Valery Sizov's avatar
Valery Sizov committed
44
        post ci_api("/builds/register"), token: runner.token
45

46
        expect(response.status).to eq(404)
47 48 49
      end

      it "should return 404 error if no builds for shared runner" do
50
        commit = FactoryGirl.create(:ci_commit, project: project)
51
        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
52

Valery Sizov's avatar
Valery Sizov committed
53
        post ci_api("/builds/register"), token: shared_runner.token
54

55
        expect(response.status).to eq(404)
56 57 58
      end

      it "returns options" do
59
        commit = FactoryGirl.create(:ci_commit, project: project)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
60
        commit.create_builds('master', false, nil)
61

Valery Sizov's avatar
Valery Sizov committed
62
        post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
63

64
        expect(response.status).to eq(201)
Valery Sizov's avatar
Valery Sizov committed
65
        expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] })
66 67 68
      end

      it "returns variables" do
69
        commit = FactoryGirl.create(:ci_commit, project: project)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
70
        commit.create_builds('master', false, nil)
71
        project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
72

Valery Sizov's avatar
Valery Sizov committed
73
        post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
74

75 76
        expect(response.status).to eq(201)
        expect(json_response["variables"]).to eq([
77 78
          { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
          { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
Valery Sizov's avatar
Valery Sizov committed
79
          { "key" => "DB_NAME", "value" => "postgres", "public" => true },
80
          { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }
81
        ])
82 83 84
      end

      it "returns variables for triggers" do
85
        trigger = FactoryGirl.create(:ci_trigger, project: project)
86
        commit = FactoryGirl.create(:ci_commit, project: project)
87

88
        trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
89
        commit.create_builds('master', false, nil, trigger_request)
90
        project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
91

Valery Sizov's avatar
Valery Sizov committed
92
        post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
93

94 95
        expect(response.status).to eq(201)
        expect(json_response["variables"]).to eq([
96 97 98
          { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
          { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
          { "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true },
Valery Sizov's avatar
Valery Sizov committed
99 100 101
          { "key" => "DB_NAME", "value" => "postgres", "public" => true },
          { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
          { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
102
        ])
103 104 105 106
      end
    end

    describe "PUT /builds/:id" do
107
      let(:commit) { FactoryGirl.create(:ci_commit, project: project)}
108
      let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) }
109 110 111

      it "should update a running build" do
        build.run!
Valery Sizov's avatar
Valery Sizov committed
112
        put ci_api("/builds/#{build.id}"), token: runner.token
113
        expect(response.status).to eq(200)
114 115 116 117 118
      end

      it 'Should not override trace information when no trace is given' do
        build.run!
        build.update!(trace: 'hello_world')
Valery Sizov's avatar
Valery Sizov committed
119
        put ci_api("/builds/#{build.id}"), token: runner.token
120 121 122
        expect(build.reload.trace).to eq 'hello_world'
      end
    end
123 124 125 126

    context "Artifacts" do
      let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
      let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
127
      let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
128 129 130 131 132
      let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) }
      let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") }
      let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
      let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
      let(:get_url) { ci_api("/builds/#{build.id}/artifacts") }
133
      let(:headers) { { "GitLab-Workhorse" => "1.0" } }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
134
      let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) }
135 136 137 138 139 140 141 142

      describe "POST /builds/:id/artifacts/authorize" do
        context "should authorize posting artifact to running build" do
          before do
            build.run!
          end

          it "using token as parameter" do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
143
            post authorize_url, { token: build.token }, headers
144
            expect(response.status).to eq(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
145
            expect(json_response["TempPath"]).to_not be_nil
146 147 148 149 150
          end

          it "using token as header" do
            post authorize_url, {}, headers_with_token
            expect(response.status).to eq(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
151
            expect(json_response["TempPath"]).to_not be_nil
152 153 154 155 156 157 158 159 160
          end
        end

        context "should fail to post too large artifact" do
          before do
            build.run!
          end

          it "using token as parameter" do
161
            stub_application_setting(max_artifacts_size: 0)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
162
            post authorize_url, { token: build.token, filesize: 100 }, headers
163 164 165 166
            expect(response.status).to eq(413)
          end

          it "using token as header" do
167
            stub_application_setting(max_artifacts_size: 0)
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
            post authorize_url, { filesize: 100 }, headers_with_token
            expect(response.status).to eq(413)
          end
        end

        context "should get denied" do
          it do
            post authorize_url, { token: 'invalid', filesize: 100 }
            expect(response.status).to eq(403)
          end
        end
      end

      describe "POST /builds/:id/artifacts" do
        context "Disable sanitizer" do
          before do
            # by configuring this path we allow to pass temp file from any path
            allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/')
          end

          context "should post artifact to running build" do
            before do
              build.run!
            end

193 194 195 196 197 198 199 200
            it "uses regual file post" do
              upload_artifacts(file_upload, headers_with_token, false)
              expect(response.status).to eq(201)
              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
            end

            it "uses accelerated file post" do
              upload_artifacts(file_upload, headers_with_token, true)
201 202 203 204 205 206 207 208 209 210 211 212
              expect(response.status).to eq(201)
              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
            end

            it "updates artifact" do
              upload_artifacts(file_upload, headers_with_token)
              upload_artifacts(file_upload2, headers_with_token)
              expect(response.status).to eq(201)
              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename)
            end
          end

213
          context 'should post artifacts file and metadata file' do
214 215 216
            let!(:artifacts) { file_upload }
            let!(:metadata) { file_upload2 }

217 218 219
            let(:stored_artifacts_file) { build.reload.artifacts_file.file }
            let(:stored_metadata_file) { build.reload.artifacts_metadata.file }

220 221
            before do
              build.run!
222 223
              post(post_url, post_data, headers_with_token)
            end
224

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
            context 'post data accelerated by workhorse is correct' do
              let(:post_data) do
                { 'file.path' => artifacts.path,
                  'file.name' => artifacts.original_filename,
                  'metadata.path' => metadata.path,
                  'metadata.name' => metadata.original_filename }
              end

              it 'responds with valid status' do
                expect(response.status).to eq(201)
              end

              it 'stores artifacts and artifacts metadata' do
                expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename)
                expect(stored_metadata_file.original_filename).to eq(metadata.original_filename)
              end
241 242
            end

243
            context 'no artifacts file in post data' do
244
              let(:post_data) do
245
                { 'metadata' => metadata }
246 247
              end

248 249
              it 'is expected to respond with bad request' do
                expect(response.status).to eq(400)
250 251
              end

252
              it 'does not store metadata' do
253 254
                expect(stored_metadata_file).to be_nil
              end
255 256 257 258
            end
          end


259 260 261 262 263 264
          context "should fail to post too large artifact" do
            before do
              build.run!
            end

            it do
265
              stub_application_setting(max_artifacts_size: 0)
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
              upload_artifacts(file_upload, headers_with_token)
              expect(response.status).to eq(413)
            end
          end

          context "should fail to post artifacts without file" do
            before do
              build.run!
            end

            it do
              post post_url, {}, headers_with_token
              expect(response.status).to eq(400)
            end
          end

          context "should fail to post artifacts without GitLab-Workhorse" do
            before do
              build.run!
            end

            it do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
288
              post post_url, { token: build.token }, {}
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
              expect(response.status).to eq(403)
            end
          end
        end

        context "should fail to post artifacts for outside of tmp path" do
          before do
            # by configuring this path we allow to pass file from @tmpdir only
            # but all temporary files are stored in system tmp directory
            @tmpdir = Dir.mktmpdir
            allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
            build.run!
          end

          after do
            FileUtils.remove_entry @tmpdir
          end

          it do
            upload_artifacts(file_upload, headers_with_token)
            expect(response.status).to eq(400)
          end
        end

313 314 315 316 317 318 319 320 321
        def upload_artifacts(file, headers = {}, accelerated = true)
          if accelerated
            post post_url, {
              'file.path' => file.path,
              'file.name' => file.original_filename
            }, headers
          else
            post post_url, { file: file }, headers
          end
322 323 324 325 326 327
        end
      end

      describe "DELETE /builds/:id/artifacts" do
        before do
          build.run!
Kamil Trzcinski's avatar
Kamil Trzcinski committed
328
          post delete_url, token: build.token, file: file_upload
329 330 331 332
        end

        it "should delete artifact build" do
          build.success
Kamil Trzcinski's avatar
Kamil Trzcinski committed
333
          delete delete_url, token: build.token
334 335 336 337 338 339 340 341 342 343 344
          expect(response.status).to eq(200)
        end
      end

      describe "GET /builds/:id/artifacts" do
        before do
          build.run!
        end

        it "should download artifact" do
          build.update_attributes(artifacts_file: file_upload)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
345
          get get_url, token: build.token
346 347 348 349
          expect(response.status).to eq(200)
        end

        it "should fail to download if no artifact uploaded" do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
350
          get get_url, token: build.token
351 352 353 354
          expect(response.status).to eq(404)
        end
      end
    end
355 356
  end
end