Skip to content
This repository was archived by the owner on Mar 16, 2021. It is now read-only.

Commit 294298f

Browse files
committed
Fixes #8409 - Pull docker image asynchronously
1 parent 394e988 commit 294298f

20 files changed

Lines changed: 333 additions & 141 deletions

File tree

app/controllers/containers/steps_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def set_form
4343

4444
def create_container
4545
@state.send(:"create_#{step}", params[:"docker_container_wizard_states_#{step}"])
46-
container = (service = Service::Containers.new).start_container!(@state)
46+
container = (service = ForemanDocker::Service::Containers.new).start_container!(@state)
4747
if container.present?
4848
process_success(:object => container, :success_redirect => container_path(container))
4949
else

app/controllers/image_search_controller.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def use_hub?
3535
end
3636

3737
def hub_image_exists?(terms)
38-
@compute_resource.exist?(terms)
38+
::Docker::Image.exist?(terms)
3939
end
4040

4141
def hub_auto_complete_image_tags(terms)
@@ -62,10 +62,9 @@ def registry_auto_complete_image_tags(terms)
6262
end
6363

6464
def registry_search_image(terms)
65-
r = ::Service::RegistryApi.new(:url => @registry.url,
66-
:user => @registry.username,
67-
:password => @registry.password).search(terms)
68-
r['results']
65+
::Service::RegistryApi.new(:url => @registry.url,
66+
:user => @registry.username,
67+
:password => @registry.password).search(terms)['results']
6968
end
7069

7170
def action_permission

app/helpers/containers_helper.rb

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,28 @@ def container_link_hash(container, resource)
2929
end
3030
end
3131

32+
def container_title(container)
33+
title = container.name.titleize
34+
title += if container.uuid.present?
35+
"- #{container.in_fog.name}"
36+
else
37+
_(" - provisioning ") + image_tag('spinner.gif')
38+
end
39+
title(container.name.titleize, title.html_safe)
40+
end
41+
3242
def container_title_actions(container)
3343
@compute_resource = container.compute_resource
44+
container_title(container)
3445
title_actions(
35-
button_group(
36-
link_to(_('Commit'), '#commit-modal', :'data-toggle' => 'modal')
37-
),
38-
button_group(container_power_action(container.in_fog)),
39-
button_group(
40-
display_delete_if_authorized(
41-
hash_for_container_path(:id => container.id)
42-
.merge(:auth_object => container,
43-
:auth_action => 'destroy',
44-
:authorizer => authorizer),
45-
:confirm => _("Delete %s?") % container.name)
46-
)
47-
)
46+
button_group(link_to(_('Commit'), '#commit-modal', :'data-toggle' => 'modal')),
47+
button_group(container_power_action(container.in_fog)),
48+
button_group(display_delete_if_authorized(
49+
hash_for_container_path(:id => container.id).merge(:auth_object => container,
50+
:auth_action => 'destroy',
51+
:authorizer => authorizer),
52+
:confirm => _("Delete %s?") % container.name))
53+
) if container.uuid.present?
4854
end
4955

5056
def container_power_action(vm, authorizer = nil)
@@ -93,4 +99,20 @@ def trunc_with_tooltip(text, length = 32)
9399
trunc(text, length)
94100
end
95101
end
102+
103+
def fog_property(container)
104+
return 'Not available' unless container.uuid.present?
105+
yield
106+
end
107+
108+
def pair_attributes_table(attributes)
109+
table = "<table id='environment_variables' class='table table-bordered'
110+
style='table-layout:fixed; word-wrap: break-word'>"
111+
attributes.each do |pair|
112+
pair = pair.split("=")
113+
table += "<tr><td><b> #{pair.first} </b></td><td><i> #{pair.second} </i></td></tr>"
114+
end
115+
table += '</table>'
116+
table.html_safe
117+
end
96118
end

app/models/concerns/fog_extensions/fogdocker/server.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def command
2727
end
2828

2929
def poweroff
30-
service.vm_action(:id => id, :action => :kill)
30+
service.container_action(:id => id, :action => :kill)
3131
end
3232

3333
def reset

app/models/container.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
class Container < ActiveRecord::Base
22
include Authorizable
33
include Taxonomix
4+
include ForemanTasks::Concerns::ActionSubject
45

56
belongs_to :compute_resource
67
belongs_to :registry, :class_name => "DockerRegistry", :foreign_key => :registry_id
@@ -36,4 +37,8 @@ def parametrize
3637
def in_fog
3738
@fog_container ||= compute_resource.vms.get(uuid)
3839
end
40+
41+
def to_hash
42+
parametrize
43+
end
3944
end

app/models/foreman_docker/docker.rb

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,12 @@ def tags_for_local_image(image)
4444
end
4545
end
4646

47-
def exist?(name)
48-
::Docker::Image.exist?(name)
49-
end
50-
5147
def image(id)
5248
client.image_get(id)
5349
end
5450

5551
def tags(image_name)
56-
if exist?(image_name)
52+
if ::Docker::Image.exist?(image_name)
5753
tags_for_local_image(local_images(image_name).first)
5854
else
5955
# If image is not found in the compute resource, get the tags from the Hub
@@ -73,23 +69,19 @@ def provider_friendly_name
7369
end
7470

