Welcome to mirror list, hosted at ThFree Co, Russian Federation.

runner_type.rb « ci « types « graphql « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 9094c6b96e470b69ee671c0aedc30a56baebcc8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true

module Types
  module Ci
    class RunnerType < BaseObject
      graphql_name 'CiRunner'

      edge_type_class(RunnerWebUrlEdge)
      connection_type_class(Types::CountableConnectionType)

      authorize :read_runner
      present_using ::Ci::RunnerPresenter
      expose_permissions Types::PermissionTypes::Ci::Runner

      JOB_COUNT_LIMIT = 1000

      alias_method :runner, :object

      field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
            description: 'ID of the runner.'
      field :description, GraphQL::Types::String, null: true,
            description: 'Description of the runner.'
      field :created_at, Types::TimeType, null: true,
            description: 'Timestamp of creation of this runner.'
      field :contacted_at, Types::TimeType, null: true,
            description: 'Timestamp of last contact from this runner.',
            method: :contacted_at
      field :token_expires_at, Types::TimeType, null: true,
            description: 'Runner token expiration time.',
            method: :token_expires_at
      field :maximum_timeout, GraphQL::Types::Int, null: true,
            description: 'Maximum timeout (in seconds) for jobs processed by the runner.'
      field :access_level, ::Types::Ci::RunnerAccessLevelEnum, null: false,
            description: 'Access level of the runner.'
      field :active, GraphQL::Types::Boolean, null: false,
            description: 'Indicates the runner is allowed to receive jobs.',
            deprecated: { reason: 'Use paused', milestone: '14.8' }
      field :paused, GraphQL::Types::Boolean, null: false,
            description: 'Indicates the runner is paused and not available to run jobs.'
      field :status,
            Types::Ci::RunnerStatusEnum,
            null: false,
            description: 'Status of the runner.',
            resolver: ::Resolvers::Ci::RunnerStatusResolver
      field :version, GraphQL::Types::String, null: true,
            description: 'Version of the runner.'
      field :short_sha, GraphQL::Types::String, null: true,
            description: %q(First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID.)
      field :revision, GraphQL::Types::String, null: true,
            description: 'Revision of the runner.'
      field :locked, GraphQL::Types::Boolean, null: true,
            description: 'Indicates the runner is locked.'
      field :run_untagged, GraphQL::Types::Boolean, null: false,
            description: 'Indicates the runner is able to run untagged jobs.'
      field :ip_address, GraphQL::Types::String, null: true,
            description: 'IP address of the runner.'
      field :runner_type, ::Types::Ci::RunnerTypeEnum, null: false,
            description: 'Type of the runner.'
      field :tag_list, [GraphQL::Types::String], null: true,
            description: 'Tags associated with the runner.'
      field :project_count, GraphQL::Types::Int, null: true,
            description: 'Number of projects that the runner is associated with.'
      field :job_count, GraphQL::Types::Int, null: true,
            description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist)."
      field :admin_url, GraphQL::Types::String, null: true,
            description: 'Admin URL of the runner. Only available for administrators.'
      field :edit_admin_url, GraphQL::Types::String, null: true,
            description: 'Admin form URL of the runner. Only available for administrators.'
      field :executor_name, GraphQL::Types::String, null: true,
            description: 'Executor last advertised by the runner.',
            method: :executor_name,
            feature_flag: :graphql_ci_runner_executor
      field :groups, ::Types::GroupType.connection_type, null: true,
            description: 'Groups the runner is associated with. For group runners only.'
      field :projects, ::Types::ProjectType.connection_type, null: true,
            description: 'Projects the runner is associated with. For project runners only.'
      field :jobs, ::Types::Ci::JobType.connection_type, null: true,
            description: 'Jobs assigned to the runner.',
            authorize: :read_builds,
            resolver: ::Resolvers::Ci::RunnerJobsResolver

      def job_count
        # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
        runner.builds.limit(JOB_COUNT_LIMIT + 1).count
      end

      def admin_url
        Gitlab::Routing.url_helpers.admin_runner_url(runner) if can_admin_runners?
      end

      def edit_admin_url
        Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners?
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def project_count
        BatchLoader::GraphQL.for(runner.id).batch(key: :runner_project_count) do |ids, loader, args|
          counts = ::Ci::Runner.project_type
            .select(:id, 'COUNT(ci_runner_projects.id) as count')
            .left_outer_joins(:runner_projects)
            .where(id: ids)
            .group(:id)
            .index_by(&:id)

          ids.each do |id|
            loader.call(id, counts[id]&.count)
          end
        end
      end
      # rubocop: enable CodeReuse/ActiveRecord

      def groups
        return unless runner.group_type?

        batched_owners(::Ci::RunnerNamespace, Group, :runner_groups, :namespace_id)
      end

      def projects
        return unless runner.project_type?

        batched_owners(::Ci::RunnerProject, Project, :runner_projects, :project_id)
      end

      private

      def can_admin_runners?
        context[:current_user]&.can_admin_all_resources?
      end

      # rubocop: disable CodeReuse/ActiveRecord
      def batched_owners(runner_assoc_type, assoc_type, key, column_name)
        BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader, args|
          runner_and_owner_ids = runner_assoc_type.where(runner_id: runner_ids).pluck(:runner_id, column_name)

          owner_ids_by_runner_id = runner_and_owner_ids.group_by(&:first).transform_values { |v| v.pluck(1) }
          owner_ids = runner_and_owner_ids.pluck(1).uniq

          owners = assoc_type.where(id: owner_ids).index_by(&:id)

          runner_ids.each do |runner_id|
            loader.call(runner_id, owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
          end
        end
      end
      # rubocop: enable CodeReuse/ActiveRecord
    end
  end
end

Types::Ci::RunnerType.prepend_mod_with('Types::Ci::RunnerType')