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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# frozen_string_literal: true
# rubocop:disable Gitlab/StrongMemoizeAttr
module Ci
module Deployable
extend ActiveSupport::Concern
included do
prepend_mod_with('Ci::Deployable') # rubocop: disable Cop/InjectEnterpriseEditionModule
has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable
state_machine :status do
after_transition any => [:success] do |job|
job.run_after_commit do
Environments::StopJobSuccessWorker.perform_async(id)
end
end
# Synchronize Deployment Status
# Please note that the data integirty is not assured because we can't use
# a database transaction due to DB decomposition.
after_transition do |job, transition|
next if transition.loopback?
next unless job.project
job.run_after_commit do
job.deployment&.sync_status_with(job)
end
end
end
end
def outdated_deployment?
strong_memoize(:outdated_deployment) do
deployment_job? &&
project.ci_forward_deployment_enabled? &&
(!project.ci_forward_deployment_rollback_allowed? || incomplete?) &&
deployment&.older_than_last_successful_deployment?
end
end
# Virtual deployment status depending on the environment status.
def deployment_status
return unless deployment_job?
if success?
return successful_deployment_status
elsif failed?
return :failed
end
:creating
end
def successful_deployment_status
if deployment&.last?
:last
else
:out_of_date
end
end
def persisted_environment
return unless has_environment_keyword?
strong_memoize(:persisted_environment) do
# This code path has caused N+1s in the past, since environments are only indirectly
# associated to builds and pipelines; see https://gitlab.com/gitlab-org/gitlab/-/issues/326445
# We therefore batch-load them to prevent dormant N+1s until we found a proper solution.
BatchLoader.for(expanded_environment_name).batch(key: project_id) do |names, loader, args|
Environment.where(name: names, project: args[:key]).find_each do |environment|
loader.call(environment.name, environment)
end
end
end
end
def persisted_environment=(environment)
strong_memoize(:persisted_environment) { environment }
end
# If build.persisted_environment is a BatchLoader, we need to remove
# the method proxy in order to clone into new item here
# https://github.com/exAspArk/batch-loader/issues/31
def actual_persisted_environment
persisted_environment.respond_to?(:__sync) ? persisted_environment.__sync : persisted_environment
end
def expanded_environment_name
return unless has_environment_keyword?
strong_memoize(:expanded_environment_name) do
# We're using a persisted expanded environment name in order to avoid
# variable expansion per request.
if metadata&.expanded_environment_name.present?
metadata.expanded_environment_name
else
ExpandVariables.expand(environment, -> { simple_variables.sort_and_expand_all })
end
end
end
def expanded_kubernetes_namespace
return unless has_environment_keyword?
namespace = options.dig(:environment, :kubernetes, :namespace)
if namespace.present? # rubocop:disable Style/GuardClause
strong_memoize(:expanded_kubernetes_namespace) do
ExpandVariables.expand(namespace, -> { simple_variables })
end
end
end
def has_environment_keyword?
environment.present?
end
def deployment_job?
has_environment_keyword? && environment_action == 'start'
end
def stops_environment?
has_environment_keyword? && environment_action == 'stop'
end
def environment_action
options.fetch(:environment, {}).fetch(:action, 'start') if options
end
def environment_tier_from_options
options.dig(:environment, :deployment_tier) if options
end
def environment_tier
environment_tier_from_options || persisted_environment.try(:tier)
end
def environment_url
options&.dig(:environment, :url) || persisted_environment.try(:external_url)
end
def environment_slug
persisted_environment.try(:slug)
end
def environment_status
strong_memoize(:environment_status) do
if has_environment_keyword? && merge_request
EnvironmentStatus.new(project, persisted_environment, merge_request, pipeline.sha)
end
end
end
def on_stop
options&.dig(:environment, :on_stop)
end
def stop_action_successful?
success?
end
end
end
# rubocop:enable Gitlab/StrongMemoizeAttr
|