summaryrefslogtreecommitdiff
path: root/spec/unit/indirector/catalog/static_compiler_spec.rb
blob: 556bc794385246acde45c799d6b1a15c1175044f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/indirector/catalog/static_compiler'
require 'puppet/file_serving/metadata'
require 'puppet/file_serving/content'
require 'yaml'

describe Puppet::Resource::Catalog::StaticCompiler do
  before :all do
    @num_file_resources = 10
  end

  before :each do
    Facter.stubs(:loadfacts)
    Facter.stubs(:to_hash).returns({})
    Facter.stubs(:value)
  end

  around(:each) do |example|
    Puppet.override({
        :current_environment => Puppet::Node::Environment.create(:app, []),
      },
      "Ensure we are using an environment other than root"
    ) do
      example.run
    end
  end

  let(:request) do
    Puppet::Indirector::Request.new(:the_indirection_named_foo,
                                    :find,
                                    "the-node-named-foo",
                                    :environment => "production")
  end

  describe "#find" do
    it "returns a catalog" do
      subject.find(request).should be_a_kind_of(Puppet::Resource::Catalog)
    end

    it "returns nil if there is no compiled catalog" do
      subject.expects(:compile).returns(nil)
      subject.find(request).should be_nil
    end

    describe "a catalog with file resources containing source parameters with puppet:// URIs" do
      it "filters file resource source URI's to checksums" do
        stub_the_compiler
        resource_catalog = subject.find(request)
        resource_catalog.resources.each do |resource|
          next unless resource.type == "File"
          resource[:content].should == "{md5}361fadf1c712e812d198c4cab5712a79"
          resource[:source].should be_nil
        end
      end

      it "does not modify file resources with non-puppet:// URI's" do
        uri = "/this/is/not/a/puppet/uri.txt"
        stub_the_compiler(:source => uri)
        resource_catalog = subject.find(request)
        resource_catalog.resources.each do |resource|
          next unless resource.type == "File"
          resource[:content].should be_nil
          resource[:source].should == uri
        end
      end

      it "copies the owner, group and mode from the fileserer" do
        stub_the_compiler
        resource_catalog = subject.find(request)
        resource_catalog.resources.each do |resource|
          next unless resource.type == "File"
          resource[:owner].should == 0
          resource[:group].should == 0
          resource[:mode].should  == 420
        end
      end
    end
  end

  describe "(#15193) when storing content to the filebucket" do
    it "explicitly uses the indirection method" do

      # We expect the content to be retrieved from the FileServer ...
      fake_content = mock('FileServer Content')
      fake_content.expects(:content).returns("HELLO WORLD")

      # Mock the FileBucket to behave as if the file content does not exist.
      # NOTE, we're simulating the first call returning false, indicating the
      # file is not present, then all subsequent calls returning true.  This
      # mocked behavior is intended to replicate the real behavior of the same
      # file being stored to the filebucket multiple times.
      Puppet::FileBucket::File.indirection.
        expects(:find).times(@num_file_resources).
        returns(false).then.returns(true)

      Puppet::FileServing::Content.indirection.
        expects(:find).once.
        returns(fake_content)

      # Once retrived from the FileServer, we expect the file to be stored into
      # the FileBucket only once.  All of the file resources in the fake
      # catalog have the same content.
      Puppet::FileBucket::File.indirection.expects(:save).once.with do |file|
        file.contents == "HELLO WORLD"
      end

      # Obtain the Static Catalog
      subject.stubs(:compile).returns(build_catalog)
      resource_catalog = subject.find(request)

      # Ensure all of the file resources were filtered
      resource_catalog.resources.each do |resource|
        next unless resource.type == "File"
        resource[:content].should == "{md5}361fadf1c712e812d198c4cab5712a79"
        resource[:source].should be_nil
      end
    end
  end

  # Spec helper methods

  def stub_the_compiler(options = {:stub_methods => [:store_content]})
    # Build a resource catalog suitable for specifying the behavior of the
    # static compiler.
    compiler = mock('indirection terminus compiler')
    compiler.stubs(:find).returns(build_catalog(options))
    subject.stubs(:compiler).returns(compiler)
    # Mock the store content method to prevent copying the contents to the
    # file bucket.
    (options[:stub_methods] || []).each do |mthd|
      subject.stubs(mthd)
    end
  end

  def build_catalog(options = {})
    options = options.dup
    options[:source] ||= 'puppet:///modules/mymodule/config_file.txt'
    options[:request] ||= request

    # Build a catalog suitable for the static compiler to operate on
    catalog = Puppet::Resource::Catalog.new("#{options[:request].key}", Puppet::Node::Environment.remote(:testing))

    # Mock out the fileserver, otherwise converting the catalog to a
    fake_fileserver_metadata = fileserver_metadata(options)

    # Stub the call to the FileServer metadata API so we don't have to have
    # a real fileserver initialized for testing.
    Puppet::FileServing::Metadata.
      indirection.stubs(:find).with do |uri, opts|
        expect(uri).to eq options[:source].sub('puppet:///','')
        expect(opts[:links]).to eq :manage
        expect(opts[:environment]).to eq "testing"
      end.returns(fake_fileserver_metadata)

    # I want a resource that all the file resources require and another
    # that requires them.
    resources = Array.new
    resources << Puppet::Resource.new("notify", "alpha")
    resources << Puppet::Resource.new("notify", "omega")

    # Create some File resources with source parameters.
    1.upto(@num_file_resources) do |idx|
      parameters = {
        :ensure  => 'file',
        :source  => options[:source],
        :require => "Notify[alpha]",
        :before  => "Notify[omega]"
      }
      # The static compiler does not operate on a RAL catalog, so we're
      # using Puppet::Resource to produce a resource catalog.
      agnostic_path = File.expand_path("/tmp/file_#{idx}.txt") # Windows Friendly
      rsrc = Puppet::Resource.new("file", agnostic_path, :parameters => parameters)
      rsrc.file = 'site.pp'
      rsrc.line = idx
      resources << rsrc
    end

    resources.each do |rsrc|
      catalog.add_resource(rsrc)
    end

    # Return the resource catalog
    catalog
  end

  describe "(#22744) when filtering resources" do
    let(:catalog) { stub_everything 'catalog' }

    it "should delegate to the catalog instance filtering" do
      catalog.expects(:filter)
      subject.filter(catalog)
    end

    it "should filter out virtual resources" do
      resource = mock 'resource', :virtual? => true
      catalog.stubs(:filter).yields(resource)

      subject.filter(catalog)
    end

    it "should return the same catalog if it doesn't support filtering" do
      catalog.stubs(:respond_to?).with(:filter)
      subject.filter(catalog).should == catalog
    end

    it "should return the filtered catalog" do
      filtered_catalog = stub 'filtered catalog'
      catalog.stubs(:filter).returns(filtered_catalog)

      subject.filter(catalog).should == filtered_catalog
    end

  end

  def fileserver_metadata(options = {})
    yaml = <<EOFILESERVERMETADATA
--- !ruby/object:Puppet::FileServing::Metadata
  checksum: "{md5}361fadf1c712e812d198c4cab5712a79"
  checksum_type: md5
  destination:
  expiration: #{Time.now + 1800}
  ftype: file
  group: 0
  links: !ruby/sym manage
  mode: 420
  owner: 0
  path: #{File.expand_path('/etc/puppet/modules/mymodule/files/config_file.txt')}
  source: #{options[:source]}
  stat_method: !ruby/sym lstat
EOFILESERVERMETADATA
    # Return a deserialized metadata object suitable for returning from a stub.
    YAML.load(yaml)
  end
end