foreman_docker 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -3
  3. data/app/assets/javascripts/foreman_docker/image_step.js +44 -5
  4. data/app/controllers/concerns/foreman_docker/find_container.rb +19 -0
  5. data/app/controllers/containers/steps_controller.rb +25 -39
  6. data/app/controllers/containers_controller.rb +10 -43
  7. data/app/controllers/image_search_controller.rb +88 -0
  8. data/app/controllers/registries_controller.rb +47 -0
  9. data/app/helpers/container_steps_helper.rb +21 -4
  10. data/app/helpers/containers_helper.rb +19 -7
  11. data/app/models/concerns/foreman_docker/parameter_validators.rb +11 -0
  12. data/app/models/container.rb +20 -7
  13. data/app/models/docker_container_wizard_state.rb +30 -0
  14. data/app/models/docker_container_wizard_states/configuration.rb +7 -0
  15. data/app/models/docker_container_wizard_states/environment.rb +21 -0
  16. data/app/models/docker_container_wizard_states/environment_variable.rb +7 -0
  17. data/app/models/docker_container_wizard_states/image.rb +11 -0
  18. data/app/models/docker_container_wizard_states/preliminary.rb +11 -0
  19. data/app/models/docker_registry.rb +28 -0
  20. data/app/models/environment_variable.rb +5 -0
  21. data/app/models/foreman_docker/docker.rb +22 -3
  22. data/app/models/service/containers.rb +38 -0
  23. data/app/models/service/registry_api.rb +26 -0
  24. data/app/views/containers/_list.html.erb +19 -18
  25. data/app/views/containers/index.html.erb +11 -6
  26. data/app/views/containers/show.html.erb +26 -7
  27. data/app/views/containers/steps/_form_buttons.html.erb +1 -1
  28. data/app/views/containers/steps/configuration.html.erb +2 -2
  29. data/app/views/containers/steps/environment.html.erb +19 -6
  30. data/app/views/containers/steps/image.html.erb +58 -39
  31. data/app/views/containers/steps/preliminary.html.erb +21 -4
  32. data/app/views/foreman_docker/common_parameters/_environment_variable.html.erb +18 -0
  33. data/app/views/image_search/_repository_search_results.html.erb +12 -0
  34. data/app/views/registries/_form.html.erb +26 -0
  35. data/app/views/registries/edit.html.erb +3 -0
  36. data/app/views/registries/index.html.erb +28 -0
  37. data/app/views/registries/new.html.erb +3 -0
  38. data/config/routes.rb +11 -2
  39. data/db/migrate/20141024163003_create_docker_registries.rb +22 -0
  40. data/db/migrate/20141120123003_add_user_credentials_to_docker_registries.rb +6 -0
  41. data/db/migrate/20141209182008_remove_docker_tables.rb +72 -0
  42. data/db/migrate/20141222113313_create_wizard_states.rb +42 -0
  43. data/lib/foreman_docker/engine.rb +25 -5
  44. data/lib/foreman_docker/version.rb +1 -1
  45. data/test/factories/containers.rb +2 -0
  46. data/test/factories/docker_registry.rb +16 -0
  47. data/test/functionals/containers_steps_controller_test.rb +7 -40
  48. data/test/integration/container_steps_test.rb +24 -0
  49. data/test/integration/container_test.rb +18 -0
  50. data/test/units/container_test.rb +0 -7
  51. data/test/units/containers_service_test.rb +21 -0
  52. data/test/units/docker_registry_test.rb +24 -0
  53. metadata +45 -22
  54. data/app/models/docker_image.rb +0 -9
  55. data/app/models/docker_tag.rb +0 -8
  56. data/test/factories/docker_image.rb +0 -5
  57. data/test/factories/docker_tag.rb +0 -6
  58. data/test/units/docker_image_test.rb +0 -23
  59. data/test/units/docker_tag_test.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e7371179124d8fa414db7e60fbcab48110a4372
4
- data.tar.gz: 339e3c491804ae9432363136be744a48346af11c
3
+ metadata.gz: 4806b85ddbb93562b7c2c718f5daa8bf5a84d48d
4
+ data.tar.gz: 531ccfeaf0fadf4c227d89d98e1f53635a3dd7d2
5
5
  SHA512:
