runners.rb 6.3 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's avatar
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's avatar
Robert Schilling committed
15
      end
16
      get do
Douwe Maan's avatar
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's avatar
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's avatar
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's avatar
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's avatar
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's avatar
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's avatar
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

82
        runner.destroy!
83 84 85
      end
    end

Robert Schilling's avatar
Robert Schilling committed
86 87 88
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
89 90 91
    resource :projects do
      before { authorize_admin_project }

Robert Schilling's avatar
Robert Schilling committed
92 93 94 95 96 97
      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'
98
        use :pagination
Robert Schilling's avatar
Robert Schilling committed
99
      end
100 101 102 103
      get ':id/runners' do
        runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
        present paginate(runners), with: Entities::Runner
      end
104

Robert Schilling's avatar
Robert Schilling committed
105 106 107 108 109 110
      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
111
      post ':id/runners' do
112
        runner = get_runner(params[:runner_id])
113
        authenticate_enable_runner!(runner)
114

115 116 117
        runner_project = runner.assign_to(user_project)

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

Robert Schilling's avatar
Robert Schilling committed
124 125 126 127 128 129
      desc "Disable project's runner" do
        success Entities::Runner
      end
      params do
        requires :runner_id, type: Integer, desc: 'The ID of the runner'
      end
130 131 132 133 134
      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
135
        forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
136 137 138

        runner_project.destroy
      end
139 140 141
    end

    helpers do
Tomasz Maczukin's avatar
Tomasz Maczukin committed
142
      def filter_runners(runners, scope, options = {})
143 144 145
        return runners unless scope.present?

        available_scopes = ::Ci::Runner::AVAILABLE_SCOPES
Tomasz Maczukin's avatar
Tomasz Maczukin committed
146 147 148 149
        if options[:without]
          available_scopes = available_scopes - options[:without]
        end

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

        runners.send(scope)
155 156 157 158 159 160 161 162
      end

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

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

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

174 175 176 177 178
      def authenticate_delete_runner!(runner)
        return if current_user.is_admin?
        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)
179 180
      end

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

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