Skip to content

Commit 18264ef

Browse files
authored
Updates for change request (#1156)
1 parent 0a88e2b commit 18264ef

File tree

22 files changed

+211
-53
lines changed

22 files changed

+211
-53
lines changed

lib/travis/api/enqueue/services/restart_model.rb

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Services
44

55
class RestartModel
66
attr_reader :current_user, :target
7-
ABUSE_DETECTED = "abuse_detected"
7+
ABUSE_DETECTED = 'abuse_detected'
88

99
def initialize(current_user, params)
1010
@current_user = current_user
@@ -15,23 +15,43 @@ def initialize(current_user, params)
1515
def push(event, payload)
1616
if current_user && target && accept?
1717
::Sidekiq::Client.push(
18-
'queue' => 'hub',
19-
'class' => 'Travis::Hub::Sidekiq::Worker',
20-
'args' => [event, payload]
21-
)
18+
'queue' => 'hub',
19+
'class' => 'Travis::Hub::Sidekiq::Worker',
20+
'args' => [event, payload]
21+
)
22+
23+
Result.new(value: payload)
2224
else
23-
@cause_of_denial
25+
Result.new(error: @cause_of_denial || 'restart failed')
2426
end
2527
end
2628

2729
def accept?
28-
current_user && permission? && resetable?
30+
current_user && permission? && resetable? && billing?
31+
end
32+
33+
def billing?
34+
@_billing_ok ||= begin
35+
jobs = target.is_a?(Job) ? [target] : target.matrix
36+
37+
jobs_attrs = jobs.map do |job|
38+
job.config ? job.config.slice(:os) : {}
39+
end
40+
41+
client = Travis::API::V3::BillingClient.new(current_user.id)
42+
client.authorize_build(repository, current_user.id, jobs_attrs)
43+
true
44+
rescue Travis::API::V3::InsufficientAccess => e
45+
@cause_of_denial = e.message
46+
false
47+
end
2948
end
3049

3150
def messages
3251
messages = []
3352
messages << { notice: "The #{type} was successfully restarted." } if accept?
3453
messages << { error: 'You do not seem to have sufficient permissions.' } unless permission?
54+
messages << { error: 'You do not have enough credits.' } unless billing?
3555
messages << { error: "This #{type} currently can not be restarted." } unless resetable?
3656
messages
3757
end
@@ -54,23 +74,36 @@ def target
5474

5575
private
5676

57-
def permission?
58-
current_user && current_user.permission?(required_role, repository_id: target.repository_id) && !abusive?
59-
end
77+
def permission?
78+
current_user && current_user.permission?(required_role, repository_id: target.repository_id) && !abusive?
79+
end
6080

61-
def abusive?
62-
abusive = Travis.redis.sismember("abuse:offenders", "#{@target.owner.class.name}:#{@target.owner_id}")
63-
@cause_of_denial = ABUSE_DETECTED if abusive
64-
abusive
65-
end
81+
def abusive?
82+
abusive = Travis.redis.sismember("abuse:offenders", "#{@target.owner.class.name}:#{@target.owner_id}")
83+
@cause_of_denial = ABUSE_DETECTED if abusive
84+
abusive
85+
end
86+
87+
def resetable?
88+
target.resetable?
89+
end
6690

67-
def resetable?
68-
target.resetable?
91+
def required_role
92+
Travis.config.roles.reset_model
93+
end
94+
95+
class Result
96+
attr_reader :error, :value
97+
98+
def initialize(value: nil, error: nil)
99+
@value = value
100+
@error = error
69101
end
70102

71-
def required_role
72-
Travis.config.roles.reset_model
103+
def success?
104+
!@error
73105
end
106+
end
74107
end
75108
end
76109
end

lib/travis/api/v3/billing_client.rb

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,26 @@ module Travis::API::V3
22
class BillingClient
33
class ConfigurationError < StandardError; end
44

5+
ALLOWANCE_TIMEOUT = 1 # second
6+
57
def initialize(user_id)
68
@user_id = user_id
79
end
810

