Module: UffizziCore::DeploymentService

Defined in:
app/services/uffizzi_core/deployment_service.rb

Constant Summary collapse

MIN_TARGET_PORT_RANGE =
37_000
MAX_TARGET_PORT_RANGE =
39_999
DEPLOYMENT_PROCESS_STATUSES =
{
  building: :building,
  deploying: :deploying,
  failed: :failed,
  queued: :queued,
}.freeze

Class Method Summary collapse

Class Method Details

.all_containers_have_unique_ports?(containers) ⇒ Boolean

Returns:

  • (Boolean)


139
140
141
142
# File 'app/services/uffizzi_core/deployment_service.rb', line 139

def all_containers_have_unique_ports?(containers)
  ports = containers.map(&:port).compact
  containers.empty? || ports.size == ports.uniq.size
end

.build_default_subdomain(deployment) ⇒ Object



123
124
125
126
127
128
129
# File 'app/services/uffizzi_core/deployment_service.rb', line 123

def build_default_subdomain(deployment)
  deployment_name = name(deployment)
  slug = deployment.project.slug.to_s
  subdomain = "#{deployment_name}-#{slug}"

  format_subdomain(subdomain)
end

.build_deployment_url(deployment) ⇒ Object



135
136
137
# File 'app/services/uffizzi_core/deployment_service.rb', line 135

def build_deployment_url(deployment)
  "#{Settings.app.managed_dns_zone}/projects/#{deployment.project_id}/deployments"
end

.build_docker_continuous_preview_subdomain(deployment) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
# File 'app/services/uffizzi_core/deployment_service.rb', line 111

def build_docker_continuous_preview_subdomain(deployment)
  project = deployment.project
  continuous_preview_payload = deployment.continuous_preview_payload
  docker_payload = continuous_preview_payload['docker']
  repo_name = docker_payload['image'].split('/').last.gsub('_', '-')
  image_tag = docker_payload['tag'].gsub('_', '-')
  deployment_name = name(deployment)
  subdomain = "#{image_tag}-#{deployment_name}-#{repo_name}-#{project.slug}"

  format_subdomain(subdomain)
end

.build_preview_url(deployment) ⇒ Object



131
132
133
# File 'app/services/uffizzi_core/deployment_service.rb', line 131

def build_preview_url(deployment)
  "#{deployment.subdomain}.#{Settings.app.managed_dns_zone}"
end

.build_pull_request_subdomain(deployment) ⇒ Object



100
101
102
103
104
105
106
107
108
109
# File 'app/services/uffizzi_core/deployment_service.rb', line 100

def build_pull_request_subdomain(deployment)
  project = deployment.project
  continuous_preview_payload = deployment.continuous_preview_payload
  pull_request_payload = continuous_preview_payload['pull_request']
  repo_name = pull_request_payload['repository_full_name'].split('/').last
  deployment_name = name(deployment)
  subdomain = "pr#{pull_request_payload['id']}-#{deployment_name}-#{repo_name}-#{project.slug}"

  format_subdomain(subdomain)
end

.build_subdomain(deployment) ⇒ Object



89
90
91
92
93
94
95
96
97
98
# File 'app/services/uffizzi_core/deployment_service.rb', line 89

def build_subdomain(deployment)
  if deployment.continuous_preview_payload.present?
    continuous_preview_payload = deployment.continuous_preview_payload

    return build_pull_request_subdomain(deployment) if continuous_preview_payload['pull_request'].present?
    return build_docker_continuous_preview_subdomain(deployment) if continuous_preview_payload['docker'].present?
  end

  build_default_subdomain(deployment)
end

.create_from_compose(compose_file, project, user) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'app/services/uffizzi_core/deployment_service.rb', line 15

def create_from_compose(compose_file, project, user)
  deployment_attributes = ActionController::Parameters.new(compose_file.template.payload)
  deployment_form = UffizziCore::Api::Cli::V1::Deployment::CreateForm.new(deployment_attributes)
  deployment_form.assign_dependences!(project, user)
  deployment_form.compose_file = compose_file
  deployment_form.creation_source = UffizziCore::Deployment.creation_source.compose_file_manual

  if deployment_form.save
    update_subdomain!(deployment_form)

    UffizziCore::Deployment::CreateJob.perform_async(deployment_form.id)
    UffizziCore::Deployment::CreateWebhooksJob.perform_async(deployment_form.id)
  end

  deployment_form
end

.create_webhooks(deployment) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/services/uffizzi_core/deployment_service.rb', line 209

def create_webhooks(deployment)
  credential = deployment.project..credentials.docker_hub.active.first

  if !deployment.containers.with_docker_hub_repo.exists? || !credential.present?
    Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} create_webhooks no dockerhub containers or credential")
    return
  end

  accounts = UffizziCore::DockerHubService.accounts(credential)

  deployment.containers.with_docker_hub_repo.find_each do |container|
    if !accounts.include?(container.repo.namespace)
      logger_message = "DEPLOYMENT_PROCESS deployment_id=#{deployment.id} no namespace(#{container.repo.namespace})
      in accounts(#{accounts.inspect})"
      Rails.logger.info(logger_message)
      next
    end

    UffizziCore::Credential::DockerHub::CreateWebhookJob.perform_async(credential.id, container.image, deployment.id)
  end
end

.deploy_containers(deployment, repeated = false) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'app/services/uffizzi_core/deployment_service.rb', line 51

def deploy_containers(deployment, repeated = false)
  if !repeated
    create_activity_items(deployment)
    update_controller_container_names(deployment)
  end

  case deployment_process_status(deployment)
  when DEPLOYMENT_PROCESS_STATUSES[:building]
    Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} repeat deploy_containers")
    UffizziCore::Deployment::DeployContainersJob.perform_in(1.minute, deployment.id, true)
  when DEPLOYMENT_PROCESS_STATUSES[:deploying]
    Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} start deploying into controller")

    containers = deployment.active_containers
    containers = add_default_deployment_variables!(containers)

    UffizziCore::ControllerService.deploy_containers(deployment, containers)
  else
    Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} deployment has builds errors, stopping")
  end
