Project

General

Profile

Revision f02a9ce2

Added by Sebastian Gräßl over 5 years ago

Fixes #18733 - Prevent fallback to Docker Hub on registry search

When searching for an external registry the search would show
results from Docker Hub due to it searching via the compute
resource.

By using a registry parameter to indicate which search tab is
active this gets prevented on the backend as well as on the
client by not searching when no registry is selected.

View differences:

app/assets/javascripts/foreman_docker/image_step.js
26 26
  });
27 27
}
28 28

  
29
function paramsForSearch(registryType) {
30
  var image = getRepo(registryType),
31
      tag = getTag(registryType),
32
      registryId = $('#docker_container_wizard_states_image_registry_id').val(),
33
      params = {
34
        registry: registryType,
35
        search: image.val()
36
      }
37

  
38
  if (tag.val() != '') {
39
    params.search = image.val() + ':' + tag.val();
40
  };
41

  
42
  if (registryType == 'registry' && registryId != '') {
43
    params.registry_id = registryId;
44
  };
45

  
46
  return params;
47
}
48

  
29 49
function autoCompleteRepo(item) {
30 50
  var registryType = $(item).data('registry'),
31 51
      search_add_on = getImageConfirmation(registryType),
32
      tag = getTag(registryType);
52
      tag = getTag(registryType),
53
      params = paramsForSearch(registryType);
54

  
55
  if (params.search == '' ||
56
      (registryType == 'registry' && typeof params.registry_id == 'undefined')) {
57
    return;
58
  }
33 59

  
34 60
  // Patternfly spinner uses 'float: left' and moves it to the left of the
35 61
  // search button. Instead, we use FontAwesome's spinner to keep it at
......
39 65
  $.ajax({
40 66
    type:'get',
41 67
    url: $(item).attr('data-url'),
42
    data: { search: item.val(), registry_id: $('#docker_container_wizard_states_image_registry_id').val() },
68
    data: params,
43 69
    success:function (result) {
44 70
       if(result == 'true'){
45 71
        search_add_on.attr('title', 'Image found in the compute resource');
......
59 85
}
60 86

  
61 87
function setAutocompleteTags(registryType) {
62
  var tag = getTag(registryType);
88
  var registryType = registryType,
89
      tag = getTag(registryType),
90
      source = [];
91

  
63 92
  tag.addClass('spinner-label');
64 93
  tag.val('');
65
  var source = [];
66
  $.getJSON( tag.data("url"), { search: getRepo(registryType).val(), registry_id: $('#docker_container_wizard_states_image_registry_id').val() },
94

  
95
  $.getJSON( tag.data("url"),
96
      paramsForSearch(registryType),
67 97
      function(data) {
68 98
        getSearchSpinner(registryType).hide();
69 99
        tag.removeClass('spinner-label');
......
90 120
    type:'get',
91 121
    dataType:'text',
92 122
    url: $(item).attr('data-url'),
93
    data: { search: search.val(), registry_id: $('#docker_container_wizard_states_image_registry_id').val() },
123
    data: paramsForSearch(registryType),
94 124
    success: function (result) {
95 125
      results.html(result);
96 126
    },
app/controllers/image_search_controller.rb
1 1
class ImageSearchController < ::ApplicationController
2
  before_filter :find_resource
3

  
4
  # this is incredibly odd. for some reason, rails sees the
5
  # requests ImageSearchControllerTest makes as requests from another host
6
  # thus, violating CSRF.
7
  protect_from_forgery :only => :nothing if Rails.env.test?
8

  
9 2
  def auto_complete_repository_name
10 3
    catch_network_errors do
11
      text = if use_hub?
12
               hub_image_exists?(params[:search])
13
             else
14
               registry_image_exists?(params[:search])
15
             end
16
      render :text => text.to_s
4
      available = image_search_service.available?(params[:search])
5
      render :text => available.to_s
17 6
    end
18 7
  end
19 8

  
20 9
  def auto_complete_image_tag
21 10
    catch_network_errors do
22
      # This is the format jQuery UI autocomplete expects
23
      tags = if use_hub?
24
               hub_auto_complete_image_tags(params[:search])
25
             else
26
               registry_auto_complete_image_tags(params[:search])
27
             end
28

  
29
      tags = tags.map do |tag|
30
        tag = CGI.escapeHTML(tag)
31
        { :label => tag, :value => tag }
32
      end
11
      tags = image_search_service.search({
12
        term: params[:search],
13
        tags: 'true'
14
      })
33 15

  
34 16
      respond_to do |format|
35 17
        format.js do
36
          render :json => tags
18
          render :json => prepare_for_autocomplete(tags)
37 19
        end
38 20
      end
39 21
    end
......
41 23

  
42 24
  def search_repository
43 25
    catch_network_errors do
44
      repositories = if use_hub?
45
                       hub_search_image(params[:search])
46
                     else
47
                       registry_search_image(params[:search])
48
                     end
26
      repositories = image_search_service.search({
27
        term: params[:search].split(':').first,
28
        tags: 'false'
29
      })
49 30

  
50 31
      respond_to do |format|
51 32
        format.js do
......
57 38
    end
58 39
  end
59 40

  
41
  private
42

  
60 43
  def catch_network_errors
61 44
    yield
62 45
  rescue Docker::Error::NotFoundError => e
......
72 55
    @registry.nil?
73 56
  end
74 57

  
75
  def hub_image_exists?(terms)
76
    @compute_resource.exist?(terms)
77
  end
78

  
79
  def hub_auto_complete_image_tags(terms)
80
    @compute_resource.tags(terms)
81
  end
82

  
83
  def hub_search_image(terms)
84
    @compute_resource.search(terms).map do |item|
85
      # el7 returns -> "name" => "docker.io: docker.io/centos",
86
      # while f20 returns -> "name" => "centos"
87
      # we need repo name to be => "docker.io/centos" for el7 and "centos" for fedora
88
      # to ensure proper search with respect to the tags, image creation etc.
89
      new_item = item.clone
90
      new_item["name"] = item["name"].split.last
91
      new_item
92
    end
93
  end
94

  
95
  def registry_image_exists?(term)
96
    result = ::Service::RegistryApi.new(:url => @registry.url,
97
                                        :user => @registry.username,
98
                                        :password => @registry.password).search(term)
99
    registry_name = if term.split('/').size > 1
100
                      term
101
                    else
102
                      "library/#{term}"
103
                    end
104

  
105
    result['results'].any? { |r| r['name'] == registry_name }
106
  end
107

  
108
  def registry_auto_complete_image_tags(terms)
109
    ::Service::RegistryApi.new(:url => @registry.url,
110
                               :user => @registry.username,
111
                               :password => @registry.password).tags(terms).map { |t| t['name'] }
112
  end
113

  
114
  def registry_search_image(terms)
115
    r = ::Service::RegistryApi.new(:url => @registry.url,
116
                                   :user => @registry.username,
117
                                   :password => @registry.password).search(terms)
118
    r['results']
119
  end
120

  
121 58
  def action_permission
122 59
    case params[:action]
123 60
    when 'auto_complete_repository_name', 'auto_complete_image_tag', 'search_repository'
......
127 64
    end
128 65
  end
129 66

  
130
  def find_resource
131
    if params[:registry_id].present?
132
      @registry = DockerRegistry.authorized(:view_registries).find(params[:registry_id])
133
    else
134
      @compute_resource = ComputeResource.authorized(:view_compute_resources).find(params[:id])
67
  # This is the format jQuery UI autocomplete expects
68
  def prepare_for_autocomplete(tags)
69
    tags.map do |tag|
70
      tag = tag.is_a?(Hash) ? tag.fetch('name', tag) : tag
71
      tag = CGI.escapeHTML(tag)
72
      { :label => tag, :value => tag }
73
    end
74
  end
75

  
76
  def image_search_service
77
    @image_search_service ||= ForemanDocker::ImageSearch.new(*sources)
78
  end
79

  
80
  def sources
81
    if params[:registry] == 'hub'
82
      @registry ||= Service::RegistryApi.docker_hub
83
      @compute_resource ||= ComputeResource.authorized(:view_compute_resources).find(params[:id])
84
      [@registry, @compute_resource]
85
    elsif params[:registry] == 'registry' && params[:registry_id].present?
86
      @registry ||= DockerRegistry.authorized(:view_registries)
87
        .find(params[:registry_id]).api
88
      [@registry]
135 89
    end
136
  rescue ActiveRecord::RecordNotFound
137
    not_found
138 90
  end
139 91
end
app/helpers/container_steps_helper.rb
34 34
    active_tab.to_s == tab_name.to_s ? "active" : ""
35 35
  end
36 36

  
37
  # el7 returns -> "name" => "docker.io: docker.io/centos",
38
  # while f20 returns -> "name" => "centos"
39
  # we need repo name to be => "docker.io/centos" for el7 and "centos" for fedora
40
  # to ensure proper search with respect to the tags, image creation etc.
41
  def cleanup_image_name(name)
42
    name.split.last
43
  end
44

  
45

  
37 46
  def model_for(registry_type)
38 47
    if active_tab.to_s == registry_type.to_s
39 48
      @docker_container_wizard_states_image
app/models/docker_registry.rb
43 43
  end
44 44

  
45 45
  def api
46
    @api ||= Service::RegistryApi.new(url: url,
47
                                      user: username,
46
    @api ||= Service::RegistryApi.new(url: url, user: username,
48 47
                                      password: password)
49 48
  end
50 49

  
app/models/foreman_docker/docker.rb
61 61
      if exist?(image_name)
62 62
        tags_for_local_image(image(image_name))
63 63
      else
64
        # If image is not found in the compute resource, get the tags from the Hub
65
        hub_api_url = "https://index.docker.io/v1/repositories/#{image_name}/tags"
66
        JSON.parse(URI.parse(hub_api_url).read).map do |tag|
67
          tag['name']
68
        end
64
        Service::RegistryApi.docker_hub.tags(image_name).map { |tag| tag['name'] }
69 65
      end
70 66
    end
71 67

  
app/models/service/registry_api.rb
59 59
      get('/v2/'.freeze).is_a? Hash
60 60
    end
61 61

  
62
    def ok?
63
      get('/v1/').match("Docker Registry API")
64
    rescue
65
      get('/v2/').is_a? Hash
66
    end
67

  
62 68
    def self.docker_hub
63 69
      @@docker_hub ||= new(url: DOCKER_HUB)
64 70
    end
app/services/foreman_docker/image_search.rb
1
module ForemanDocker
2
  class ImageSearch
3
    def initialize(*args)
4
      @sources = {}
5
      args.each do |source|
6
        add_source(source)
7
      end
8
    end
9

  
10
    def add_source(source)
11
      case source
12
      when ForemanDocker::Docker
13
        @sources[:compute_resource] ||= []
14
        @sources[:compute_resource] << source
15
      when Service::RegistryApi
16
        @sources[:registry] ||= []
17
        @sources[:registry] << source
18
      end
19
    end
20

  
21
    def remove_source(source)
22
      @sources.each do |_, sources|
23
        sources.delete(source)
24
      end
25
    end
26

  
27
    def search(query)
28
      return [] if query[:term].blank? || query[:term] == ':'
29

  
30
      unless query[:tags] == 'true'
31
        images(query[:term])
32
      else
33
        tags(query[:term])
34
      end
35
    end
36

  
37
    def images(query)
38
      sources_results_for(:search, query)
39
    end
40

  
41
    def tags(query)
42
      image_name, tag = query.split(':')
43
      sources_results_for(:tags, image_name, tag)
44
        .map { |tag_name| { 'name' => tag_name } }
45
    end
46

  
47
    def available?(query)
48
      tags(query).present?
49
    end
50

  
51
    private
52

  
53
    def registry_search(registry, term)
54
      registry.search(term)['results']
55
    end
56

  
57
    def compute_resource_search(compute_resource, query)
58
      images = compute_resource.local_images(query)
59
      images.flat_map do |image|
60
        image.info['RepoTags'].map do |tag|
61
          { 'name' => tag.split(':').first }
62
        end
63
      end.uniq
64
    end
65

  
66
    def compute_resource_image(compute_resource, image_name)
67
      compute_resource.image(image_name)
68
    rescue ::Docker::Error::NotFoundError
69
      nil
70
    end
71

  
72
    def compute_resource_tags(compute_resource, image_name, tag)
73
      image = compute_resource_image(compute_resource, image_name)
74
      image ? compute_resource.tags_for_local_image(image, tag) : []
75
    end
76

  
77
    def registry_tags(registry, image_name, tag)
78
      registry.tags(image_name, tag).map { |t| t['name'] }
79
    end
80

  
81
    def sources_results_for(search, *args)
82
      result = []
83
      @sources.each do |kind, sources|
84
        sources.each do |source|
85
          result << self.send("#{kind}_#{search}", source, *args)
86
        end
87
      end
88
      result.flatten.compact
89
    end
90
  end
91
end
92

  
app/views/image_search/_repository_search_results.html.erb
1 1
<% repositories.each do |repository| %>
2
    <h3><%= link_to repository['name'], '#', :onclick => 'repoSelected(this)', "data-hub" => use_hub %></h3>
2
    <h3><%= link_to cleanup_image_name(repository['name']), '#', :onclick => 'repoSelected(this)', "data-hub" => use_hub %></h3>
3 3
    <p>
4 4
      <%= '<span class="glyphicon glyphicon-certificate" title="Official"></span>'.html_safe if repository['is_official'] %>
5 5
      <%= '<span class="glyphicon glyphicon-thumbs-up" title="Trusted"></span>'.html_safe    if repository['is_trusted'] %>
test/functionals/image_search_controller_test.rb
1 1
require 'test_plugin_helper'
2 2

  
3 3
class ImageSearchControllerTest < ActionController::TestCase
4
  let(:image) { 'centos' }
5
  let(:tags) { ['latest', '5', '4.3'].map { |tag| "#{term}:#{tag}" } }
6
  let(:term) { image }
7

  
8
  let(:docker_hub) { Service::RegistryApi.new(url: 'https://nothub.com') }
9
  let(:compute_resource) { FactoryGirl.create(:docker_cr) }
10
  let(:registry) { FactoryGirl.create(:docker_registry) }
11
  let(:image_search_service) { ForemanDocker::ImageSearch.new }
12

  
4 13
  setup do
5
    @container = FactoryGirl.create(:docker_cr)
14
    Service::RegistryApi.stubs(:docker_hub).returns(docker_hub)
15
    ComputeResource::ActiveRecord_Relation.any_instance
16
      .stubs(:find).returns(compute_resource)
17
    DockerRegistry::ActiveRecord_Relation.any_instance
18
      .stubs(:find).returns(registry)
19
  end
20

  
21
  describe '#auto_complete_repository_name' do
22
    test 'returns if an image is available' do
23
      exists = ['true', 'false'].sample
24
      search_type = ['hub', 'registry'].sample
25
      subject.instance_variable_set(:@image_search_service, image_search_service)
26
      image_search_service.expects(:available?).returns(exists)
27

  
28
      xhr :get, :auto_complete_repository_name,
29
        { registry: search_type, search: term,
30
          id: compute_resource }, set_session_user
31
      assert_equal exists, response.body
32
    end
33

  
34
    context 'it is a Docker Hub tab request' do
35
      let(:search_type) { 'hub' }
36

  
37
      test 'it queries the compute_resource and Docker Hub' do
38
        compute_resource.expects(:image).with(term)
39
          .returns(term)
40
        compute_resource.expects(:tags_for_local_image)
41
          .returns(tags)
42
        docker_hub.expects(:tags).returns([])
43

  
44
        xhr :get, :auto_complete_repository_name,
45
          { registry: search_type, search: term,
46
            id: compute_resource }, set_session_user
47
      end
48
    end
49

  
50
    context 'it is a External Registry tab request' do
51
      let(:search_type) { 'registry' }
52

  
53
      test 'it only queries the registry api' do
54
        compute_resource.expects(:image).with(term).never
55
        docker_hub.expects(:tags).never
56
        registry.api.expects(:tags).with(term, nil)
57
          .returns(['latest'])
58

  
59
        xhr :get, :auto_complete_repository_name,
60
          { registry: search_type, registry_id: registry,
61
            search: term, id: compute_resource }, set_session_user
62
      end
63
    end
6 64
  end
7 65

  
8 66
  describe '#auto_complete_image_tag' do
9
    let(:tags) { ['latest', '5', '4.3'] }
67
    let(:tag_fragment) { 'lat' }
68
    let(:term) { "#{image}:#{tag_fragment}"}
10 69

  
11 70
    test 'returns an array of { label:, value: } hashes' do
12
      ForemanDocker::Docker.any_instance.expects(:tags)
13
        .with('test')
71
      search_type = ['hub', 'registry'].sample
72
      subject.instance_variable_set(:@image_search_service, image_search_service)
73
      image_search_service.expects(:search)
74
        .with({ term: term, tags: 'true' })
14 75
        .returns(tags)
15
      get :auto_complete_image_tag,
16
          { search: "test", id: @container.id, format: :js }, set_session_user
76
      xhr :get, :auto_complete_image_tag,
77
        { registry: search_type, search: term,
78
          id: compute_resource }, set_session_user
17 79
      assert_equal tags.first, JSON.parse(response.body).first['value']
18 80
    end
81

  
82
    context 'a Docker Hub tab request' do
83
      let(:search_type) { 'hub' }
84

  
85
      test 'it searches Docker Hub and the ComputeResource' do
86
        compute_resource.expects(:image).with(image)
87
          .returns(term)
88
        compute_resource.expects(:tags_for_local_image)
89
          .returns(tags)
90
        docker_hub.expects(:tags).returns([])
91

  
92
        xhr :get, :auto_complete_image_tag,
93
          { registry: search_type, search: term,
94
            id: compute_resource }, set_session_user
95
      end
96
    end
97

  
98
    context 'it is a External Registry tab request' do
99
      let(:search_type) { 'registry' }
100

  
101
      test 'it only queries the registry api' do
102
        compute_resource.expects(:image).with(image).never
103
        docker_hub.expects(:tags).never
104
        registry.api.expects(:tags).with(image, tag_fragment)
105
          .returns([])
106

  
107
        xhr :get, :auto_complete_image_tag,
108
          { registry: search_type, registry_id: registry,
109
            search: term, id: compute_resource }, set_session_user
110
      end
111
    end
112
  end
113

  
114
  describe '#search_repository' do
115
    test 'returns html with the found images' do
116
      search_type = ['hub', 'registry'].sample
117
      subject.instance_variable_set(:@image_search_service, image_search_service)
118
      image_search_service.expects(:search)
119
        .with({ term: term, tags: 'false' })
120
        .returns([{ 'name' => term}])
121
      xhr :get, :search_repository,
122
        { registry: search_type, search: term,
123
          id: compute_resource }, set_session_user
124
      assert response.body.include?(term)
125
    end
126

  
127
    context 'a Docker Hub tab request' do
128
      let(:search_type) { 'hub' }
129

  
130
      test 'it searches Docker Hub and the ComputeResource' do
131
        compute_resource.expects(:local_images)
132
          .returns([OpenStruct.new(info: { 'RepoTags' => [term] })])
133
        docker_hub.expects(:search).returns({})
134

  
135
        xhr :get, :search_repository,
136
          { registry: search_type, search: term,
137
            id: compute_resource }, set_session_user
138
      end
139
    end
140

  
141
    context 'it is a External Registry tab request' do
142
      let(:search_type) { 'registry' }
143

  
144
      test 'it only queries the registry api' do
145
        compute_resource.expects(:local_images).with(image).never
146
        docker_hub.expects(:search).never
147
        registry.api.expects(:search).with(image)
148
          .returns({})
149

  
150
        xhr :get, :search_repository,
151
          { registry: search_type, registry_id: registry,
152
            search: term, id: compute_resource }, set_session_user
153
      end
154
    end
19 155
  end
20 156

  
21 157
  [Docker::Error::DockerError, Excon::Errors::Error, Errno::ECONNREFUSED].each do |error|
22 158
    test 'auto_complete_repository_name catches exceptions on network errors' do
23
      ForemanDocker::Docker.any_instance.expects(:exist?).raises(error)
24
      get :auto_complete_repository_name, { :search => "test", :id => @container.id },
25
          set_session_user
159
      ForemanDocker::ImageSearch.any_instance.expects(:available?)
160
        .raises(error)
161
      xhr :get, :auto_complete_repository_name,
162
        { registry: 'hub', search: term, id: compute_resource }, set_session_user
26 163
      assert_response_is_expected
27 164
    end
28 165

  
29 166
    test 'auto_complete_image_tag catch exceptions on network errors' do
30
      ForemanDocker::Docker.any_instance.expects(:tags).raises(error)
31
      get :auto_complete_image_tag, { :search => "test", :id => @container.id }, set_session_user
167
      ForemanDocker::ImageSearch.any_instance.expects(:search).raises(error)
168
      xhr :get, :auto_complete_image_tag,
169
        { registry: 'hub', search:  term, id: compute_resource }, set_session_user
32 170
      assert_response_is_expected
33 171
    end
34 172

  
35 173
    test 'search_repository catch exceptions on network errors' do
36
      ForemanDocker::Docker.any_instance.expects(:search).raises(error)
37
      get :search_repository, { :search => "test", :id => @container.id }, set_session_user
174
      ForemanDocker::ImageSearch.any_instance.expects(:search).raises(error)
175
      xhr :get, :search_repository,
176
        { registry: 'hub', search: term, id: compute_resource }, set_session_user
38 177
      assert_response_is_expected
39 178
    end
40 179
  end
......
49 188
                   "name" =>  repo_full_name,
50 189
                   "star_count" => 0
51 190
                }]
52
    ForemanDocker::Docker.any_instance.expects(:search).returns(expected).at_least_once
53
    get :search_repository, { :search => "centos", :id => @container.id }, set_session_user
191
    ForemanDocker::ImageSearch.any_instance.expects(:search).returns(expected).at_least_once
192
    xhr :get, :search_repository,
193
      { registry: 'hub', search: 'centos', id: compute_resource }, set_session_user
54 194
    assert_response :success
55 195
    refute response.body.include?(repo_full_name)
56 196
    assert response.body.include?(repository)
......
66 206
                  "name" =>  repo_full_name,
67 207
                  "star_count" => 0
68 208
                }]
69
    ForemanDocker::Docker.any_instance.expects(:search).returns(expected).at_least_once
70
    get :search_repository, { :search => "centos", :id => @container.id }, set_session_user
209
    ForemanDocker::ImageSearch.any_instance.expects(:search).returns(expected).at_least_once
210
    xhr :get, :search_repository,
211
      { registry: 'hub', search: 'centos', id: compute_resource  }, set_session_user
71 212
    assert_response :success
72 213
    assert response.body.include?(repo_full_name)
73 214
    assert response.body.include?(repository)
test/test_plugin_helper.rb
12 12
# Add plugin to FactoryGirl's paths
13 13
FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
14 14
FactoryGirl.reload
15

  
16
def stub_image_existance(exists = true)
17
  Docker::Image.any_instance.stubs(:exist?).returns(exists)
18
  ForemanDocker::ImageSearch.any_instance.stubs(:available?).returns(exists)
19
end
20

  
21
def stub_registry_api
22
  Service::RegistryApi.any_instance.stubs(:get).returns({'results' => []})
23
  Docker::Image.stubs(:all).returns([])
24
end
25

  
test/units/docker_registry_test.rb
55 55
      assert_kind_of Service::RegistryApi, api
56 56
    end
57 57
  end
58

  
59
  describe '#api' do
60
    let(:api) { subject.api }
61

  
62
    test 'returns a RegistryApi instance' do
63
      assert_kind_of Service::RegistryApi, api
64
    end
65
  end
58 66
end
test/units/image_search_service_test.rb
1
require 'test_plugin_helper'
2

  
3
class ImageSearchServiceTest < ActiveSupport::TestCase
4
  let(:compute_resource) { FactoryGirl.create(:docker_cr) }
5
  let(:registry) { FactoryGirl.create(:docker_registry).api }
6
  let(:term) { 'centos' }
7
  let(:query) { { term: term, tags: 'false' } }
8

  
9
  subject { ForemanDocker::ImageSearch.new(compute_resource, registry) }
10

  
11
  setup do
12
    stub_registry_api
13
  end
14

  
15
  describe '#add_source' do
16
    setup do
17
      subject.instance_variable_set(:@sources, {})
18
    end
19

  
20
    test 'adds a compute resource to @sources[:compute_resource]' do
21
      subject.add_source(compute_resource)
22
      assert_equal compute_resource,
23
                   subject.instance_variable_get(:@sources)[:compute_resource].first
24
    end
25

  
26
    test 'adds a registry to @sources[:registry]' do
27
      subject.add_source(registry)
28
      assert_equal registry,
29
                   subject.instance_variable_get(:@sources)[:registry].first
30
    end
31
  end
32

  
33
  describe '#remove_source' do
34
    test 'removes a registry source from @sources' do
35
      refute subject.instance_variable_get(:@sources)[:registry].empty?
36
      subject.remove_source(registry)
37
      assert subject.instance_variable_get(:@sources)[:registry].empty?
38
    end
39

  
40
    test 'removes a compute_resource source from @sources' do
41
      refute subject.instance_variable_get(:@sources)[:compute_resource].empty?
42
      subject.remove_source(compute_resource)
43
      assert subject.instance_variable_get(:@sources)[:compute_resource].empty?
44
    end
45
  end
46

  
47
  describe '#search' do
48
    test 'returns {"name" => value } pairs' do
49
      return_result = Hash.new
50
      return_result.stubs(:info).returns({ 'RepoTags' => ["#{term}:latest"]})
51
      compute_resource.stubs(:local_images).with(term)
52
        .returns([return_result])
53
      result = subject.search(query)
54
      assert_equal({"name" => term}, result.first)
55
    end
56

  
57
    context 'tags is false' do
58
      test 'calls #images with term as query' do
59
        subject.expects(:images).with(term)
60
          .returns([])
61
        subject.search(query)
62
      end
63
    end
64

  
65
    context 'tags is "true"' do
66
      setup do
67
        query[:tags] = 'true'
68
      end
69

  
70
      test 'calls #tags with term as query' do
71
        subject.expects(:tags).with(term)
72
          .returns([])
73
        subject.search(query)
74
      end
75
    end
76
  end
77

  
78
  describe '#images' do
79
    context 'a compute_resource set' do
80
      test 'calls #search_compute_resource with term as query' do
81
        subject.expects(:compute_resource_search).with(compute_resource, term)
82
          .returns([])
83
        subject.images(term)
84
      end
85
    end
86

  
87
    context 'no compute_resource is set' do
88
      setup do
89
        subject.remove_source(compute_resource)
90
      end
91

  
92
      test 'does not call #search_compute_resource' do
93
        subject.expects(:compute_resource_search).with(compute_resource, term)
94
          .never
95
        subject.images(term)
96
      end
97
    end
98

  
99
    context 'a registry is set' do
100
      test 'calls #search_registry' do
101
        subject.expects(:registry_search).with(registry, term)
102
          .returns([])
103
        subject.images(term)
104
      end
105
    end
106

  
107
    context 'no registry is set' do
108
      setup do
109
        subject.remove_source(registry)
110
      end
111

  
112
      test 'does not call #search_registry' do
113
        subject.expects(:registry_search).with(registry, term)
114
          .never
115
        subject.images(term)
116
      end
117
    end
118
  end
119

  
120
  describe '#tags' do
121
    let(:tag) { 'latest' }
122
    let(:query) { "#{term}:#{tag}" }
123

  
124
    context 'a compute_resource set' do
125
      test 'calls #compute_resource with image name and tag' do
126
        subject.expects(:compute_resource_tags).with(compute_resource, term, tag)
127
          .returns([])
128
        subject.tags(query)
129
      end
130
    end
131

  
132
    context 'no compute_resource is set' do
133
      setup do
134
        subject.remove_source(compute_resource)
135
      end
136

  
137
      test 'does not call #search_compute_resource' do
138
        subject.expects(:compute_resource_tags).with(compute_resource, term, tag)
139
          .never
140
        subject.tags(query)
141
      end
142
    end
143

  
144
    context 'a registry is set' do
145
      setup do
146
        subject.remove_source(compute_resource)
147
      end
148

  
149
      test 'calls #registry_tags with image name and tag' do
150
        subject.expects(:registry_tags).with(registry, term, tag)
151
          .returns([])
152
        subject.tags(query)
153
      end
154
    end
155

  
156
    context 'no registry is set' do
157
      setup do
158
        subject.remove_source(registry)
159
      end
160

  
161
      test 'does not call #registry_tags' do
162
        subject.expects(:registry_search).with(registry, term, tag)
163
          .never
164
        subject.images(query)
165
      end
166
    end
167
  end
168

  
169
  describe '#available?' do
170
    test 'calls #tags with query' do
171
      subject.expects(:tags).with(query).once
172
        .returns([])
173
      subject.available?(query)
174
    end
175

  
176
    test 'returns true if any matching image and tag is found' do
177
      subject.stubs(:tags).with(query)
178
        .returns([{ 'name' => "#{term}:latest" }])
179
      subject.available?(query)
180
    end
181

  
182
    test 'returns false if none are found' do
183
      subject.stubs(:tags).with(query)
184
        .returns([])
185
      subject.available?(query)
186
    end
187
  end
188
end

Also available in: Unified diff