7571
def create_container(args = {})
76-
options = vm_instance_defaults.merge(args)
72+
options = vm_instance_defaults.merge(args) { |_, default, new| new.empty? ? default : new }
7773
logger.debug("Creating container with the following options: #{options.inspect}")
78-
docker_command do
79-
::Docker::Container.create(options)
80-
end
74+
docker_command { ::Docker::Container.create(options) }
8175
end
8276

8377
def create_image(args = {})
78+
return true if ::Docker::Image.exist?(args[:fromImage])
8479
logger.debug("Creating docker image with the following options: #{args.inspect}")
85-
docker_command do
86-
::Docker::Image.create(args)
87-
end
80+
docker_command { ::Docker::Image.create(args) }
8881
end
8982

9083
def vm_instance_defaults
91-
ActiveSupport::HashWithIndifferentAccess.new('name' => "foreman_#{Time.now.to_i}",
92-
'Cmd' => ['/bin/bash'])
84+
{ 'name' => "foreman_#{Time.now.to_i}", 'Cmd' => ['/bin/bash'] }
9385
end
9486

9587
def console(uuid)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module ForemanDocker
2+
module Service
3+
module Actions
4+
module Container
5+
class Provision < ::Actions::EntryAction
6+
def plan(container)
7+
action_subject(container)
8+
9+
sequence do
10+
plan_action(Container::Pull, container)
11+
plan_action(Container::Start, container)
12+
end
13+
end
14+
15+
def humanized_name
16+
_('Provision container')
17+
end
18+
19+
def finalize
20+
action_logger.info('Finished provisioning of Docker container')
21+
end
22+
end
23+
end
24+
end
25+
end
26+
end
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
module ForemanDocker
2+
module Service
3+
module Actions
4+
module Container
5+
class Pull < ::Actions::EntryAction
6+
def plan(container)
7+
action_subject(container)
8+
plan_self(:container => container.id)
9+
end
10+
11+
def run(event = nil)
12+
case event
13+
when :done # do nothing and move to next step
14+
when StandardError
15+
action_logger.error(event.message)
16+
action_logger.debug(event.backtrace.join("\n "))
17+
error!(event)
18+
else
19+
suspend do |suspended_action|
20+
pull_docker_image(::Container.find(input[:container]), suspended_action)
21+
end
22+
end
23+
end
24+
25+
def pull_docker_image(container, suspended_action)
26+
Thread.new do
27+
begin
28+
container.compute_resource
29+
.create_image(:fromImage => container.repository_pull_url)
30+
suspended_action << :done
31+
rescue => e
32+
# encapsulate e as Foreman::Exception
33+
suspended_action << e
34+
end
35+
end
36+
end
37+
38+
def humanized_name
39+
_('Pull')
40+
end
41+
42+
def finalize
43+
container = ::Container.find(input[:container])
44+
action_logger.info("[Docker] Finished pulling image #{container.repository_pull_url}")
45+
end
46+
end
47+
end
48+
end
49+
end
50+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module ForemanDocker
2+
module Service
3+
module Actions
4+
module Container
5+
class Start < ::Actions::EntryAction
6+
def plan(container)
7+
action_subject(container)
8+
plan_self(:container => container.id)
9+
end
10+
11+
def run
12+
container = ::Container.find(input[:container])
13+
started = container.compute_resource.create_container(container.parametrize)
14+
if started
15+
container.update_attribute(:uuid, started.id)
16+
else
17+
errors << container.compute_resource.errors[:base]
18+
end
19+
started
20+
end
21+
22+
def humanized_name
23+
_('Start')
24+
end
25+
26+
def finalize
27+
container = ::Container.find(input[:container])
28+
action_logger.info("[Docker] container #{container.name} successfully started")
29+
end
30+
end
31+
end
32+
end
33+
end
34+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module ForemanDocker
2+
module Service
3+
class Containers
4+
def errors
5+
@errors ||= []
6+
end
7+
8+
def start_container!(wizard_state)
9+
container = ActiveRecord::Base.transaction do
10+
container = Container.new(wizard_state.container_attributes) do |r|
11+
# eagerly load environment variables
12+
state =
13+
DockerContainerWizardState.includes(:environment => [:environment_variables])
14+
.find(wizard_state.id)
15+
state.environment_variables.each do |environment_variable|
16+
r.environment_variables.build :name => environment_variable.name,
17+
:value => environment_variable.value,
18+
:priority => environment_variable.priority
19+
end
20+
end
21+
Taxonomy.enabled_taxonomies.each do |taxonomy|
22+
container.send(:"#{taxonomy}=", wizard_state.preliminary.send(:"#{taxonomy}"))
23+
end
24+
container.save!
25+
container
26+
end
27+
28+
destroy_wizard_state(wizard_state)
29+
ForemanTasks.async_task(Service::Actions::Container::Provision, container)
30+
container
31+
end
32+
33+
def destroy_wizard_state(wizard_state)
34+
wizard_state.destroy
35+
DockerContainerWizardState.destroy_all(["updated_at < ?", (Time.now - 24.hours)])
36+
end
37+
end
38+
end
39+
end

0 commit comments

Comments
 (0)