BigW Consortium Gitlab

runners.rb 5.28 KB
Newer Older
1 2 3 4 5 6
module API
  # Runners API
  class Runners < Grape::API
    before { authenticate! }

    resource :runners do
7
      # Get runners available for user
8 9 10 11
      #
      # Example Request:
      #   GET /runners
      get do
12
        runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared'])
13 14
        present paginate(runners), with: Entities::Runner
      end
15

16 17 18 19 20 21 22
      # Get all runners - shared and specific
      #
      # Example Request:
      #   GET /runners/all
      get 'all' do
        authenticated_as_admin!
        runners = filter_runners(Ci::Runner.all, params[:scope])
23 24 25
        present paginate(runners), with: Entities::Runner
      end

26 27 28 29 30 31
      # Get runner's details
      #
      # Parameters:
      #   id (required) - The ID of ther runner
      # Example Request:
      #   GET /runners/:id
32 33
      get ':id' do
        runner = get_runner(params[:id])
34
        authenticate_show_runner!(runner)
35

36
        present runner, with: Entities::RunnerDetails, current_user: current_user
37 38
      end

39 40 41 42 43 44 45 46 47
      # Update runner's details
      #
      # Parameters:
      #   id (required) - The ID of ther runner
      #   description (optional) - Runner's description
      #   active (optional) - Runner's status
      #   tag_list (optional) - Array of tags for runner
      # Example Request:
      #   PUT /runners/:id
48 49
      put ':id' do
        runner = get_runner(params[:id])
50
        authenticate_update_runner!(runner)
51

52
        attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged]
53
        if runner.update(attrs)
54
          present runner, with: Entities::RunnerDetails, current_user: current_user
55 56 57 58 59
        else
          render_validation_error!(runner)
        end
      end

60 61 62 63 64 65
      # Remove runner
      #
      # Parameters:
      #   id (required) - The ID of ther runner
      # Example Request:
      #   DELETE /runners/:id
66 67
      delete ':id' do
        runner = get_runner(params[:id])
68
        authenticate_delete_runner!(runner)
69 70
        runner.destroy!

71
        present runner, with: Entities::Runner
72 73 74 75 76 77 78 79 80 81 82 83 84 85
      end
    end

    resource :projects do
      before { authorize_admin_project }

      # Get runners available for project
      #
      # Example Request:
      #   GET /projects/:id/runners
      get ':id/runners' do
        runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
        present paginate(runners), with: Entities::Runner
      end
86

87 88 89 90 91 92
      # Enable runner for project
      #
      # Parameters:
      #   id (required) - The ID of the project
      #   runner_id (required) - The ID of the runner
      # Example Request:
93
      #   POST /projects/:id/runners/:runner_id
94 95 96
      post ':id/runners' do
        required_attributes! [:runner_id]

97
        runner = get_runner(params[:runner_id])
98
        authenticate_enable_runner!(runner)
99 100 101 102 103
        Ci::RunnerProject.create(runner: runner, project: user_project)

        present runner, with: Entities::Runner
      end

104 105 106 107 108 109 110
      # Disable project's runner
      #
      # Parameters:
      #   id (required) - The ID of the project
      #   runner_id (required) - The ID of the runner
      # Example Request:
      #   DELETE /projects/:id/runners/:runner_id
111 112 113 114 115
      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
116
        forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
117 118 119 120 121

        runner_project.destroy

        present runner, with: Entities::Runner
      end
122 123 124
    end

    helpers do
125
      def filter_runners(runners, scope, options = {})
126 127 128
        return runners unless scope.present?

        available_scopes = ::Ci::Runner::AVAILABLE_SCOPES
129 130 131 132
        if options[:without]
          available_scopes = available_scopes - options[:without]
        end

133
        if (available_scopes & [scope]).empty?
134 135
          render_api_error!('Scope contains invalid value', 400)
        end
136 137

        runners.send(scope)
138 139 140 141 142 143 144 145
      end

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

146 147 148
      def authenticate_show_runner!(runner)
        return if runner.is_shared || current_user.is_admin?
        forbidden!("No access granted") unless user_can_access_runner?(runner)
149 150
      end

151 152 153 154
      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)
155 156
      end

157 158 159 160 161
      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)
162 163
      end

164 165 166 167
      def authenticate_enable_runner!(runner)
        forbidden!("Runner is shared") if runner.is_shared?
        return if current_user.is_admin?
        forbidden!("No access granted") unless user_can_access_runner?(runner)
168 169
      end

170
      def user_can_access_runner?(runner)
171
        current_user.ci_authorized_runners.exists?(runner.id)
172 173 174 175
      end
    end
  end
end