Class: Gitlab::Workhorse

Inherits:
Object
  • Object
show all
Includes:
JwtAuthenticatable
Defined in:
lib/gitlab/workhorse.rb

Constant Summary collapse

SEND_DATA_HEADER =
'Gitlab-Workhorse-Send-Data'
VERSION_FILE =
'GITLAB_WORKHORSE_VERSION'
INTERNAL_API_CONTENT_TYPE =
'application/vnd.gitlab-workhorse+json'
INTERNAL_API_REQUEST_HEADER =
'Gitlab-Workhorse-Api-Request'
NOTIFICATION_CHANNEL =
'workhorse:notifications'
ALLOWED_GIT_HTTP_ACTIONS =
%w[git_receive_pack git_upload_pack info_refs].freeze
DETECT_HEADER =
'Gitlab-Workhorse-Detect-Content-Type'
ARCHIVE_FORMATS =
%w(zip tar.gz tar.bz2 tar).freeze

Constants included from JwtAuthenticatable

JwtAuthenticatable::SECRET_LENGTH

Class Method Summary collapse

Methods included from JwtAuthenticatable

included

Class Method Details

.channel_websocket(channel) ⇒ Object


172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/gitlab/workhorse.rb', line 172

def channel_websocket(channel)
  details = {
    'Channel' => {
      'Subprotocols' => channel[:subprotocols],
      'Url' => channel[:url],
      'Header' => channel[:headers],
      'MaxSessionTime' => channel[:max_session_time]
    }
  }
  details['Channel']['CAPem'] = channel[:ca_pem] if channel.key?(:ca_pem)

  details
end

.decode_jwt(encoded_message) ⇒ Object


195
196
197
# File 'lib/gitlab/workhorse.rb', line 195

def decode_jwt(encoded_message)
  decode_jwt_for_issuer('gitlab-workhorse', encoded_message)
end

.git_http_ok(repository, repo_type, user, action, show_all_refs: false) ⇒ Object


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
# File 'lib/gitlab/workhorse.rb', line 22

def git_http_ok(repository, repo_type, user, action, show_all_refs: false)
  raise "Unsupported action: #{action}" unless ALLOWED_GIT_HTTP_ACTIONS.include?(action.to_s)

  attrs = {
    GL_ID: Gitlab::GlId.gl_id(user),
    GL_REPOSITORY: repo_type.identifier_for_container(repository.container),
    GL_USERNAME: user&.username,
    ShowAllRefs: show_all_refs,
    Repository: repository.gitaly_repository.to_h,
    GitConfigOptions: [],
    GitalyServer: {
      address: Gitlab::GitalyClient.address(repository.storage),
      token: Gitlab::GitalyClient.token(repository.storage),
      features: Feature::Gitaly.server_feature_flags
    }
  }

  # Custom option for git-receive-pack command
  receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
  if receive_max_input_size > 0
    attrs[:GitConfigOptions] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
  end

  attrs
end

.secret_pathObject


199
200
201
# File 'lib/gitlab/workhorse.rb', line 199

def secret_path
  Gitlab.config.workhorse.secret_file
end

.send_artifacts_entry(file, entry) ⇒ Object


133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/gitlab/workhorse.rb', line 133

def send_artifacts_entry(file, entry)
  archive = file.file_storage? ? file.path : file.url

  params = {
    'Archive' => archive,
    'Entry' => Base64.encode64(entry.to_s)
  }

  [
    SEND_DATA_HEADER,
    "artifacts-entry:#{encode(params)}"
  ]
end

.send_git_archive(repository, ref:, format:, append_sha:, path: nil) ⇒ Object


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
# File 'lib/gitlab/workhorse.rb', line 64

def send_git_archive(repository, ref:, format:, append_sha:, path: nil)
  format ||= 'tar.gz'
  format = format.downcase

   = repository.(
    ref,
    Gitlab.config.gitlab.repository_downloads_path,
    format,
    append_sha: append_sha,
    path: path
  )

  raise "Repository or ref not found" if .empty?

  params = send_git_archive_params(repository, , path, archive_format(format))

  # If present, DisableCache must be a Boolean. Otherwise
  # workhorse ignores it.
  params['DisableCache'] = true if git_archive_cache_disabled?
  params['GitalyServer'] = gitaly_server_hash(repository)

  [
    SEND_DATA_HEADER,
    "git-archive:#{encode(params)}"
  ]
