BigW Consortium Gitlab

runners.rb 6.36 KB
Newer Older
1 2
module API
  class Runners < Grape::API
3 4
    include PaginationParams

5 6 7
    before { authenticate! }

    resource :runners do
Robert Schilling committed
8 9 10 11 12 13
      desc 'Get runners available for user' do
        success Entities::Runner
      end
      params do
        optional :scope, type: String, values: %w[active paused online],
                         desc: 'The scope of specific runners to show'
14
        use :pagination
Robert Schilling committed
15
      end
16
      get do
Douwe Maan committed
17
        runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: %w(specific shared))
18 19
        present paginate(runners), with: Entities::Runner
      end
20

Robert Schilling committed
21 22 23 24 25 26
      desc 'Get all runners - shared and specific' do
        success Entities::Runner
      end
      params do
        optional :scope, type: String, values: %w[active paused online specific shared],
                         desc: 'The scope of specific runners to show'
27
        use :pagination
Robert Schilling committed
28
      end
29 30 31
      get 'all' do
        authenticated_as_admin!
        runners = filter_runners(Ci::Runner.all, params[:scope])
32 33 34
        present paginate(runners), with: Entities::Runner
      end

Robert Schilling committed
35 36 37 38 39 40
      desc "Get runner's details" do
        success Entities::RunnerDetails
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the runner'
      end
41 42
      get ':id' do
        runner = get_runner(params[:id])
43
        authenticate_show_runner!(runner)
44

45
        present runner, with: Entities::RunnerDetails, current_user: current_user
46 47
      end

Robert Schilling committed
48 49 50 51 52 53 54 55 56 57 58 59
      desc "Update runner's details" do
        success Entities::RunnerDetails
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the runner'
        optional :description, type: String, desc: 'The description of the runner'
        optional :active, type: Boolean, desc: 'The state of a runner'
        optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
        optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
        optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
        at_least_one_of :description, :active, :tag_list, :run_untagged, :locked
      end
60
      put ':id' do
Robert Schilling committed
61
        runner = get_runner(params.delete(:id))
62
        authenticate_update_runner!(runner)
63
        update_service = Ci::UpdateRunnerService.new(runner)
64

65
        if update_service.update(declared_params(include_missing: false))
66
          present runner, with: Entities::RunnerDetails, current_user: current_user
67 68 69 70 71
        else
          render_validation_error!(runner)
        end
      end

Robert Schilling committed
72 73 74 75 76 77
      desc 'Remove a runner' do
        success Entities::Runner
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the runner'
      end
78 79
      delete ':id' do
        runner = get_runner(params[:id])
80
        authenticate_delete_runner!(runner)
81

Dmitriy Zaporozhets committed
82
        status 204
83
        runner.destroy!
84 85 86
      end
    end

Robert Schilling committed
87 88 89
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
90
    resource :projects, requirements: { id: %r{[^/]+} } do
91 92
      before { authorize_admin_project }

Robert Schilling committed
93 94 95 96 97 98
      desc 'Get runners available for project' do
        success Entities::Runner
      end
      params do
        optional :scope, type: String, values: %w[active paused online specific shared],
                         desc: 'The scope of specific runners to show'
99
        use :pagination
Robert Schilling committed
100
      end
101 102 103 104
      get ':id/runners' do
        runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
        present paginate(runners), with: Entities::Runner
      end
105

Robert Schilling committed
106 107 108 109 110 111
      desc 'Enable a runner for a project' do
        success Entities::Runner
      end
      params do
        requires :runner_id, type: Integer, desc: 'The ID of the runner'
      end
112
      post ':id/runners' do
113
        runner = get_runner(params[:runner_id])
114
        authenticate_enable_runner!(runner)
115

116 117 118
        runner_project = runner.assign_to(user_project)

        if runner_project.persisted?
119 120 121 122
          present runner, with: Entities::Runner
        else
          conflict!("Runner was already enabled for this project")
        end
123 124
      end

Robert Schilling committed
125 126 127 128 129 130
      desc "Disable project's runner" do
        success Entities::Runner
      end
      params do
        requires :runner_id, type: Integer, desc: 'The ID of the runner'
      end
131 132 133 134 135
      delete ':id/runners/:runner_id' do
        runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id])
        not_found!('Runner') unless runner_project

        runner = runner_project.runner
136
        forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
137

Dmitriy Zaporozhets committed
138
        status 204
139 140
        runner_project.destroy
      end
141 142 143
    end

    helpers do
144
      def filter_runners(runners, scope, options = {})
145 146 147
        return runners unless scope.present?

        available_scopes = ::Ci::Runner::AVAILABLE_SCOPES
148 149 150 151
        if options[:without]
          available_scopes = available_scopes - options[:without]
        end

152
        if (available_scopes & [scope]).empty?
153 154
          render_api_error!('Scope contains invalid value', 400)
        end
155 156

        runners.send(scope)
157 158 159 160 161 162 163 164
      end

      def get_runner(id)
        runner = Ci::Runner.find(id)
        not_found!('Runner') unless runner
        runner
      end

165
      def authenticate_show_runner!(runner)
166
        return if runner.is_shared || current_user.admin?
167
        forbidden!("No access granted") unless user_can_access_runner?(runner)
168 169
      end

170
      def authenticate_update_runner!(runner)
171
        return if current_user.admin?
172 173
        forbidden!("Runner is shared") if runner.is_shared?
        forbidden!("No access granted") unless user_can_access_runner?(runner)
174 175
      end

176
      def authenticate_delete_runner!(runner)
177
        return if current_user.admin?
178 179 180
        forbidden!("Runner is shared") if runner.is_shared?
        forbidden!("Runner associated with more than one project") if runner.projects.count > 1
        forbidden!("No access granted") unless user_can_access_runner?(runner)
181 182
      end

183 184
      def authenticate_enable_runner!(runner)
        forbidden!("Runner is shared") if runner.is_shared?
185
        forbidden!("Runner is locked") if runner.locked?
186
        return if current_user.admin?
187
        forbidden!("No access granted") unless user_can_access_runner?(runner)
188 189
      end

190
      def user_can_access_runner?(runner)
191
        current_user.ci_authorized_runners.exists?(runner.id)
192 193 194 195
      end
    end
  end
end