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.
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