foreman_docker 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11781ae40d79526d1ce98c52dddbb9341c9aa211
4
- data.tar.gz: 87df5bd21364b87d937dda8d3de05cf9bae0b303
3
+ metadata.gz: 2e7371179124d8fa414db7e60fbcab48110a4372
4
+ data.tar.gz: 339e3c491804ae9432363136be744a48346af11c
5
5
  SHA512:
6
- metadata.gz: 0c70da7838dc58dc17fb4e9bbd716fe0d3d34d3bba529923ad5d62968db33f58331760fd662806cb123df7922409e19009a5f8ddaeed51a12d9991db10d8eeeb
7
- data.tar.gz: 5cde392c952ca19d530f17a8cac8e92ecb89f612143cce0d445ebe2e87e06b88895a25e28fd69025723e80a4f4c56bdd0af63822bb8c3bf0c999832aaa5abd09
6
+ metadata.gz: 08356581341a4c80c452ae75dc851ef81c9ec51c61df9106c44d99d8f157ceda5bdf1f0d325e6182983f8591d602f1998618f0058704a3791105465eb0a4d044
7
+ data.tar.gz: b0c79637f96a572eadf4d82c2a322669d8152690e4a956be0696bd06ee32b82ab93d87dca0f5891799dc1cf58671f5043283a12d33d98a7d2ffe16a204e7ce00
data/README.md CHANGED
@@ -1,6 +1,39 @@
1
1
  # Foreman Docker Plugin
2
2
 
