foreman_docker 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -3
- data/app/assets/javascripts/foreman_docker/image_step.js +44 -5
- data/app/controllers/concerns/foreman_docker/find_container.rb +19 -0
- data/app/controllers/containers/steps_controller.rb +25 -39
- data/app/controllers/containers_controller.rb +10 -43
- data/app/controllers/image_search_controller.rb +88 -0
- data/app/controllers/registries_controller.rb +47 -0
- data/app/helpers/container_steps_helper.rb +21 -4
- data/app/helpers/containers_helper.rb +19 -7
- data/app/models/concerns/foreman_docker/parameter_validators.rb +11 -0
- data/app/models/container.rb +20 -7
- data/app/models/docker_container_wizard_state.rb +30 -0
- data/app/models/docker_container_wizard_states/configuration.rb +7 -0
- data/app/models/docker_container_wizard_states/environment.rb +21 -0
- data/app/models/docker_container_wizard_states/environment_variable.rb +7 -0
- data/app/models/docker_container_wizard_states/image.rb +11 -0
- data/app/models/docker_container_wizard_states/preliminary.rb +11 -0
- data/app/models/docker_registry.rb +28 -0
- data/app/models/environment_variable.rb +5 -0
- data/app/models/foreman_docker/docker.rb +22 -3
- data/app/models/service/containers.rb +38 -0
- data/app/models/service/registry_api.rb +26 -0
- data/app/views/containers/_list.html.erb +19 -18
- data/app/views/containers/index.html.erb +11 -6
- data/app/views/containers/show.html.erb +26 -7
- data/app/views/containers/steps/_form_buttons.html.erb +1 -1
- data/app/views/containers/steps/configuration.html.erb +2 -2
- data/app/views/containers/steps/environment.html.erb +19 -6
- data/app/views/containers/steps/image.html.erb +58 -39
- data/app/views/containers/steps/preliminary.html.erb +21 -4
- data/app/views/foreman_docker/common_parameters/_environment_variable.html.erb +18 -0
- data/app/views/image_search/_repository_search_results.html.erb +12 -0
- data/app/views/registries/_form.html.erb +26 -0
- data/app/views/registries/edit.html.erb +3 -0
- data/app/views/registries/index.html.erb +28 -0
- data/app/views/registries/new.html.erb +3 -0
- data/config/routes.rb +11 -2
- data/db/migrate/20141024163003_create_docker_registries.rb +22 -0
- data/db/migrate/20141120123003_add_user_credentials_to_docker_registries.rb +6 -0
- data/db/migrate/20141209182008_remove_docker_tables.rb +72 -0
- data/db/migrate/20141222113313_create_wizard_states.rb +42 -0
- data/lib/foreman_docker/engine.rb +25 -5
- data/lib/foreman_docker/version.rb +1 -1
- data/test/factories/containers.rb +2 -0
- data/test/factories/docker_registry.rb +16 -0
- data/test/functionals/containers_steps_controller_test.rb +7 -40
- data/test/integration/container_steps_test.rb +24 -0
- data/test/integration/container_test.rb +18 -0
- data/test/units/container_test.rb +0 -7
- data/test/units/containers_service_test.rb +21 -0
- data/test/units/docker_registry_test.rb +24 -0
- metadata +45 -22
- data/app/models/docker_image.rb +0 -9
- data/app/models/docker_tag.rb +0 -8
- data/test/factories/docker_image.rb +0 -5
- data/test/factories/docker_tag.rb +0 -6
- data/test/units/docker_image_test.rb +0 -23
- data/test/units/docker_tag_test.rb +0 -35
@@ -2,10 +2,27 @@ module ContainerStepsHelper
|
|
2
2
|
def container_wizard(step)
|
3
3
|
wizard_header(
|
4
4
|
step,
|
5
|
-
|
6
|
-
_('Image'),
|
7
|
-
_('Configuration'),
|
8
|
-
_('Environment')
|
5
|
+
*wizard_steps.map { |s| s.to_s.humanize }
|
9
6
|
)
|
10
7
|
end
|
8
|
+
|
9
|
+
def select_registry(f)
|
10
|
+
registries = DockerRegistry.with_taxonomy_scope_override(@location, @organization)
|
11
|
+
.authorized(:view_registries)
|
12
|
+
field(f, 'docker_container_wizard_states_image[registry_id]', :label => _("Registry")) do
|
13
|
+
collection_select :wizard_states_image, :registry_id,
|
14
|
+
registries,
|
15
|
+
:id, :name,
|
16
|
+
{ :prompt => _("Select a registry") },
|
17
|
+
:class => "form-control", :disabled => registries.size == 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def last_step?
|
22
|
+
step == wizard_steps.last
|
23
|
+
end
|
24
|
+
|
25
|
+
def taxonomy_icon(taxonomy)
|
26
|
+
taxonomy == 'locations' ? 'globe' : 'briefcase'
|
27
|
+
end
|
11
28
|
end
|
@@ -1,10 +1,6 @@
|
|
1
1
|
module ContainersHelper
|
2
2
|
def managed_icon(container, resource)
|
3
|
-
|
4
|
-
'<span class="glyphicon glyphicon-check"></span>'.html_safe
|
5
|
-
else
|
6
|
-
'<span class="glyphicon glyphicon-unchecked"></span>'.html_safe
|
7
|
-
end
|
3
|
+
icon_text(managed?(container, resource) ? 'check' : 'unchecked')
|
8
4
|
end
|
9
5
|
|
10
6
|
def managed?(container, resource)
|
@@ -12,7 +8,9 @@ module ContainersHelper
|
|
12
8
|
end
|
13
9
|
|
14
10
|
def uuids_in_resource(resource)
|
15
|
-
|
11
|
+
@uuids_in_resource ||= {}
|
12
|
+
@uuids_in_resource[resource.id] ||= Container.where(:compute_resource_id => resource.id)
|
13
|
+
.pluck(:uuid)
|
16
14
|
end
|
17
15
|
|
18
16
|
def link_to_container(container, resource)
|
@@ -20,6 +18,12 @@ module ContainersHelper
|
|
20
18
|
container_link_hash(container, resource)
|
21
19
|
end
|
22
20
|
|
21
|
+
def link_to_taxonomies(taxonomies)
|
22
|
+
taxonomies.map do |taxonomy|
|
23
|
+
link_to(taxonomy)
|
24
|
+
end.join(' ')
|
25
|
+
end
|
26
|
+
|
23
27
|
def container_link_hash(container, resource)
|
24
28
|
if managed?(container, resource)
|
25
29
|
hash_for_container_path(:id => Container.find_by_uuid(container.identity).id)
|
@@ -47,8 +51,16 @@ module ContainersHelper
|
|
47
51
|
)
|
48
52
|
end
|
49
53
|
|
50
|
-
def
|
54
|
+
def auto_complete_docker_search(name, val, options = {})
|
51
55
|
addClass options, 'form-control'
|
52
56
|
text_field_tag(name, val, options)
|
53
57
|
end
|
58
|
+
|
59
|
+
def hub_url(image)
|
60
|
+
if image['is_official']
|
61
|
+
"https://registry.hub.docker.com/_/#{image['name']}"
|
62
|
+
else
|
63
|
+
"https://registry.hub.docker.com/u/#{image['name']}"
|
64
|
+
end
|
65
|
+
end
|
54
66
|
end
|
data/app/models/container.rb
CHANGED
@@ -1,22 +1,35 @@
|
|
1
1
|
class Container < ActiveRecord::Base
|
2
2
|
include Authorizable
|
3
|
+
include Taxonomix
|
3
4
|
|
4
5
|
belongs_to :compute_resource
|
5
|
-
belongs_to :
|
6
|
-
|
6
|
+
belongs_to :registry, :class_name => "DockerRegistry", :foreign_key => :registry_id
|
7
|
+
has_many :environment_variables, :dependent => :destroy, :foreign_key => :reference_id,
|
8
|
+
:inverse_of => :container,
|
9
|
+
:class_name => 'EnvironmentVariable',
|
10
|
+
:validate => false
|
11
|
+
accepts_nested_attributes_for :environment_variables, :allow_destroy => true
|
12
|
+
include ForemanDocker::ParameterValidators
|
7
13
|
|
8
|
-
attr_accessible :command, :
|
9
|
-
:cpu_set, :cpu_shares, :memory, :tty, :attach_stdin,
|
10
|
-
:attach_stdout, :attach_stderr, :tag, :uuid
|
14
|
+
attr_accessible :command, :repository_name, :name, :compute_resource_id, :entrypoint,
|
15
|
+
:cpu_set, :cpu_shares, :memory, :tty, :attach_stdin, :registry_id,
|
16
|
+
:attach_stdout, :attach_stderr, :tag, :uuid, :environment_variables_attributes
|
17
|
+
|
18
|
+
def repository_pull_url
|
19
|
+
repo = tag.blank? ? repository_name : "#{repository_name}:#{tag}"
|
20
|
+
repo = registry.prefixed_url(repo) if registry
|
21
|
+
repo
|
22
|
+
end
|
11
23
|
|
12
24
|
def parametrize
|
13
25
|
{ 'name' => name, # key has to be lower case to be picked up by the Docker API
|
14
|
-
'Image' =>
|
26
|
+
'Image' => repository_pull_url,
|
15
27
|
'Tty' => tty, 'Memory' => memory,
|
16
28
|
'Entrypoint' => entrypoint.try(:split), 'Cmd' => command.try(:split),
|
17
29
|
'AttachStdout' => attach_stdout, 'AttachStdin' => attach_stdin,
|
18
30
|
'AttachStderr' => attach_stderr, 'CpuShares' => cpu_shares,
|
19
|
-
'Cpuset' => cpu_set
|
31
|
+
'Cpuset' => cpu_set,
|
32
|
+
'Env' => environment_variables.map { |env| "#{env.name}=#{env.value}" } }
|
20
33
|
end
|
21
34
|
|
22
35
|
def in_fog
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class DockerContainerWizardState < ActiveRecord::Base
|
2
|
+
has_one :preliminary, :class_name => DockerContainerWizardStates::Preliminary,
|
3
|
+
:dependent => :destroy, :validate => true, :autosave => true
|
4
|
+
has_one :image, :class_name => DockerContainerWizardStates::Image,
|
5
|
+
:dependent => :destroy, :validate => true, :autosave => true
|
6
|
+
has_one :configuration, :class_name => DockerContainerWizardStates::Configuration,
|
7
|
+
:dependent => :destroy, :validate => true, :autosave => true
|
8
|
+
has_one :environment, :class_name => DockerContainerWizardStates::Environment,
|
9
|
+
:dependent => :destroy, :validate => true, :autosave => true
|
10
|
+
|
11
|
+
delegate :compute_resource_id, :to => :preliminary
|
12
|
+
delegate :environment_variables, :to => :environment
|
13
|
+
|
14
|
+
def container_attributes
|
15
|
+
{ :repository_name => image.repository_name,
|
16
|
+
:tag => image.tag,
|
17
|
+
:registry_id => image.registry_id,
|
18
|
+
:name => configuration.name,
|
19
|
+
:compute_resource_id => preliminary.compute_resource_id,
|
20
|
+
:tty => environment.tty,
|
21
|
+
:memory => configuration.memory,
|
22
|
+
:entrypoint => configuration.entrypoint,
|
23
|
+
:command => configuration.command,
|
24
|
+
:attach_stdout => environment.attach_stdout,
|
25
|
+
:attach_stdin => environment.attach_stdin,
|
26
|
+
:attach_stderr => environment.attach_stderr,
|
27
|
+
:cpu_shares => configuration.cpu_shares,
|
28
|
+
:cpu_set => configuration.cpu_set }
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module DockerContainerWizardStates
|
2
|
+
class Configuration < ActiveRecord::Base
|
3
|
+
self.table_name_prefix = 'docker_container_wizard_states_'
|
4
|
+
belongs_to :wizard_state, :class_name => DockerContainerWizardState,
|
5
|
+
:foreign_key => :docker_container_wizard_state_id
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DockerContainerWizardStates
|
2
|
+
class Environment < ActiveRecord::Base
|
3
|
+
self.table_name_prefix = 'docker_container_wizard_states_'
|
4
|
+
belongs_to :wizard_state, :class_name => DockerContainerWizardState
|
5
|
+
# Fix me:
|
6
|
+
# Validations are off on this association as there's a bug in ::Parameter
|
7
|
+
# that forces validation of reference_id. This will fail on new records as
|
8
|
+
# validations are executed before parent and children records have been persisted.
|
9
|
+
has_many :environment_variables, :dependent => :destroy, :foreign_key => :reference_id,
|
10
|
+
:inverse_of => :environment,
|
11
|
+
:class_name => 'DockerContainerWizardStates::EnvironmentVariable',
|
12
|
+
:validate => false
|
13
|
+
include ::ParameterValidators
|
14
|
+
|
15
|
+
accepts_nested_attributes_for :environment_variables, :allow_destroy => true
|
16
|
+
|
17
|
+
def parameters_symbol
|
18
|
+
:environment_variables
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module DockerContainerWizardStates
|
2
|
+
class EnvironmentVariable < Parameter
|
3
|
+
belongs_to :environment, :foreign_key => :reference_id, :inverse_of => :environment_variables,
|
4
|
+
:class_name => 'DockerContainerWizardStates::Environment'
|
5
|
+
validates :name, :uniqueness => { :scope => :reference_id }
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module DockerContainerWizardStates
|
2
|
+
class Image < ActiveRecord::Base
|
3
|
+
self.table_name_prefix = 'docker_container_wizard_states_'
|
4
|
+
belongs_to :wizard_state, :class_name => DockerContainerWizardState,
|
5
|
+
:foreign_key => :docker_container_wizard_state_id
|
6
|
+
delegate :compute_resource_id, :to => :wizard_state
|
7
|
+
|
8
|
+
validates :tag, :presence => true
|
9
|
+
validates :repository_name, :presence => true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module DockerContainerWizardStates
|
2
|
+
class Preliminary < ActiveRecord::Base
|
3
|
+
include Taxonomix
|
4
|
+
|
5
|
+
self.table_name_prefix = 'docker_container_wizard_states_'
|
6
|
+
belongs_to :wizard_state, :class_name => DockerContainerWizardState,
|
7
|
+
:foreign_key => :docker_container_wizard_state_id
|
8
|
+
|
9
|
+
validates :compute_resource_id, :presence => true
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class DockerRegistry < ActiveRecord::Base
|
2
|
+
include Authorizable
|
3
|
+
include Taxonomix
|
4
|
+
include Encryptable
|
5
|
+
|
6
|
+
has_many :containers, :foreign_key => "registry_id", :dependent => :destroy
|
7
|
+
encrypts :password
|
8
|
+
|
9
|
+
scoped_search :on => :name, :complete_value => true
|
10
|
+
scoped_search :on => :url
|
11
|
+
|
12
|
+
def used_location_ids
|
13
|
+
Location.joins(:taxable_taxonomies).where(
|
14
|
+
'taxable_taxonomies.taxable_type' => 'DockerRegistry',
|
15
|
+
'taxable_taxonomies.taxable_id' => id).pluck(:id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def used_organization_ids
|
19
|
+
Organization.joins(:taxable_taxonomies).where(
|
20
|
+
'taxable_taxonomies.taxable_type' => 'DockerRegistry',
|
21
|
+
'taxable_taxonomies.taxable_id' => id).pluck(:id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def prefixed_url(image_name)
|
25
|
+
uri = URI(url)
|
26
|
+
"#{uri.hostname}:#{uri.port}/#{image_name}"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
class EnvironmentVariable < Parameter
|
2
|
+
belongs_to :container, :foreign_key => :reference_id, :inverse_of => :environment_variables
|
3
|
+
audited :except => [:priority], :associated_with => :container, :allow_mass_assignment => true
|
4
|
+
validates :name, :uniqueness => { :scope => :reference_id }
|
5
|
+
end
|
@@ -28,12 +28,18 @@ module ForemanDocker
|
|
28
28
|
client.images.all
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
31
|
+
def local_images(filter = '')
|
32
32
|
client # initialize Docker-Api
|
33
|
-
# we are using an older version of docker-api, which differs from the current
|
34
33
|
::Docker::Image.all('filter' => filter)
|
35
34
|
end
|
36
35
|
|
36
|
+
def tags_for_local_image(image)
|
37
|
+
image.info['RepoTags'].map do |image_tag|
|
38
|
+
_, tag = image_tag.split(':')
|
39
|
+
tag
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
37
43
|
def exist?(name)
|
38
44
|
::Docker::Image.exist?(name)
|
39
45
|
end
|
@@ -42,6 +48,18 @@ module ForemanDocker
|
|
42
48
|
client.image_get(id)
|
43
49
|
end
|
44
50
|
|
51
|
+
def tags(image_name)
|
52
|
+
if exist?(image_name)
|
53
|
+
tags_for_local_image(local_images(image_name).first)
|
54
|
+
else
|
55
|
+
# If image is not found in the compute resource, get the tags from the Hub
|
56
|
+
hub_api_url = "https://index.docker.io/v1/repositories/#{image_name}/tags"
|
57
|
+
JSON.parse(URI.parse(hub_api_url).read).map do |tag|
|
58
|
+
tag['name']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
45
63
|
def search(term = '')
|
46
64
|
client.images.image_search(:term => term)
|
47
65
|
end
|
@@ -77,10 +95,11 @@ module ForemanDocker
|
|
77
95
|
|
78
96
|
def test_connection(options = {})
|
79
97
|
super
|
80
|
-
client
|
98
|
+
client.present?
|
81
99
|
# This should only rescue Fog::Errors, but Fog returns all kinds of errors...
|
82
100
|
rescue => e
|
83
101
|
errors[:base] << e.message
|
102
|
+
false
|
84
103
|
end
|
85
104
|
|
86
105
|
protected
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Service
|
2
|
+
class Containers
|
3
|
+
def self.start_container!(wizard_state)
|
4
|
+
ActiveRecord::Base.transaction do
|
5
|
+
container = Container.new(wizard_state.container_attributes) do |r|
|
6
|
+
# eagerly load environment variables
|
7
|
+
state = DockerContainerWizardState.includes(:environment => [:environment_variables])
|
8
|
+
.find(wizard_state.id)
|
9
|
+
state.environment_variables.each do |environment_variable|
|
10
|
+
r.environment_variables.build :name => environment_variable.name,
|
11
|
+
:value => environment_variable.value,
|
12
|
+
:priority => environment_variable.priority
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Taxonomy.enabled_taxonomies.each do |taxonomy|
|
16
|
+
container.send(:"#{taxonomy}=", wizard_state.preliminary.send(:"#{taxonomy}"))
|
17
|
+
end
|
18
|
+
|
19
|
+
fail ActiveRecord::Rollback unless start_container(container)
|
20
|
+
|
21
|
+
container.save!
|
22
|
+
destroy_wizard_state(wizard_state)
|
23
|
+
container
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.start_container(container)
|
28
|
+
started = container.compute_resource.create_container(container.parametrize)
|
29
|
+
container.uuid = started.id if started
|
30
|
+
started
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.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
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Service
|
2
|
+
class RegistryApi
|
3
|
+
DEFAULTS = { :url => 'http://localhost:5000' }
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(params = {})
|
7
|
+
config = DEFAULTS.merge(params)
|
8
|
+
uri = URI(config.delete(:url))
|
9
|
+
uri.user = config.delete(:user)
|
10
|
+
uri.password = config.delete(:password)
|
11
|
+
@config = config.merge(:url => uri.to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
def search(aquery)
|
15
|
+
response = RestClient.get(config[:url] + '/v1/search',
|
16
|
+
:params => { :q => aquery }, :accept => :json)
|
17
|
+
JSON.parse(response.body)
|
18
|
+
end
|
19
|
+
|
20
|
+
def list_repository_tags(arepository)
|
21
|
+
response = RestClient.get(config[:url] + "/v1/repositories/#{arepository}/tags",
|
22
|
+
:accept => :json)
|
23
|
+
JSON.parse(response.body)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,25 +1,29 @@
|
|
1
1
|
<table class="table table-bordered table-striped table-condensed" data-table="inline">
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<th class="text-center"><%= _("Name") %></th>
|
5
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Status") %></th>
|
6
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Image") %></th>
|
7
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Command") %></th>
|
8
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Uptime") %></th>
|
9
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Running on") %></th>
|
10
|
+
<th class="hidden-tablet hidden-xs text-center"><%= _("Managed") %></th>
|
11
|
+
<th></th>
|
12
|
+
</tr>
|
13
13
|
</thead>
|
14
14
|
|
15
|
+
<tbody>
|
15
16
|
<% containers.each do |container| %>
|
16
17
|
<tr>
|
17
18
|
<td class="ellipsis text-center"><%= link_to_container(container, resource) %></td>
|
18
|
-
<td class="hidden-tablet hidden-xs text-center"
|
19
|
+
<td class="hidden-tablet hidden-xs text-center">
|
20
|
+
<span <%= vm_power_class(container.ready?) %>><%= vm_state(container) %></span></td>
|
19
21
|
<td class="hidden-tablet hidden-xs text-center"><%= trunc(container.image_friendly_name) %></td>
|
20
22
|
<td class="hidden-tablet hidden-xs text-center"><%= trunc(container.command) %></td>
|
21
|
-
<td class="hidden-tablet hidden-xs text-center"
|
22
|
-
|
23
|
+
<td class="hidden-tablet hidden-xs text-center">
|
24
|
+
<span class="glyphicon glyphicon-time"></span> <%= container.ready? ? time_ago_in_words(container.started_at) : "N/A" %>
|
25
|
+
</td>
|
26
|
+
<td class="hidden-tablet hidden-xs text-center"><%= link_to resource, compute_resource_path(resource) %> </td>
|
23
27
|
<td class="hidden-tablet hidden-xs text-center"><%= managed_icon(container, resource) %></td>
|
24
28
|
<% @compute_resource = resource %>
|
25
29
|
<td><%= action_buttons(vm_power_action(container),
|
@@ -30,8 +34,5 @@
|
|
30
34
|
:confirm => _("Delete %s?") % container.name)) %></td>
|
31
35
|
</tr>
|
32
36
|
<% end %>
|
37
|
+
</tbody>
|
33
38
|
</table>
|
34
|
-
|
35
|
-
<!-- To be replaced by will_paginate_with_info after 1.6 support is deprecated -->
|
36
|
-
<%= page_entries_info containers %>
|
37
|
-
<%= will_paginate containers %>
|