summaryrefslogtreecommitdiff
path: root/lib/chef/resource/cpan_module.rb
blob: da63dc9a740aadcfe0cd059da8c915b06f0b09d6 (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
require "chef/resource"

class Chef
  class Resource
    # @author Joshua Timberman <jtimberman@chef.io>
    # @author Steve Nolen <technolengy@gmail.com>
    # resource to install modules from CPAN
    class CpanModule < Chef::Resource
      resource_name :cpan_module

      property :module_name, String, name_property: true
      property :cpam_bin_path, String, default: "/usr/local/bin/cpanm"
      property :force, [true, false], default: false
      property :test, [true, false], default: false
      property :version, String
      property :cwd, String

      action :install do
        declare_resource(:execute, "CPAN :install #{new_resource.module_name}") do
          cwd current_working_dir
          command cpanm_install_cmd
          environment "HOME" => current_working_dir, "PATH" => "/usr/local/bin:/usr/bin:/bin"
          not_if { module_exists_new_enough }
        end
      end

      action :uninstall do
        declare_resource(:execute, "CPAN :uninstall #{new_resource.module_name}") do
          cwd current_working_dir
          command cpanm_uninstall_cmd
          only_if module_exists
        end
      end

      action_class do
        def module_exists_new_enough
          existing_version = parse_cpan_version
          return false if existing_version.empty? # mod doesn't exist
          return true if new_resource.version.nil? # mod exists and version is unimportant
          @comparator, @pending_version = new_resource.version.split(" ", 2)
          @current_vers = Gem::Version.new(existing_version)
          @pending_vers = Gem::Version.new(@pending_version)
          @current_vers.method(@comparator).call(@pending_vers)
        end

        def parse_cpan_version
          mod_ver_cmd = shell_out("perl -M#{new_resource.module_name} -e 'print $#{new_resource.module_name}::VERSION;' 2> /dev/null")
          mod_ver = mod_ver_cmd.stdout
          return mod_ver if mod_ver.empty?
          # remove leading v and convert underscores to dots since gems parses them wrong
          mod_ver.gsub!(/v_/, "v" => 3, "_" => ".")
          # in the event that this command outputs whatever it feels like, only keep the first vers number!
          version_match = /(^[0-9.]*)/.match(mod_ver)
          version_match[0]
        end

        # builds a string of the  perl command to see if the module exists
        # @return [String]
        def module_exists
          "perl -m#{new_resource.module_name} -e ';' 2> /dev/null"
        end

        def cpanm_install_cmd
          @cmd = "#{new_resource.cpam_bin_path} --quiet "
          @cmd += "--force " if new_resource.force
          @cmd += "--notest " unless new_resource.test
          @cmd += new_resource.module_name
          @cmd += parsed_version
          @cmd
        end

        def cpanm_uninstall_cmd
          @cmd = "#{new_resource.cpam_bin_path} "
          @cmd += "--force " if new_resource.force
          @cmd += "--uninstall "
          @cmd += new_resource.module_name
          @cmd
        end

        # a bit of a stub, could use a version parser for really consistent expeirence
        def parsed_version
          return "~\"#{new_resource.version}\"" if new_resource.version
          ""
        end

        def current_working_dir
          return new_resource.cwd if new_resource.cwd
          return "/var/root" if node["platform"] == "mac_os_x"
          "/root"
        end
      end
    end
  end
end