3
- This plugin enables provisioning and managing Docker containers and images in Foreman.
3
+ ```foreman_docker``` enables provisioning and managing of [Docker](http://docker.com) containers and images in [Foreman](http://github.com/theforeman/foreman), all of that under the GPL v3+ license.
4
+
5
+ * Website: [TheForeman.org](http://theforeman.org)
6
+ * ServerFault tag: [Foreman](http://serverfault.com/questions/tagged/foreman)
7
+ * Issues: [foreman_docker Redmine](http://projects.theforeman.org/projects/docker/issues)
8
+ * Wiki: [Foreman wiki](http://projects.theforeman.org/projects/foreman/wiki/About)
9
+ * Community and support: #theforeman for general support, #theforeman-dev for development chat in [Freenode](irc.freenode.net)
10
+ * Mailing lists:
11
+ * [foreman-users](https://groups.google.com/forum/?fromgroups#!forum/foreman-users)
12
+ * [foreman-dev](https://groups.google.com/forum/?fromgroups#!forum/foreman-dev)
13
+
14
+ ## Features
15
+
16
+ * Special view with logs and processes of Foreman managed containers
17
+ ![](http://i.imgur.com/D21bdgj.png)
18
+ ![](http://i.imgur.com/XnrPTZC.png)
19
+ * Wizard for container creation and cgroups configuration
20
+ ![Select a docker image](http://i.imgur.com/IoMuNnr.png)
21
+ ![Cgroups configuration](http://i.imgur.com/74d99Tf.png)
22
+ * Commit and upload containers: creates an image with the status of your current container
23
+ ![Commit and upload to the docker hub](http://i.imgur.com/coF5Y0L.png)
24
+ * Container listing and basic CRUD operations
25
+ ![](http://i.imgur.com/DPcaHkZ.png)
26
+
27
+ ### Planned
28
+ * [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes/) integration
29
+ * Events stream ([#8037](http://projects.theforeman.org/issues/8037))
30
+ * Tight integration between Docker hosts [Atomic](http://www.projectatomic.io/) and [CoreOS](http://coreos.com/) and containers ([#7653](http://projects.theforeman.org/issues/7653), [#7652](http://projects.theforeman.org/issues/7652))
31
+ * Quickstart images - pre-supplied images and configuration ([#7869](http://projects.theforeman.org/issues/7869))
32
+ * Environment variables support ([#8226](http://projects.theforeman.org/issues/8226))
33
+ * Support to expose ports during creation or at runtime ([#7864](http://projects.theforeman.org/issues/7864))
34
+ * Links to other containers ([#7866](http://projects.theforeman.org/issues/7866))
35
+ * API ([#7874](http://projects.theforeman.org/issues/7874))
36
+ * [Hammer CLI](http://github.com/theforeman/hammer-cli-foreman) support ([#8227](http://projects.theforeman.org/issues/8227))
4
37
 
5
38
  ## Installation
6
39
 
@@ -8,19 +41,62 @@ Please see the Foreman manual for appropriate instructions:
8
41
 
9
42
  * [Foreman: How to Install a Plugin](http://theforeman.org/manuals/latest/index.html#6.1InstallaPlugin)
10
43
 
11
- The gem name is "foreman_docker".
44
+ ### Red Hat, CentOS, Fedora, Scientific Linux (rpm)
12
45
 
13
- RPM users can install the "ruby193-rubygem-foreman_docker" or "rubygem-foreman_docker" packages.
46
+ Set up the repo as explained in the link above, then run
47
+
48
+ # yum install ruby193-rubygem-foreman_docker
49
+
50
+ ### Debian, Ubuntu (deb)
51
+
52
+ Set up the repo as explained in the link above, then run
53
+
54
+ # apt-get install ruby-foreman-docker
55
+
56
+ ### Bundle (gem)
57
+
58
+ Add the following to bundler.d/Gemfile.local.rb in your Foreman installation directory (/usr/share/foreman by default)
59
+
60
+ $ gem 'foreman_docker'
61
+
62
+ Then run `bundle install` and `foreman-rake db:migrate` from the same directory
63
+
64
+ --------------
65
+
66
+ To verify that the installation was successful, go to Foreman, top bar **Administer > About** and check 'foreman_docker' shows up in the **System Status** menu under the Plugins tab. You should also see a **'Containers'** button show up in the top bar, similar to this
67
+
68
+ ![](http://i.imgur.com/Ug14Ktl.png)
69
+
70
+ ## Configuration
71
+
72
+ Go to **Infrastructure > Compute Resources** and click on "New Compute Resource".
73
+
74
+ Choose the **Docker provider**, and fill in all the fields. User name, password, and email are used so that Docker clients such as Foreman can make the host download images from the Docker hub. Your password will be encrypted in the database.
75
+
76
+ That's it. You're now ready to create and manage containers in your new Docker compute resource.
14
77
 
15
78
  ## Compatibility
16
79
 
17
- | Foreman Version | Plugin Version |
80
+ | Foreman | Plugin |
18
81
  | ---------------:| --------------:|
19
- | >= 1.5 | 0.0.1 |
82
+ | >= 1.5 | 0.0.1 - 0.0.3 |
83
+ | >= 1.6 | 0.1.0 - 0.2.0 |
20
84
 
21
- ## Testing
85
+ ## Known bugs
86
+ * Unsaved new containers leave a dangling container object in the database
87
+ * Power operations redirect to compute resource container view even for managed container
22
88
 
23
- Run `rake test:docker:test` from your Foreman directory to run the test suite.
89
+ ## How to contribute?
90
+
91
+ Generally, follow the [Foreman guidelines](http://theforeman.org/contribute.html). For code-related contributions, fork this project and send a pull request with all changes. Some things to keep in mind:
92
+ * [Follow the rules](http://theforeman.org/contribute.html#SubmitPatches) about commit message style and create a Redmine issue. Doing this right will help reviewers to get your contribution merged faster.
93
+ * [Rubocop](https://github.com/bbatsov/rubocop) will analyze your code, you can run it locally with `rake rubocop`.
94
+ * All of our pull requests run the full test suite in our [Jenkins CI system](http://ci.theforeman.org/). Please include tests in your pull requests for any additions or changes in functionality
95
+
96
+
97
+ ### Testing
98
+
99
+ Run `rake test:docker` from your Foreman directory to run the test suite.
24
100
 
25
101
  ## Latest code
26
102
 
@@ -44,3 +120,4 @@ GNU General Public License for more details.
44
120
 
45
121
  You should have received a copy of the GNU General Public License
46
122
  along with this program. If not, see <http://www.gnu.org/licenses/>.
123
+
@@ -0,0 +1,56 @@
1
+ $(document).ready(function() {
2
+ var tag = $('#tag');
3
+ tag.autocomplete({
4
+ source: [],
5
+ delay: 500,
6
+ minLength: 0
7
+ }).focus( function() {
8
+ $(this).data("uiAutocomplete").search($(this).val());
9
+ });
10
+
11
+ var target = $('#search');
12
+ autoCompleteImage(target);
13
+ target.autocomplete({
14
+ source: function( request, response ) { autoCompleteImage(target); },
15
+ delay: 500,
16
+ minLength: 1
17
+ });
18
+ });
19
+
20
+ function autoCompleteImage(item) {
21
+ $.ajax({
22
+ type:'get',
23
+ url: $(item).attr('data-url'),
24
+ data:'search=' + item.val(),
25
+ success:function (result) {
26
+ if(result == 'true'){
27
+ $('#search-addon').attr('title', 'Image found in the compute resource');
28
+ $('#search-addon').removeClass('glyphicon-remove');
29
+ $('#search-addon').css('color', 'lightgreen');
30
+ $('#search-addon').addClass('glyphicon-ok');
31
+ setAutocompleteTags();
32
+ } else {
33
+ $('#search-addon').attr('title', 'Image NOT found in the compute resource');
34
+ $('#search-addon').removeClass('glyphicon-ok');
35
+ $('#search-addon').css('color', 'red');
36
+ $('#search-addon').addClass('glyphicon-remove');
37
+ $('#tag').autocomplete('option', 'source', []);
38
+ }
39
+ }
40
+ });
41
+ }
42
+
43
+ function setAutocompleteTags() {
44
+ var tag = $('#tag');
45
+ tag.addClass('tags-autocomplete-loading');
46
+ tag.val('');
47
+ var source = [];
48
+ $.getJSON( tag.data("url"), { search: $('#search').val() },
49
+ function(data) {
50
+ tag.removeClass('tags-autocomplete-loading');
51
+ $.each( data, function(index, value) {
52
+ source.push({label: value.label, value: value.value});
53
+ });
54
+ });
55
+ tag.autocomplete('option', 'source', source);
56
+ }
@@ -0,0 +1,3 @@
1
+ .tags-autocomplete-loading {
2
+ background: white url('/assets/spinner.gif') right center no-repeat;
3
+ }
@@ -21,13 +21,20 @@ module Containers
21
21
  when :preliminary
22
22
  @container.update_attribute(:compute_resource_id, params[:container][:compute_resource_id])
23
23
  when :image
24
- @container.image = params[:image]
25
- @container.update_attributes(params[:container])
24
+ @container.update_attributes!(
25
+ :image => (image = DockerImage.find_or_create_by_image_id!(params[:image])),
26
+ :tag => DockerTag.find_or_create_by_tag_and_docker_image_id!(params[:container][:tag],
27
+ image.id))
26
28
  when :configuration
27
29
  @container.update_attributes(params[:container])
28
30
  when :environment
29
31
  @container.update_attributes(params[:container])
30
- @container.uuid = start_container.id
32
+ if (response = start_container)
33
+ @container.uuid = response.id
34
+ else
35
+ process_error(:object => @container.compute_resource, :render => 'environment')
36
+ return
37
+ end
31
38
  end
32
39
  render_wizard @container
33
40
  end
@@ -49,7 +56,7 @@ module Containers
49
56
  end
50
57
 
51
58
  def start_container
52
- @container.compute_resource.create_vm(@container.parametrize)
59
+ @container.compute_resource.create_container(@container.parametrize)
53
60
  end
54
61
  end
55
62
  end
@@ -1,5 +1,8 @@
1
1
  class ContainersController < ::ApplicationController
2
- before_filter :find_resource, :only => [:show]
2
+ before_filter :find_container, :only => [:show,
3
+ :auto_complete_image,
4
+ :auto_complete_image_tags,
5
+ :commit]
3
6
 
4
7
  def index
5
8
  @container_resources = allowed_resources.select { |cr| cr.provider == 'Docker' }
@@ -20,7 +23,8 @@ class ContainersController < ::ApplicationController
20
23
  def destroy
21
24
  if resource_deletion
22
25
  process_success(:success_redirect => containers_path,
23
- :success_msg => _("Container #{@deleted_identifier} is being deleted."))
26
+ :success_msg => (_("Container %s is being deleted.") %
27
+ @deleted_identifier))
24
28
  else
25
29
  process_error(:redirect => containers_path)
26
30
  end
@@ -31,8 +35,51 @@ class ContainersController < ::ApplicationController
31
35
  def show
32
36
  end
33
37
 
38
+ def auto_complete_image
39
+ if @container.compute_resource.exist?(params[:search])
40
+ render :text => 'true'
41
+ else
42
+ render :text => 'false'
43
+ end
44
+ end
45
+
46
+ def auto_complete_image_tags
47
+ images = @container.compute_resource.all_images(params[:search]).map do |image|
48
+ image.info['RepoTags'].map do |image_tag|
49
+ _, tag = image_tag.split(':')
50
+ { :label => CGI.escapeHTML(tag), :value => CGI.escapeHTML(tag) }
51
+ end
52
+ end
53
+ render :json => images.flatten
54
+ end
55
+
56
+ def commit
57
+ Docker::Container.get(@container.uuid).commit(:author => params[:commit][:author],
58
+ :repo => params[:commit][:repo],
59
+ :tag => params[:commit][:tag],
60
+ :comment => params[:commit][:comment])
61
+
62
+ process_success :success_redirect => :back,
63
+ :success_msg => _("%{container} commit was successful") %
64
+ { :container => @container }
65
+ rescue => e
66
+ process_error :redirect => :back, :error_msg => _("Failed to commit %{container}: %{e}") %
67
+ { :container => @container, :e => e }
68
+ end
69
+
34
70
  private
35
71
 
72
+ def action_permission
73
+ case params[:action]
74
+ when 'auto_complete_image', 'auto_complete_image_tags'
75
+ :view
76
+ when 'commit'
77
+ :commit
78
+ else
79
+ super
80
+ end
81
+ end
82
+
36
83
  def resource_deletion
37
84
  # Unmanaged container - only present in Compute Resource
38
85
  if params[:compute_resource_id].present?
@@ -59,4 +106,14 @@ class ContainersController < ::ApplicationController
59
106
  def allowed_resources
60
107
  ComputeResource.authorized(:view_compute_resources)
61
108
  end
109
+
110
+ # To be replaced by find_resource after 1.6 support is deprecated
111
+ def find_container
112
+ if params[:id].blank?
113
+ not_found
114
+ return
115
+ end
116
+ @container = Container.authorized("#{action_permission}_#{controller_name}".to_sym)
117
+ .find(params[:id])
118
+ end
62
119
  end
@@ -33,12 +33,7 @@ module ContainersHelper
33
33
  @compute_resource = container.compute_resource
34
34
  title_actions(
35
35
  button_group(
36
- link_to_if_authorized(_("Commit"), hash_for_container_path(:id => container)
37
- .merge(:auth_object => container,
38
- :permission => 'commit_containers'),
39
- :title => _('Saves differences between image' \
40
- 'and current state of container' \
41
- 'as a new image'))
36
+ link_to(_('Commit'), '#commit-modal', :'data-toggle' => 'modal')
42
37
  ),
43
38
  button_group(vm_power_action(container.in_fog)),
44
39
  button_group(
@@ -51,4 +46,9 @@ module ContainersHelper
51
46
  )
52
47
  )
53
48
  end
49
+
50
+ def auto_complete_search(name, val, options = {})
51
+ addClass options, 'form-control'
52
+ text_field_tag(name, val, options)
53
+ end
54
54
  end
@@ -1,4 +1,6 @@
1
1
  class Container < ActiveRecord::Base
2
+ include Authorizable
3
+
2
4
  belongs_to :compute_resource
3
5
  belongs_to :image, :class_name => 'DockerImage', :foreign_key => 'docker_image_id'
4
6
  belongs_to :tag, :class_name => 'DockerTag', :foreign_key => 'docker_tag_id'
@@ -8,19 +10,13 @@ class Container < ActiveRecord::Base
8
10
  :attach_stdout, :attach_stderr, :tag, :uuid
9
11
 
10
12
  def parametrize
11
- { :name => name, :image => tag.tag.blank? ? image.image_id : "#{image.image_id}:#{tag.tag}",
12
- :tty => tty, :memory => memory, :cmd => command.nil? ? '' : command.split(','),
13
- :attach_stdout => attach_stdout, :attach_stdout => attach_stdout,
14
- :attach_stderr => attach_stderr, :cpushares => cpu_shares, :cpuset => cpu_set }
15
- end
16
-
17
- def image=(image_id)
18
- self[:docker_image_id] = DockerImage.find_or_create_by_image_id!(image_id).id
19
- end
20
-
21
- def tag=(tag_name)
22
- self[:docker_tag_id] = DockerTag
23
- .find_or_create_by_tag_and_docker_image_id!(tag_name, image.id).id
13
+ { 'name' => name, # key has to be lower case to be picked up by the Docker API
14
+ 'Image' => tag.tag.blank? ? image.image_id : "#{image.image_id}:#{tag.tag}",
15
+ 'Tty' => tty, 'Memory' => memory,
16
+ 'Entrypoint' => entrypoint.try(:split), 'Cmd' => command.try(:split),
17
+ 'AttachStdout' => attach_stdout, 'AttachStdin' => attach_stdin,
18
+ 'AttachStderr' => attach_stderr, 'CpuShares' => cpu_shares,
19
+ 'Cpuset' => cpu_set }
24
20
  end
25
21
 
26
22
  def in_fog
@@ -4,4 +4,6 @@ class DockerImage < ActiveRecord::Base
4
4
  has_many :containers
5
5
 
6
6
  attr_accessible :image_id, :size
7
+
8
+ validates :image_id, :presence => true, :uniqueness => true
7
9
  end
@@ -3,5 +3,6 @@ class DockerTag < ActiveRecord::Base
3
3
 
4
4
  attr_accessible :tag, :image
5
5
 
6
+ validates :tag, :presence => true, :uniqueness => { :scope => :docker_image_id }
6
7
  validates :image, :presence => true
7
8
  end
@@ -20,34 +20,43 @@ module ForemanDocker
20
20
  super.merge(:mac => :mac)
21
21
  end
22
22
 
23
- # FIXME
24
- def max_cpu_count
25
- 8
26
- end
27
-
28
23
  def max_memory
29
24
  16 * 1024 * 1024 * 1024
30
25
  end
31
26
 
32
27
  def available_images
33
- client.images
28
+ client.images.all
29
+ end
30
+
31
+ def all_images(filter = '')
32
+ client # initialize Docker-Api
33
+ # we are using an older version of docker-api, which differs from the current
34
+ ::Docker::Image.all('filter' => filter)
35
+ end
36
+
37
+ def exist?(name)
38
+ ::Docker::Image.exist?(name)
34
39
  end
35
40
 
36
41
  def image(id)
37
- client.image.get(id) || fail(ActiveRecord::RecordNotFound)
42
+ client.image_get(id)
43
+ end
44
+
45
+ def search(term = '')
46
+ client.images.image_search(:term => term)
38
47
  end
39
48
 
40
49
  def provider_friendly_name
41
50
  'Docker'
42
51
  end
43
52
 
44
- def create_vm(args = {})
53
+ def create_container(args = {})
45
54
  options = vm_instance_defaults.merge(args)
46
- logger.debug("creating Docker with the following options: #{options.inspect}")
47
- client.servers.create options
48
- rescue Excon::Errors::SocketError, Fog::Errors::Error => e
55
+ logger.debug("Creating container with the following options: #{options.inspect}")
56
+ ::Docker::Container.create(options)
57
+ rescue Excon::Errors::Error, ::Docker::Error::DockerError => e
49
58
  logger.debug "Fog error: #{e.message}\n " + e.backtrace.join("\n ")
50
- errors.add(:base, e.message.to_s)
59
+ errors.add(:base, _("Error creating container. Check the Foreman logs: %s") % e.message.to_s)
51
60
  false
52
61
  end
53
62
 
@@ -56,6 +65,16 @@ module ForemanDocker
56
65
  'cmd' => ['/bin/bash'])
57
66
  end
58
67
 
68
+ def console(uuid)
69
+ test_connection
70
+ container = ::Docker::Container.get(uuid)
71
+ {
72
+ :name => container.info['Name'],
73
+ 'timestamp' => Time.now.utc,
74
+ 'output' => container.logs(:stdout => true, :tail => 100)
75
+ }
76
+ end
77
+
59
78
  def test_connection(options = {})
60
79
  super
61
80
  client
@@ -32,4 +32,6 @@
32
32
  <% end %>
33
33
  </table>
34
34
 
35
- <%= will_paginate_with_info containers %>
35
+ <!-- To be replaced by will_paginate_with_info after 1.6 support is deprecated -->
36
+ <%= page_entries_info containers %>
37
+ <%= will_paginate containers %>
@@ -102,3 +102,45 @@
102
102
  <% end %>
103
103
  </div>
104
104
  </div>
105
+
106
+ <div id="commit-modal" class="modal fade">
107
+ <div class="modal-dialog">
108
+ <div class="modal-content">
109
+ <div class="modal-header">
110
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
111
+ <h4 class="modal-title"><%= _('Commit this container state') %></h4>
112
+ </div>
113
+ <div class="modal-body">
114
+ This will save your current container state to an image.
115
+ <hr/>
116
+ <%= form_tag commit_container_path(:id => @container.id), :id => 'commit-form', :class => 'form-horizontal' do %>
117
+ <div class="form-group">
118
+ <%= label_tag "commit[repo]", _("Repo"), :class=>"col-sm-2 control-label" %>
119
+ <%= text_field :commit, :repo, { :class => "col-sm-8", :focus_on_load => true,
120
+ :placeholder => _('docker/my-committed-image') } %>
121
+ </div>
122
+ <div class="form-group">
123
+ <%= label_tag "commit[tag]", _("Tag"), :class=>"col-sm-2 control-label" %>
124
+ <%= text_field :commit, :tag, { :class => "col-sm-8", :focus_on_load => true,
125
+ :placeholder => _('latest') } %>
126
+ </div>
127
+ <div class="form-group">
128
+ <%= label_tag "commit[author]", _("Author"), :class=>"col-sm-2 control-label" %>
129
+ <%= text_field :commit, :author, { :class => "col-sm-8",
130
+ :placeholder => _('Foreman user <foremaner@theforeman.org>') } %>
131
+ </div>
132
+ <div class="form-group">
133
+ <%= label_tag "commit[comment]", _("Comment"), :class=>"col-sm-2 control-label" %>
134
+ <%= text_field :commit, :comment, { :class => "col-sm-8",
135
+ :placeholder => _('Description of the commit') } %>
136
+ </div>
137
+ <div class="modal-footer">
138
+ <button type="button" class="btn btn-default" data-dismiss="modal"><%= _('Cancel') %></button>
139
+ <%= button_tag(:type => 'submit', :class => 'btn btn-primary') do %>
140
+ <%= _('Submit') %> <span class="glyphicon glyphicon-cloud-upload"></span>
141
+ <% end %>
142
+ </div>
143
+ <% end %>
144
+ </div>
145
+ </div>
146
+ </div>
@@ -3,12 +3,16 @@
3
3
  <h3><%= _("Basic options") %></h3>
4
4
  <%= text_f f, :name, :size => 'col-md-4' %>
5
5
  <%= text_f f, :command, :size => 'col-md-4' %>
6
- <%= text_f f, :entrypoint, :size => 'col-md-4' %>
6
+ <%= text_f f, :entrypoint, :size => 'col-md-4', :placeholder => '/bin/sh -c by default' %>
7
7
  <hr>
8
8
  <h3><%= _("Compute options") %></h3>
9
- <%= selectable_f f, :cpu_set, 1..f.object.compute_resource.max_cpu_count, { }, :class => "col-md-2", :label => _('Cores') %>
10
- <%= text_f f, :cpu_shares, :class => "col-md-2", :label => _('CPU shares') %>
11
- <%= selectable_f f, :memory, memory_options(f.object.compute_resource.max_memory), { }, :class => "col-md-2", :label => _('Memory') %>
9
+ <%= text_f f, :cpu_set, :class => "col-md-2", :label => _('CPU sets'),
10
+ :placeholder => '0-2,16 represents CPUs 0, 1, 2, and 16.',
11
+ :help_block => link_to(_("learn more about CPU sets"), 'http://docs.fedoraproject.org/en-US/Fedora/17/html/Resource_Management_Guide/sec-cpuset.html') %>
12
+ <%= text_f f, :cpu_shares, :class => "col-md-2", :label => _('CPU shares'),
13
+ :placeholder => 'relative share of CPU time in cgroup',
14
+ :help_block => link_to(_("learn more about CPU shares"), 'http://docs.fedoraproject.org/en-US/Fedora/17/html/Resource_Management_Guide/sec-cpuset.html') %>
15
+ <%= text_f f, :memory, :class => "col-md-2", :label => _('Memory'), :placeholder => '512m limits container memory usage to 512 MB' %>
12
16
  <%= render :partial => 'form_buttons' %>
13
17
  <% end %>
14
18
  <% end %>
@@ -1,33 +1,45 @@
1
+ <%= javascript 'foreman_docker/image_step' %>
2
+ <%= stylesheet 'foreman_docker/autocomplete' %>
1
3
  <%= render :layout => 'title', :locals => { :step => 2 } do %>
4
+ <%= form_for @container, :url => wizard_path, :method => :put do |f| %>
5
+
2
6
  <ul class="nav nav-tabs" data-tabs="tabs">
3
7
  <li class="active"><a href="#primary" data-toggle="tab">
4
8
  <span class="glyphicon glyphicon-cloud-download"></span>
5
9
  <%= _("Docker hub") %>
6
10
  </a></li>
7
- <li><a href="#others" data-toggle="tab">
8
- <span class="glyphicon glyphicon-globe"></span>
9
- <%= _("Others") %>
10
- </a></li>
11
11
  </ul>
12
+
12
13
  <div class="tab-content">
13
- <%= form_for @container, :url => wizard_path, :method => :put do |f| %>
14
- <div class="tab-pane active" id="primary">
15
- <div class="input-group col-md-6">
16
- <%= auto_complete_search(:image, '',
17
- :path => 'containers_path',
18
- :value => f.object.image.present? ? f.object.image.image_id : '',
19
- :placeholder => ('Find your favorite container, e.g: centos:latest')) %>
20
- <span class="input-group-btn">
21
- <%= button_tag(:class => 'btn btn-default') do %>
14
+ <div class="tab-pane active" id="hub">
15
+ <div class="input-group col-md-6">
16
+ <%= auto_complete_search(:image, '',
17
+ :'data-url' => auto_complete_image_container_path(@container),
18
+ :value => f.object.image.present? ? f.object.image.image_id : '',
19
+ :id => :search,
20
+ :placeholder => _('Find your favorite container, e.g: centos:latest')) %>
21
+ <span class="input-group-addon glyphicon" id="search-addon"></span>
22
+ <span class="input-group-btn">
23
+ <%= button_tag(:class => 'btn btn-default',
24
+ :type => 'button',
25
+ :onclick => "$('#search').trigger('focus')") do %>
22
26
  <span class="glyphicon glyphicon-search"></span>
23
27
  <%= _("Search") %>
24
28
  <% end %>
25
- </span>
26
- </div>
27
- <hr>
28
- <%= text_f f, :tag, :value => f.object.tag.present? ? f.object.tag.tag : '', :size => 'col-md-4' %>
29
+ </span>
30
+ </div>
31
+ <br/>
32
+ <div class="input-group col-md-6">
33
+ <%= text_f f, :tag,
34
+ :size => 'col-md-6',
35
+ :value => f.object.tag.present? ? f.object.tag.tag : '',
36
+ :id => 'tag',
37
+ :'data-url' => auto_complete_image_tags_container_path(@container) %>
29
38
  </div>
39
+ <hr/>
30
40
  <%= render :partial => 'form_buttons' %>
31
- <% end %>
41
+ </div>
32
42
  </div>
43
+
44
+ <% end %>
33
45
  <% end %>
@@ -1,5 +1,10 @@
1
1
  Rails.application.routes.draw do
2
2
  resources :containers, :only => [:index, :new, :show, :destroy] do
3
+ member do
4
+ post :commit
5
+ end
3
6
  resources :steps, :controller => 'containers/steps', :only => [:show, :update]
7
+ get :auto_complete_image, :on => :member
8
+ get :auto_complete_image_tags, :on => :member
4
9
  end
5
10
  end
@@ -0,0 +1,9 @@
1
+ class ChangeMemoryInContainer < ActiveRecord::Migration
2
+ def up
3
+ change_column :containers, :memory, :string
4
+ end
5
+
6
+ def down
7
+ change_column :containers, :memory, :integer
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class ChangeCpusetInContainer < ActiveRecord::Migration
2
+ def up
3
+ change_column :containers, :cpu_set, :string
4
+ end
5
+
6
+ def down
7
+ change_column :containers, :cpu_set, :integer
8
+ end
9
+ end
@@ -3,22 +3,26 @@ require 'gettext_i18n_rails'
3
3
  require 'fog'
4
4
  require 'fog/fogdocker'
5
5
  require 'wicked'
6
+ require 'docker'
6
7
 
7
8
  module ForemanDocker
8
9
  # Inherit from the Rails module of the parent app (Foreman), not the plugin.
9
10
  # Thus, inherits from ::Rails::Engine and not from Rails::Engine
10
11
  class Engine < ::Rails::Engine
12
+ engine_name 'foreman_docker'
13
+
11
14
  initializer 'foreman_docker.load_app_instance_data' do |app|
12
15
  app.config.paths['db/migrate'] += ForemanDocker::Engine.paths['db/migrate'].existent
13
16
  end
14
17
 
15
18
  initializer "foreman_docker.assets.precompile" do |app|
16
- app.config.assets.precompile += ['foreman_docker/terminal.css']
19
+ app.config.assets.precompile += %w(foreman_docker/terminal.css foreman_docker/image_step.js)
17
20
  end
18
21
 
19
22
  initializer 'foreman_docker.configure_assets', :group => :assets do
20
- SETTINGS[:foreman_docker_engine] =
21
- { :assets => { :precompile => ['foreman_docker/terminal.css'] } }
23
+ SETTINGS[:foreman_docker] =
24
+ { :assets => { :precompile => ['foreman_docker/terminal.css',
25
+ 'foreman_docker/image_step.js'] } }
22
26
  end
23
27
 
24
28
  initializer 'foreman_docker.register_gettext', :after => :load_config_initializers do
@@ -44,7 +48,10 @@ module ForemanDocker
44
48
  end
45
49
 
46
50
  security_block :containers do
47
- permission :view_containers, :containers => [:index, :show]
51
+ permission :view_containers, :containers => [:index, :show,
52
+ :auto_complete_image,
53
+ :auto_complete_image_tags]
54
+ permission :commit_containers, :containers => [:commit]
48
55
  permission :create_containers, :'containers/steps' => [:show, :update],
49
56
  :containers => [:new]
50
57
  permission :destroy_containers, :containers => [:destroy]
@@ -1,3 +1,3 @@
1
1
  module ForemanDocker
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -24,4 +24,17 @@ class ContainersControllerTest < ActionController::TestCase
24
24
  :id => container.id }, set_session_user
25
25
  assert_redirected_to containers_path
26
26
  end
27
+
28
+ test 'committing a managed container' do
29
+ container = FactoryGirl.create(:container)
30
+ request.env['HTTP_REFERER'] = container_path(:id => container.id)
31
+ commit_hash = { :author => 'a', :repo => 'b', :tag => 'c', :comment => 'd' }
32
+
33
+ mock_container = mock
34
+ ::Docker::Container.expects(:get).with(container.uuid).returns(mock_container)
35
+ mock_container.expects(:commit).with(commit_hash)
36
+
37
+ post :commit, { :commit => commit_hash,
38
+ :id => container.id }, set_session_user
39
+ end
27
40
  end
@@ -18,16 +18,30 @@ module Containers
18
18
  assert_equal DockerTag.find_by_tag('latest'), @container.tag
19
19
  end
20
20
 
21
- test 'uuid of the created container is saved at the end of the wizard' do
22
- Fog.mock!
23
- @container.update_attribute(:image, 'centos')
24
- @container.update_attribute(:tag, 'latest')
25
- fake_container = @container.compute_resource.create_vm
26
- fake_container.id = SecureRandom.uuid
27
- ForemanDocker::Docker.any_instance.expects(:create_vm).returns(fake_container)
28
- put :update, { :id => :environment,
29
- :container_id => @container.id }, set_session_user
30
- assert_equal fake_container.id, Container.find(@container.id).uuid
21
+ context 'container creation' do
22
+ setup do
23
+ @container.update_attribute(:image, (image = FactoryGirl.create(:docker_image,
24
+ :image_id => 'centos')))
25
+ @container.update_attribute(:tag, FactoryGirl.create(:docker_tag, :image => image,
26
+ :tag => 'latest'))
27
+ end
28
+
29
+ test 'uuid of the created container is saved at the end of the wizard' do
30
+ Fog.mock!
31
+ fake_container = @container.compute_resource.send(:client).servers.first
32
+ ForemanDocker::Docker.any_instance.expects(:create_container).returns(fake_container)
33
+ put :update, { :id => :environment,
34
+ :container_id => @container.id }, set_session_user
35
+ assert_equal fake_container.id, Container.find(@container.id).uuid
36
+ end
37
+
38
+ test 'errors are displayed when container creation fails' do
39
+ Docker::Container.expects(:create).raises(Docker::Error::DockerError, 'some error')
40
+ put :update, { :id => :environment,
41
+ :container_id => @container.id }, set_session_user
42
+ assert_template 'environment'
43
+ assert_match(/some error/, flash[:error])
44
+ end
31
45
  end
32
46
 
33
47
  test 'wizard finishes with a redirect to the managed container' do
@@ -0,0 +1,11 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class ContainerTest < ActiveSupport::TestCase
4
+ test 'validations do not happen if inactive' do
5
+ FactoryGirl.build(:container)
6
+ end
7
+
8
+ test 'attributes are validated when active' do
9
+ FactoryGirl.build(:container)
10
+ end
11
+ end
@@ -9,4 +9,15 @@ class DockerImageTest < ActiveSupport::TestCase
9
9
  refute DockerImage.exists?(image.id)
10
10
  refute DockerTag.exists?(tag.id)
11
11
  end
12
+
13
+ context 'validations' do
14
+ test 'without image_id is invalid' do
15
+ refute FactoryGirl.build(:docker_image, :image_id => '').valid?
16
+ end
17
+
18
+ test 'image_id has to be unique' do
19
+ old_image = FactoryGirl.create(:docker_image)
20
+ refute FactoryGirl.build(:docker_image, :image_id => old_image.image_id).valid?
21
+ end
22
+ end
12
23
  end
@@ -0,0 +1,35 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class DockerTagTest < ActiveSupport::TestCase
4
+ test 'creating fails if no image is provided' do
5
+ tag = FactoryGirl.build(:docker_tag, :image => nil)
6
+ refute tag.valid?
7
+ assert tag.errors.size >= 1
8
+ end
9
+
10
+ test 'creating succeeds if an image is provided' do
11
+ tag = FactoryGirl.build(:docker_tag)
12
+ tag.image = FactoryGirl.build(:docker_image)
13
+
14
+ assert tag.valid?
15
+ assert tag.save
16
+ end
17
+
18
+ context 'validations' do
19
+ test 'tag has to be present' do
20
+ refute FactoryGirl.build(:docker_tag, :tag => '').valid?
21
+ end
22
+
23
+ test 'tag is unique within image scope' do
24
+ image = FactoryGirl.create(:docker_image)
25
+ tag = FactoryGirl.create(:docker_tag, :image => image)
26
+ duplicated_tag = FactoryGirl.build(:docker_tag, :image => image, :tag => tag.tag)
27
+ refute duplicated_tag.valid?
28
+ end
29
+
30
+ test 'tag is not unique for different images' do
31
+ tag = FactoryGirl.create(:docker_tag)
32
+ assert FactoryGirl.build(:docker_tag, :tag => tag.tag).valid?
33
+ end
34
+ end
35
+ end
metadata CHANGED
@@ -1,85 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_docker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Lobato, Amos Benari
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-21 00:00:00.000000000 Z
11
+ date: 2014-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rake
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - '>='
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - '>='
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: fog
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - '>='
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: docker-api
43
15
  requirement: !ruby/object:Gem::Requirement
44
16
  requirements:
45
17
  - - ~>
46
18
  - !ruby/object:Gem::Version
47
- version: 1.13.6
19
+ version: '1.13'
48
20
  type: :runtime
49
21
  prerelease: false
50
22
  version_requirements: !ruby/object:Gem::Requirement
51
23
  requirements:
52
24
  - - ~>
53
25
  - !ruby/object:Gem::Version
54
- version: 1.13.6
26
+ version: '1.13'
55
27
  - !ruby/object:Gem::Dependency
56
28
  name: wicked
57
29
  requirement: !ruby/object:Gem::Requirement
58
30
  requirements:
59
- - - '>='
31
+ - - ~>
60
32
  - !ruby/object:Gem::Version
61
- version: '0'
33
+ version: '1.1'
62
34
  type: :runtime
63
35
  prerelease: false
64
36
  version_requirements: !ruby/object:Gem::Requirement
65
37
  requirements:
66
- - - '>='
38
+ - - ~>
67
39
  - !ruby/object:Gem::Version
68
- version: '0'
40
+ version: '1.1'
69
41
  - !ruby/object:Gem::Dependency
70
42
  name: rubocop
71
43
  requirement: !ruby/object:Gem::Requirement
72
44
  requirements:
73
45
  - - ~>
74
46
  - !ruby/object:Gem::Version
75
- version: 0.26.1
47
+ version: '0.26'
76
48
  type: :development
77
49
  prerelease: false
78
50
  version_requirements: !ruby/object:Gem::Requirement
79
51
  requirements:
80
52
  - - ~>
81
53
  - !ruby/object:Gem::Version
82
- version: 0.26.1
54
+ version: '0.26'
83
55
  description: Provision and manage Docker containers and images from Foreman.
84
56
  email:
85
57
  - dlobatog@redhat.com, abenari@redhat.com
@@ -90,6 +62,8 @@ files:
90
62
  - LICENSE
91
63
  - README.md
92
64
  - Rakefile
65
+ - app/assets/javascripts/foreman_docker/image_step.js
66
+ - app/assets/stylesheets/foreman_docker/autocomplete.css.scss
93
67
  - app/assets/stylesheets/foreman_docker/terminal.css.scss
94
68
  - app/controllers/containers/steps_controller.rb
95
69
  - app/controllers/containers_controller.rb
@@ -126,6 +100,8 @@ files:
126
100
  - db/migrate/20141009011026_add_attributes_to_container.rb
127
101
  - db/migrate/20141010173220_create_docker_images.rb
128
102
  - db/migrate/20141018110810_add_uuid_to_containers.rb
103
+ - db/migrate/20141028164206_change_memory_in_container.rb
104
+ - db/migrate/20141028164633_change_cpuset_in_container.rb
129
105
  - lib/foreman_docker.rb
130
106
  - lib/foreman_docker/engine.rb
131
107
  - lib/foreman_docker/tasks/test.rake
@@ -137,10 +113,10 @@ files:
137
113
  - test/factories/docker_tag.rb
138
114
  - test/functionals/container_controller_test.rb
139
115
  - test/functionals/containers_steps_controller_test.rb
140
- - test/models/container_test.rb
141
- - test/models/docker_image_test.rb
142
- - test/models/docker_tag_test.rb
143
116
  - test/test_plugin_helper.rb
117
+ - test/units/container_test.rb
118
+ - test/units/docker_image_test.rb
119
+ - test/units/docker_tag_test.rb
144
120
  homepage: http://github.com/theforeman/foreman-docker
145
121
  licenses:
146
122
  - GPL-3
@@ -168,12 +144,12 @@ summary: Provision and manage Docker containers and images from Foreman
168
144
  test_files:
169
145
  - test/functionals/containers_steps_controller_test.rb
170
146
  - test/functionals/container_controller_test.rb
171
- - test/models/docker_image_test.rb
172
- - test/models/container_test.rb
173
- - test/models/docker_tag_test.rb
174
147
  - test/factories/docker_image.rb
175
148
  - test/factories/compute_resources.rb
176
149
  - test/factories/docker_tag.rb
177
150
  - test/factories/containers.rb
151
+ - test/units/docker_image_test.rb
152
+ - test/units/container_test.rb
153
+ - test/units/docker_tag_test.rb
178
154
  - test/test_plugin_helper.rb
179
155
  has_rdoc:
@@ -1,36 +0,0 @@
1
- require 'test_plugin_helper'
2
-
3
- class ContainerTest < ActiveSupport::TestCase
4
- context 'update attributes' do
5
- setup do
6
- @container = FactoryGirl.create(:container)
7
- end
8
-
9
- test 'update image reuses previously created image' do
10
- assert_difference('DockerImage.count', 1) do
11
- @container.update_attribute(:image, "centos")
12
- end
13
- assert_equal "centos", @container.image.image_id
14
- refute_nil DockerImage.find_by_image_id("centos")
15
-
16
- assert_difference('DockerImage.count', 1) do
17
- @container.update_attribute(:image, "redis")
18
- end
19
- assert_equal "redis", @container.image.image_id
20
-
21
- assert_difference('DockerImage.count', 0) do
22
- @container.update_attribute(:image, "centos")
23
- end
24
- end
25
-
26
- test "update tag uses container's associated image" do
27
- @container.update_attribute(:image, 'centos')
28
- assert_difference('DockerTag.count', 1) do
29
- @container.update_attribute(:tag, 'latest')
30
- end
31
-
32
- assert_equal 'latest', @container.tag.tag
33
- assert_equal @container.tag.image, @container.image
34
- end
35
- end
36
- end
@@ -1,17 +0,0 @@
1
- require 'test_plugin_helper'
2
-
3
- class DockerTagTest < ActiveSupport::TestCase
4
- test 'creating fails if no image is provided' do
5
- tag = DockerTag.new(FactoryGirl.attributes_for(:docker_tag))
6
- refute tag.valid?
7
- assert tag.errors.size >= 1
8
- end
9
-
10
- test 'creating succeeds if an image is provided' do
11
- tag = FactoryGirl.build(:docker_tag)
12
- tag.image = FactoryGirl.create(:docker_image)
13
-
14
- assert tag.valid?
15
- assert tag.save
16
- end
17
- end