end

.disable!(deployment) ⇒ Object



73
74
75
76
77
78
79
# File 'app/services/uffizzi_core/deployment_service.rb', line 73

def disable!(deployment)
  deployment.disable!
  compose_file = deployment.compose_file || deployment.template&.compose_file
  return unless compose_file&.kind&.temporary?

  compose_file.destroy!
end

.fail!(deployment) ⇒ Object



81
82
83
84
85
86
87
# File 'app/services/uffizzi_core/deployment_service.rb', line 81

def fail!(deployment)
  deployment.fail!
  compose_file = deployment.compose_file || deployment.template&.compose_file
  return unless compose_file&.kind&.temporary?

  compose_file.destroy!
end

.failed?(deployment) ⇒ Boolean

Returns:

  • (Boolean)


235
236
237
238
239
# File 'app/services/uffizzi_core/deployment_service.rb', line 235

def failed?(deployment)
  deployment_status = deployment_process_status(deployment)

  deployment_status == DEPLOYMENT_PROCESS_STATUSES[:failed]
end

.find_unused_port(deployment) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'app/services/uffizzi_core/deployment_service.rb', line 164

def find_unused_port(deployment)
  selected_port = nil

  Timeout.timeout(20) do
    loop do
      selected_port = rand(MIN_TARGET_PORT_RANGE..MAX_TARGET_PORT_RANGE)

      break if !deployment.containers.exists?(target_port: selected_port)
    end
  end

  selected_port
end

.ingress_container?(containers) ⇒ Boolean

Returns:

  • (Boolean)


160
161
162
# File 'app/services/uffizzi_core/deployment_service.rb', line 160

def ingress_container?(containers)
  containers.empty? || containers.map(&:receive_incoming_requests).count(true) == 1
end

.name(deployment) ⇒ Object



199
200
201
# File 'app/services/uffizzi_core/deployment_service.rb', line 199

def name(deployment)
  "deployment-#{deployment.id}"
end

.pull_request_payload_present?(deployment) ⇒ Boolean

Returns:

  • (Boolean)


231
232
233
# File 'app/services/uffizzi_core/deployment_service.rb', line 231

def pull_request_payload_present?(deployment)
  deployment.continuous_preview_payload.present? && deployment.continuous_preview_payload['pull_request'].present?
end

.setup_ingress_container(deployment, ingress_container, port) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'app/services/uffizzi_core/deployment_service.rb', line 178

def setup_ingress_container(deployment, ingress_container, port)
  old_deployment_subdomain = deployment.subdomain

  containers = deployment.containers.active

  UffizziCore::Container.transaction do
    containers.update_all(receive_incoming_requests: false, port: nil, public: false)
    containers.find(ingress_container.id).update!(port: port, public: true, receive_incoming_requests: true)
  end

  deployment.reload

  new_deployment_subdomain = build_subdomain(deployment)

  if new_deployment_subdomain != old_deployment_subdomain
    deployment.update(subdomain: new_deployment_subdomain)
  end

  UffizziCore::Deployment::DeployContainersJob.perform_async(deployment.id)
end

.update_from_compose(compose_file, project, user, deployment_id) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'app/services/uffizzi_core/deployment_service.rb', line 32

def update_from_compose(compose_file, project, user, deployment_id)
  deployment_attributes = ActionController::Parameters.new(compose_file.template.payload)

  deployment_form = UffizziCore::Api::Cli::V1::Deployment::UpdateForm.new(deployment_attributes)
  deployment_form.assign_dependences!(project, user)
  deployment_form.compose_file = compose_file

  if deployment_form.valid?
    deployment = UffizziCore::Deployment.find(deployment_id)
    deployment.containers.destroy_all
    deployment.compose_file.destroy if deployment.compose_file.kind.temporary?
    deployment.update!(containers: deployment_form.containers, compose_file_id: compose_file.id)

    return deployment
  end

  deployment_form
end

.update_subdomain!(deployment) ⇒ Object



203
204
205
206
207
# File 'app/services/uffizzi_core/deployment_service.rb', line 203

def update_subdomain!(deployment)
  deployment.subdomain = build_subdomain(deployment)

  deployment.save!
end

.valid_containers_memory_limit?(deployment) ⇒ Boolean

Returns:

  • (Boolean)


144
145
146
147
148
149
150
# File 'app/services/uffizzi_core/deployment_service.rb', line 144

def valid_containers_memory_limit?(deployment)
  containers = deployment.containers
  container_memory_limit = deployment.project..container_memory_limit
  return true if container_memory_limit.nil?

  containers.all? { |container| container.memory_limit <= container_memory_limit }
end

.valid_containers_memory_request?(deployment) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
# File 'app/services/uffizzi_core/deployment_service.rb', line 152

def valid_containers_memory_request?(deployment)
  containers = deployment.containers
  container_memory_limit = deployment.project..container_memory_limit
  return true if container_memory_limit.nil?

  containers.all? { |container| container.memory_request <= container_memory_limit }
end