6
- metadata.gz: 08356581341a4c80c452ae75dc851ef81c9ec51c61df9106c44d99d8f157ceda5bdf1f0d325e6182983f8591d602f1998618f0058704a3791105465eb0a4d044
7
- data.tar.gz: b0c79637f96a572eadf4d82c2a322669d8152690e4a956be0696bd06ee32b82ab93d87dca0f5891799dc1cf58671f5043283a12d33d98a7d2ffe16a204e7ce00
6
+ metadata.gz: 8601bd6d7fab40b502542717f707e4fbe60252140da0483c975e3b31749d3db3261a92176cfbe989a70ca12767d0c3551b3cd7dfd6c330d6163fa6432a51f209
7
+ data.tar.gz: 721dc7939ecb7d142471baa127e30b83530b216b8f56738c609bdee784ba7346043f81513e13cd92d1eaed4adb4e973ca703f9ebff9384b5e33c9164e4e312db
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Foreman Docker Plugin
2
2
 
3
+ [![Code Climate](https://codeclimate.com/github/theforeman/foreman-docker/badges/gpa.svg)](https://codeclimate.com/github/theforeman/foreman-docker)
4
+ [![Gem Version](https://badge.fury.io/rb/foreman_docker.svg)](http://badge.fury.io/rb/foreman_docker)
5
+ [![Dependency Status](https://gemnasium.com/theforeman/foreman-docker.svg)](https://gemnasium.com/theforeman/foreman-docker)
6
+
3
7
  ```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
8
 
5
9
  * Website: [TheForeman.org](http://theforeman.org)
@@ -81,14 +85,17 @@ That's it. You're now ready to create and manage containers in your new Docker c
81
85
  | ---------------:| --------------:|
82
86
  | >= 1.5 | 0.0.1 - 0.0.3 |
83
87
  | >= 1.6 | 0.1.0 - 0.2.0 |
88
+ | >= 1.7 | 1.0.0 |
84
89
 
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
90
+ We will follow [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html). This means:
91
+ * MAJOR versions: will break compatibility with the latest supported Foreman version. For instance, foreman-docker 1.0 breaks Foreman 1.6 compatibility.
92
+ * MINOR versions: will enhance foreman-docker with features in a backwards-compatible manner.
93
+ * PATCH versions: will contain bugfixes for the latest minor version in a backwards-compatible manner.
88
94
 
89
95
  ## How to contribute?
90
96
 
91
97
  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:
98
+ * Code from the master branch can contain features only present in [Fog's](http://github.com/fog/fog) master branch, we commit to wait for the next Fog release to put that code in a foreman-docker release.
92
99
  * [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
100
  * [Rubocop](https://github.com/bbatsov/rubocop) will analyze your code, you can run it locally with `rake rubocop`.
94
101
  * 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
@@ -2,6 +2,7 @@ $(document).ready(function() {
2
2
  var tag = $('#tag');
3
3
  tag.autocomplete({
4
4
  source: [],
5
+ autoFocus: true,
5
6
  delay: 500,
6
7
  minLength: 0
7
8
  }).focus( function() {
@@ -9,25 +10,31 @@ $(document).ready(function() {
9
10
  });
10
11
 
11
12
  var target = $('#search');
12
- autoCompleteImage(target);
13
+ //autoCompleteRepo(target);
13
14
  target.autocomplete({
14
- source: function( request, response ) { autoCompleteImage(target); },
15
+ source: function( request, response ) { autoCompleteRepo(target); },
15
16
  delay: 500,
16
17
  minLength: 1
17
18
  });
19
+
20
+ $('#hub_tab').click( function() {
21
+ $('#wizard_states_image_registry_id').val('');
22
+ });
18
23
  });
19
24
 
20
- function autoCompleteImage(item) {
25
+ function autoCompleteRepo(item) {
21
26
  $.ajax({
22
27
  type:'get',
23
28
  url: $(item).attr('data-url'),
24
- data:'search=' + item.val(),
29
+ data: { search: item.val(), registry_id: $('#wizard_states_image_registry_id').val() },
30
+ //data:'search=' + item.val(),
25
31
  success:function (result) {
26
32
  if(result == 'true'){
27
33
  $('#search-addon').attr('title', 'Image found in the compute resource');
28
34
  $('#search-addon').removeClass('glyphicon-remove');
29
35
  $('#search-addon').css('color', 'lightgreen');
30
36
  $('#search-addon').addClass('glyphicon-ok');
37
+ setWaitingText('Image found: <strong>' + item.val() + '</strong>. Retrieving available tags, please wait...');
31
38
  setAutocompleteTags();
32
39
  } else {
33
40
  $('#search-addon').attr('title', 'Image NOT found in the compute resource');
@@ -45,12 +52,44 @@ function setAutocompleteTags() {
45
52
  tag.addClass('tags-autocomplete-loading');
46
53
  tag.val('');
47
54
  var source = [];
48
- $.getJSON( tag.data("url"), { search: $('#search').val() },
55
+ $.getJSON( tag.data("url"), { search: $('#search').val(), registry_id: $('#wizard_states_image_registry_id').val() },
49
56
  function(data) {
57
+ $('#searching_spinner').hide();
50
58
  tag.removeClass('tags-autocomplete-loading');
51
59
  $.each( data, function(index, value) {
52
60
  source.push({label: value.label, value: value.value});
53
61
  });
62
+ $('#tag').focus();
54
63
  });
55
64
  tag.autocomplete('option', 'source', source);
56
65
  }
66
+
67
+ function searchRepo(item) {
68
+ setWaitingText('<strong>Searching</strong> in the hub, this can be slow, please wait...');
69
+ $('#repository_search_results').html('');
70
+ $('#repository_search_results').show();
71
+ $.ajax({
72
+ type:'get',
73
+ dataType:'text',
74
+ url: $(item).attr('data-url'),
75
+ data: { search: $('#search').val(), registry_id: $('#wizard_states_image_registry_id').val() },
76
+ success: function (result) {
77
+ $('#repository_search_results').html(result);
78
+ },
79
+ complete: function (result) {
80
+ $('#searching_spinner').hide();
81
+ }
82
+ });
83
+ }
84
+
85
+ function repoSelected(item) {
86
+ $('#repository_search_results').hide();
87
+ setWaitingText('Image selected: <strong>' + item.text + '</strong>. Retrieving available tags, please wait...');
88
+ $('#search').val(item.text);
89
+ setAutocompleteTags(item);
90
+ }
91
+
92
+ function setWaitingText(string) {
93
+ $('#waiting_text').html(string);
94
+ $('#searching_spinner').show();
95
+ }
@@ -0,0 +1,19 @@
1
+ # To be replaced by find_resource, FindCommon after 1.6 support is deprecated
2
+ module ForemanDocker
3
+ module FindContainer
4
+ extend ActiveSupport::Concern
5
+
6
+ def find_container
7
+ if params[:id].blank?
8
+ not_found
9
+ return
10
+ end
11
+ @container = Container.authorized("#{action_permission}_#{controller_name}".to_sym)
12
+ .find(params[:id])
13
+ end
14
+
15
+ def allowed_resources
16
+ ForemanDocker::Docker.authorized(:view_compute_resources)
17
+ end
18
+ end
19
+ end
@@ -1,62 +1,48 @@
1
1
  module Containers
2
2
  class StepsController < ::ApplicationController
3
3
  include Wicked::Wizard
4
+ include ForemanDocker::FindContainer
4
5
 
5
6
  steps :preliminary, :image, :configuration, :environment
6
- before_filter :find_container
7
+
8
+ before_filter :build_state
9
+ before_filter :set_form
7
10
 
8
11
  def show
9
- case step
10
- when :preliminary
11
- @container_resources = ComputeResource.select { |cr| cr.provider == 'Docker' }
12
- when :image
13
- when :configuration
14
- when :environment
15
- end
12
+ @container_resources = allowed_resources if step == :preliminary
16
13
  render_wizard
17
14
  end
18
15
 
19
16
  def update
20
- case step
21
- when :preliminary
22
- @container.update_attribute(:compute_resource_id, params[:container][:compute_resource_id])
23
- when :image
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))
28
- when :configuration
29
- @container.update_attributes(params[:container])
30
- when :environment
31
- @container.update_attributes(params[:container])
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
17
+ if step == wizard_steps.last
18
+ create_container
19
+ else
20
+ render_wizard @state
38
21
  end
39
- render_wizard @container
40
22
  end
41
23
 
42
24
  private
43
25
 
44
- def finish_wizard_path
45
- container_path(:id => params[:container_id])
46
- end
47
-
48
- def allowed_resources
49
- ComputeResource.authorized(:view_compute_resources)
50
- end
51
-
52
- def find_container
53
- @container = Container.find(params[:container_id])
26
+ def build_state
27
+ @state = DockerContainerWizardState.find(params[:wizard_state_id])
28
+ @state.send(:"build_#{step}", params[:"docker_container_wizard_states_#{step}"])
54
29
  rescue ActiveRecord::RecordNotFound
55
30
  not_found
56
31
  end
57
32
 
58
- def start_container
59
- @container.compute_resource.create_container(@container.parametrize)
33
+ def set_form
34
+ instance_variable_set("@#{step}", @state.send(:"#{step}") || @state.send(:"build_#{step}"))
35
+ end
36
+
37
+ def create_container
38
+ @state.send(:"create_#{step}", params[:"docker_container_wizard_states_#{step}"])
39
+ container = Service::Containers.start_container!(@state)
40
+ if container.present?
41
+ process_success(:object => container, :success_redirect => container_path(container))
42
+ else
43
+ @environment = @state.environment
44
+ process_error(:object => @state.environment, :render => 'environment')
45
+ end
60
46
  end
61
47
  end
62
48
  end
@@ -1,11 +1,10 @@
1
1
  class ContainersController < ::ApplicationController
2
- before_filter :find_container, :only => [:show,
3
- :auto_complete_image,
4
- :auto_complete_image_tags,
5
- :commit]
2
+ include ForemanDocker::FindContainer
3
+
4
+ before_filter :find_container, :only => [:show, :commit]
6
5
 
7
6
  def index
8
- @container_resources = allowed_resources.select { |cr| cr.provider == 'Docker' }
7
+ @container_resources = allowed_resources
9
8
  if @container_resources.empty?
10
9
  warning('You need a Compute Resource of type Docker to start managing containers')
11
10
  redirect_to new_compute_resource_path
@@ -16,12 +15,12 @@ class ContainersController < ::ApplicationController
16
15
  end
17
16
 
18
17
  def new
19
- @container = Container.create
20
- redirect_to container_step_path(:container_id => @container.id, :id => :preliminary)
18
+ redirect_to wizard_state_step_path(:wizard_state_id => DockerContainerWizardState.create.id,
19
+ :id => :preliminary)
21
20
  end
22
21
 
23
22
  def destroy
24
- if resource_deletion
23
+ if container_deletion
25
24
  process_success(:success_redirect => containers_path,
26
25
  :success_msg => (_("Container %s is being deleted.") %
27
26
  @deleted_identifier))
@@ -35,24 +34,6 @@ class ContainersController < ::ApplicationController
35
34
  def show
36
35
  end
37
36
 
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
37
  def commit
57
38
  Docker::Container.get(@container.uuid).commit(:author => params[:commit][:author],
58
39
  :repo => params[:commit][:repo],
@@ -71,7 +52,7 @@ class ContainersController < ::ApplicationController
71
52
 
72
53
  def action_permission
73
54
  case params[:action]
74
- when 'auto_complete_image', 'auto_complete_image_tags'
55
+ when 'auto_complete_repository_name', 'auto_complete_tag', 'search_repository'
75
56
  :view
76
57
  when 'commit'
77
58
  :commit
@@ -80,13 +61,13 @@ class ContainersController < ::ApplicationController
80
61
  end
81
62
  end
82
63
 
83
- def resource_deletion
64
+ def container_deletion
84
65
  # Unmanaged container - only present in Compute Resource
85
66
  if params[:compute_resource_id].present?
86
67
  @deleted_identifier = params[:id]
87
68
  destroy_compute_resource_vm(params[:compute_resource_id], params[:id])
88
69
  else # Managed container
89
- find_resource
70
+ find_container
90
71
  @deleted_identifier = @container.name
91
72
 
92
73
  destroy_compute_resource_vm(@container.compute_resource, @container.uuid) &&
@@ -102,18 +83,4 @@ class ContainersController < ::ApplicationController
102
83
  logger.error "#{error.message} (#{error.class})\n#{error.backtrace.join("\n")}"
103
84
  false
104
85
  end
105
-
106
- def allowed_resources
107
- ComputeResource.authorized(:view_compute_resources)
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
119
86
  end
@@ -0,0 +1,88 @@
1
+ class ImageSearchController < ::ApplicationController
2
+ before_filter :find_resource
3
+
4
+ def auto_complete_repository_name
5
+ render :text => (use_hub? ? hub_image_exists?(params[:search]) :
6
+ registry_image_exists?(params[:search])).to_s
7
+ end
8
+
9
+ def auto_complete_image_tag
10
+ # This is the format jQuery UI autocomplete expects
11
+ tags = use_hub? ? hub_auto_complete_image_tags(params[:search]) :
12
+ registry_auto_complete_image_tags(params[:search])
13
+ respond_to do |format|
14
+ format.js do
15
+ tags.map! { |tag| { :label => CGI.escapeHTML(tag), :value => CGI.escapeHTML(tag) } }
16
+ render :json => tags
17
+ end
18
+ end
19
+ end
20
+
21
+ def search_repository
22
+ repositories = use_hub? ? hub_search_image(params[:search]) :
23
+ registry_search_image(params[:search])
24
+ respond_to do |format|
25
+ format.js do
26
+ render :partial => 'repository_search_results',
27
+ :locals => { :repositories => repositories }
28
+ end
29
+ end
30
+ end
31
+
32
+ def use_hub?
33
+ @registry.nil?
34
+ end
35
+
36
+ def hub_image_exists?(terms)
37
+ @compute_resource.exist?(terms)
38
+ end
39
+
40
+ def hub_auto_complete_image_tags(terms)
41
+ @compute_resource.tags(terms)
42
+ end
43
+
44
+ def hub_search_image(terms)
45
+ @compute_resource.search(terms)
46
+ end
47
+
48
+ def registry_image_exists?(term)
49
+ result = ::Service::RegistryApi.new(:url => @registry.url,
50
+ :user => @registry.username,
51
+ :password => @registry.password).search(term)
52
+ registry_name = term.split('/').size > 1 ? term :
53
+ 'library/' + term
54
+ result['results'].any? { |r| r['name'] == registry_name }
55
+ end
56
+
57
+ def registry_auto_complete_image_tags(terms)
58
+ ::Service::RegistryApi.new(:url => @registry.url,
59
+ :user => @registry.username,
60
+ :password => @registry.password).list_repository_tags(terms).keys
61
+ end
62
+
63
+ def registry_search_image(terms)
64
+ r = ::Service::RegistryApi.new(:url => @registry.url,
65
+ :user => @registry.username,
66
+ :password => @registry.password).search(terms)
67
+ r['results']
68
+ end
69
+
70
+ def action_permission
71
+ case params[:action]
72
+ when 'auto_complete_repository_name', 'auto_complete_image_tag', 'search_repository'
73
+ :search_repository
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ def find_resource
80
+ if params[:registry_id].present?
81
+ @registry = DockerRegistry.authorized(:view_registries).find(params[:registry_id])
82
+ else
83
+ @compute_resource = ComputeResource.authorized(:view_compute_resources).find(params[:id])
84
+ end
85
+ rescue ActiveRecord::RecordNotFound
86
+ not_found
87
+ end
88
+ end
@@ -0,0 +1,47 @@
1
+ class RegistriesController < ::ApplicationController
2
+ include Foreman::Controller::AutoCompleteSearch
3
+ before_filter :find_registry, :only => [:edit, :update, :destroy]
4
+
5
+ def index
6
+ @registries = DockerRegistry.search_for(params[:search], :order => params[:order])
7
+ .paginate :page => params[:page]
8
+ end
9
+
10
+ def new
11
+ @registry = DockerRegistry.new
12
+ end
13
+
14
+ def create
15
+ @registry = DockerRegistry.new(params[:docker_registry])
16
+ if @registry.save
17
+ process_success
18
+ else
19
+ process_error
20
+ end
21
+ end
22
+
23
+ def edit
24
+ end
25
+
26
+ def update
27
+ if @registry.update_attributes(params[:docker_registry])
28
+ process_success
29
+ else
30
+ process_error
31
+ end
32
+ end
33
+
34
+ def destroy
35
+ if @registry.destroy
36
+ process_success
37
+ else
38
+ process_error
39
+ end
40
+ end
41
+
42
+ def find_registry
43
+ @registry = DockerRegistry.find(params[:id])
44
+ rescue ActiveRecord::RecordNotFound
45
+ not_found
46
+ end
47
+ end