end

.send_git_blob(repository, blob) ⇒ Object


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/gitlab/workhorse.rb', line 48

def send_git_blob(repository, blob)
  params = {
    'GitalyServer' => gitaly_server_hash(repository),
    'GetBlobRequest' => {
      repository: repository.gitaly_repository.to_h,
      oid: blob.id,
      limit: -1
    }
  }

  [
    SEND_DATA_HEADER,
    "git-blob:#{encode(params)}"
  ]
end

.send_git_diff(repository, diff_refs) ⇒ Object


105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/gitlab/workhorse.rb', line 105

def send_git_diff(repository, diff_refs)
  params = {
    'GitalyServer' => gitaly_server_hash(repository),
    'RawDiffRequest' => Gitaly::RawDiffRequest.new(
      gitaly_diff_or_patch_hash(repository, diff_refs)
    ).to_json
  }

  [
    SEND_DATA_HEADER,
    "git-diff:#{encode(params)}"
  ]
end

.send_git_patch(repository, diff_refs) ⇒ Object


119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/gitlab/workhorse.rb', line 119

def send_git_patch(repository, diff_refs)
  params = {
    'GitalyServer' => gitaly_server_hash(repository),
    'RawPatchRequest' => Gitaly::RawPatchRequest.new(
      gitaly_diff_or_patch_hash(repository, diff_refs)
    ).to_json
  }

  [
    SEND_DATA_HEADER,
    "git-format-patch:#{encode(params)}"
  ]
end

.send_git_snapshot(repository) ⇒ Object


91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/gitlab/workhorse.rb', line 91

def send_git_snapshot(repository)
  params = {
    'GitalyServer' => gitaly_server_hash(repository),
    'GetSnapshotRequest' => Gitaly::GetSnapshotRequest.new(
      repository: repository.gitaly_repository
    ).to_json
  }

  [
    SEND_DATA_HEADER,
    "git-snapshot:#{encode(params)}"
  ]
end

.send_scaled_image(location, width, content_type) ⇒ Object


159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/gitlab/workhorse.rb', line 159

def send_scaled_image(location, width, content_type)
  params = {
    'Location' => location,
    'Width' => width,
    'ContentType' => content_type
  }

  [
    SEND_DATA_HEADER,
    "send-scaled-img:#{encode(params)}"
  ]
end

.send_url(url, allow_redirects: false) ⇒ Object


147
148
149
150
151
152
153
154
155
156
157
# File 'lib/gitlab/workhorse.rb', line 147

def send_url(url, allow_redirects: false)
  params = {
    'URL' => url,
    'AllowRedirects' => allow_redirects
  }

  [
    SEND_DATA_HEADER,
    "send-url:#{encode(params)}"
  ]
end

.set_key_and_notify(key, value, expire: nil, overwrite: true) ⇒ Object


203
204
205
206
207
208
209
210
211
212
213
# File 'lib/gitlab/workhorse.rb', line 203

def set_key_and_notify(key, value, expire: nil, overwrite: true)
  Gitlab::Redis::SharedState.with do |redis|
    result = redis.set(key, value, ex: expire, nx: !overwrite)
    if result
      redis.publish(NOTIFICATION_CHANNEL, "#{key}=#{value}")
      value
    else
      redis.get(key)
    end
  end
end

.verify_api_request!(request_headers) ⇒ Object


191
192
193
# File 'lib/gitlab/workhorse.rb', line 191

def verify_api_request!(request_headers)
  decode_jwt(request_headers[INTERNAL_API_REQUEST_HEADER])
end

.versionObject


186
187
188
189
# File 'lib/gitlab/workhorse.rb', line 186

def version
  path = Rails.root.join(VERSION_FILE)
  path.readable? ? path.read.chomp : 'unknown'
end