911
def allowance(owner_type, owner_id)
10-
response = connection.get("/usage/#{owner_type.downcase}s/#{owner_id}/allowance")
12+
response = connection(timeout: ALLOWANCE_TIMEOUT).get("/usage/#{owner_type.downcase}s/#{owner_id}/allowance")
1113
return BillingClient.default_allowance_response unless response.status == 200
1214

1315
Travis::API::V3::Models::Allowance.new(2, owner_id, response.body)
1416
end
1517

18+
def authorize_build(repo, sender_id, jobs)
19+
response = connection.post("/#{repo.owner.class.name.downcase.pluralize}/#{repo.owner.id}/authorize_build", { repository: { private: repo.private? }, sender_id: sender_id, jobs: jobs })
20+
handle_errors_and_respond(response)
21+
end
22+
1623
def self.default_allowance_response(id = 0)
17-
Travis::API::V3::Models::Allowance.new(1, id, {
24+
Travis::API::V3::Models::Allowance.new(1, id, {
1825
"public_repos" => true,
1926
"private_repos" => false,
2027
"concurrency_limit" => 1,
@@ -221,24 +228,28 @@ def handle_errors_and_respond(response)
221228
true
222229
when 204
223230
true
224-
when 404
225-
raise Travis::API::V3::NotFound, response.body['error']
226231
when 400
227232
raise Travis::API::V3::ClientError, response.body['error']
233+
when 403
234+
raise Travis::API::V3::InsufficientAccess, response.body['rejection_code']
235+
when 404
236+
raise Travis::API::V3::NotFound, response.body['error']
228237
when 422
229238
raise Travis::API::V3::UnprocessableEntity, response.body['error']
230239
else
231240
raise Travis::API::V3::ServerError, 'Billing system failed'
232241
end
233242
end
234243

235-
def connection
244+
def connection(timeout: 10)
236245
@connection ||= Faraday.new(url: billing_url, ssl: { ca_path: '/usr/lib/ssl/certs' }) do |conn|
237246
conn.basic_auth '_', billing_auth_key
238247
conn.headers['X-Travis-User-Id'] = @user_id.to_s
239248
conn.headers['Content-Type'] = 'application/json'
240249
conn.request :json
241250
conn.response :json
251+
conn.options[:open_timeout] = timeout
252+
conn.options[:timeout] = timeout
242253
conn.use OpenCensus::Trace::Integrations::FaradayMiddleware if Travis::Api::App::Middleware::OpenCensus.enabled?
243254
conn.adapter :net_http
244255
end

lib/travis/api/v3/models/v2_plan_config.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Travis::API::V3
22
class Models::V2PlanConfig
3-
attr_reader :id, :name, :private_repos, :starting_price, :starting_users, :private_credits, :public_credits,
4-
:addon_configs, :available_standalone_addons
3+
attr_reader :id, :name, :private_repos, :starting_price, :starting_users, :plan_type,
4+
:private_credits, :public_credits, :addon_configs, :concurrency_limit, :available_standalone_addons
55

66
def initialize(attrs)
77
@id = attrs.fetch('id')
@@ -12,6 +12,8 @@ def initialize(attrs)
1212
@private_credits = attrs.fetch('private_credits')
1313
@public_credits = attrs.fetch('public_credits')
1414
@addon_configs = attrs.fetch('addon_configs')
15+
@plan_type = attrs.fetch('plan_type')
16+
@concurrency_limit = attrs.fetch('concurrency_limit')
1517
@available_standalone_addons = attrs.fetch('available_standalone_addons')
1618
end
1719
end

lib/travis/api/v3/models/v2_subscription.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ module Travis::API::V3
22
class Models::V2Subscription
33
include Models::Owner
44

5-
attr_reader :id, :plan, :permissions, :source, :billing_info, :credit_card_info, :owner, :client_secret, :payment_intent, :addons, :available_standalone_addons, :created_at
5+
attr_reader :id, :plan, :permissions, :source, :billing_info, :credit_card_info, :owner, :status, :valid_to, :canceled_at,
6+
:client_secret, :payment_intent, :addons, :available_standalone_addons, :created_at
67

78
def initialize(attributes = {})
89
@id = attributes.fetch('id')
@@ -16,6 +17,9 @@ def initialize(attributes = {})
1617
@client_secret = attributes.fetch('client_secret')
1718
@addons = attributes['addons'].select { |addon| addon['current_usage']['status'] != 'expired' }.map { |addon| Models::V2Addon.new(addon) }
1819
@created_at = attributes.fetch('created_at')
20+
@status = attributes.fetch('status')
21+
@valid_to = attributes.fetch('valid_to')
22+
@canceled_at = attributes.fetch('canceled_at')
1923
end
2024
end
2125

lib/travis/api/v3/queries/build.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,7 @@ def restart(user)
2525
service = Travis::Enqueue::Services::RestartModel.new(user, { build_id: id })
2626
payload = { id: id, user_id: user.id }
2727

28-
restart_status = service.push("build:restart", payload)
29-
30-
if restart_status == "abuse_detected"
31-
restart_status
32-
else
33-
payload
34-
end
28+
service.push("build:restart", payload)
3529
end
3630
end
3731
end

lib/travis/api/v3/queries/job.rb

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,7 @@ def restart(user)
2525
service = Travis::Enqueue::Services::RestartModel.new(user, { job_id: id })
2626
payload = { id: id, user_id: user.id }
2727

28-
restart_status = service.push("job:restart", payload)
29-
30-
if restart_status == "abuse_detected"
31-
restart_status
32-
else
33-
payload
34-
end
28+
service.push("job:restart", payload)
3529
end
3630
end
3731
end
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module Travis::API::V3
22
class Renderer::V2PlanConfig < ModelRenderer
33
representation(:standard, :id, :name, :private_repos, :starting_price, :starting_users, :private_credits,
4-
:public_credits, :addon_configs, :available_standalone_addons)
4+
:public_credits, :addon_configs, :plan_type, :concurrency_limit, :available_standalone_addons)
55
representation(:minimal, :id, :name, :private_repos, :starting_price, :starting_users, :private_credits,
6-
:public_credits, :addon_configs)
6+
:public_credits, :addon_configs, :plan_type, :concurrency_limit)
77
end
88
end

lib/travis/api/v3/renderer/v2_subscription.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Travis::API::V3
22
class Renderer::V2Subscription < ModelRenderer
3-
representation(:standard, :id, :plan, :addons, :source, :owner, :client_secret, :billing_info, :credit_card_info, :payment_intent, :created_at)
3+
representation(:standard, :id, :plan, :addons, :status, :valid_to, :canceled_at, :source, :owner, :client_secret, :billing_info, :credit_card_info, :payment_intent, :created_at)
44

55
def billing_info
66
Renderer.render_model(model.billing_info, mode: :standard) unless model.billing_info.nil?

lib/travis/api/v3/service.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ def abuse_detected(message = 'Abuse detected. Restart disabled. If you think you
192192
rejected(Error.new(message, status: 403))
193193
end
194194

195+
def insufficient_balance(message = 'Builds have been temporarily disabled for private repositories due to a insufficient credit balance')
196+
rejected(Error.new(message, status: 403))
197+
end
198+
195199
def not_implemented
196200
raise NotImplemented
197201
end

lib/travis/api/v3/services/build/restart.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ def run
88
access_control.permissions(build).restart!
99
build.clear_debug_options!
1010

11-
restart_status = query.restart(access_control.user)
12-
if restart_status == "abuse_detected"
11+
result = query.restart(access_control.user)
12+
13+
if result.success?
14+
accepted(build: build, state_change: :restart)
15+
elsif result.error == Travis::Enqueue::Services::RestartModel::ABUSE_DETECTED
1316
abuse_detected
1417
else
15-
accepted(build: build, state_change: :restart)
18+
insufficient_balance
1619
end
1720
end
1821
end

0 commit comments

Comments
 (0)