summaryrefslogtreecommitdiff
path: root/chef/lib/chef/provider/remote_file.rb
blob: 9b5c9db04a36efee3e295df3c8d0b44b4d63c0e2 (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
#
# Author:: Adam Jacob (<adam@opscode.com>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'chef/provider/file'
require 'chef/rest'
require 'chef/mixin/find_preferred_file'
require 'uri'
require 'tempfile'
require 'net/https'

class Chef
  class Provider
    class RemoteFile < Chef::Provider::File

      include Chef::Mixin::FindPreferredFile

      def action_create
        Chef::Log.debug("Checking #{@new_resource} for changes")
        do_remote_file(@new_resource.source, @current_resource.path)
      end

      def action_create_if_missing
        if ::File.exists?(@new_resource.path)
          Chef::Log.debug("File #{@new_resource.path} exists, taking no action.")
        else
          action_create
        end
      end

      def do_remote_file(source, path)
        retval = true

        if(@new_resource.checksum && @current_resource.checksum && @current_resource.checksum =~ /^#{@new_resource.checksum}/)
          Chef::Log.debug("File #{@new_resource} checksum matches, not updating")
        else
          begin
            # The remote filehandle
            raw_file = get_from_uri(source)    ||
                       get_from_server(source, @current_resource.checksum) ||
                       get_from_local_cookbook(source)

            # If the file exists
            if ::File.exists?(@new_resource.path)
              # And it matches the checksum of the raw file
              @new_resource.checksum(self.checksum(raw_file.path))
              if @new_resource.checksum != @current_resource.checksum
                # Updating target file, let's perform a backup!
                Chef::Log.debug("#{@new_resource} changed from #{@current_resource.checksum} to #{@new_resource.checksum}")
                Chef::Log.info("Updating #{@new_resource} at #{@new_resource.path}")
                backup(@new_resource.path)
              end
            else
              # We're creating a new file
              Chef::Log.info("Creating #{@new_resource} at #{@new_resource.path}")
            end

            FileUtils.cp(raw_file.path, @new_resource.path)
            @new_resource.updated = true

            # We're done with the file, so make sure to close it if it was open.
            raw_file.close unless raw_file.closed?
          rescue Net::HTTPRetriableError => e
            if e.response.kind_of?(Net::HTTPNotModified)
              Chef::Log.debug("File #{path} is unchanged")
              retval = false
            else
              raise e
            end
          end
        end
        
        set_owner if @new_resource.owner
        set_group if @new_resource.group
        set_mode  if @new_resource.mode

        retval
      end

      def get_from_uri(source)
        begin
          uri = URI.parse(source)
          if uri.absolute
            r = Chef::REST.new(source, nil, nil)
            Chef::Log.debug("Downloading from absolute URI: #{source}")
            r.get_rest(source, true).open
          end
        rescue URI::InvalidURIError
          nil
        end
      end

      def get_from_server(source, current_checksum)
        unless Chef::Config[:solo]
          r = Chef::REST.new(Chef::Config[:remotefile_url])
          url = generate_url(source, "files", :checksum => current_checksum)
          Chef::Log.debug("Downloading from server: #{url}")
          r.get_rest(url, true).open
        end
      end

      def get_from_local_cookbook(source)
        if Chef::Config[:solo]
          cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
          filename = find_preferred_file(
            cookbook_name,
            :remote_file,
            source,
            @node[:fqdn],
            @node[:platform],
            @node[:platform_version]
          )
          Chef::Log.debug("Using local file for remote_file:#{filename}")
          ::File.open(filename)
        end
      end

    end
  end
end