File: command_line.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (196 lines) | stat: -rw-r--r-- 6,737 bytes parent folder | download
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
# Bundler and rubygems maintain a set of directories from which to
# load gems. If Bundler is loaded, let it determine what can be
# loaded. If it's not loaded, then use rubygems. But do this before
# loading any puppet code, so that our gem loading system is sane.
if not defined? ::Bundler
  begin
    require 'rubygems'
  rescue LoadError
  end
end

require_relative '../../puppet'
require_relative '../../puppet/util'
require_relative '../../puppet/util/rubygems'
require_relative '../../puppet/util/limits'
require_relative '../../puppet/util/colors'
require_relative '../../puppet/gettext/module_translations'

module Puppet
  module Util
    # This is the main entry point for all puppet applications / faces; it
    # is basically where the bootstrapping process / lifecycle of an app
    # begins.
    class CommandLine
      include Puppet::Util::Limits

      OPTION_OR_MANIFEST_FILE = /^-|\.pp$/

      # @param zero [String] the name of the executable
      # @param argv [Array<String>] the arguments passed on the command line
      # @param stdin [IO] (unused)
      def initialize(zero = $0, argv = ARGV, stdin = STDIN)
        @command = File.basename(zero, '.rb')
        @argv = argv
      end

      # @return [String] name of the subcommand is being executed
      # @api public
      def subcommand_name
        return @command if @command != 'puppet'

        if @argv.first =~ OPTION_OR_MANIFEST_FILE
          nil
        else
          @argv.first
        end
      end

      # @return [Array<String>] the command line arguments being passed to the subcommand
      # @api public
      def args
        return @argv if @command != 'puppet'

        if subcommand_name.nil?
          @argv
        else
          @argv[1..-1]
        end
      end

      # Run the puppet subcommand. If the subcommand is determined to be an
      # external executable, this method will never return and the current
      # process will be replaced via {Kernel#exec}.
      #
      # @return [void]
      def execute
        require_config = true
        if @argv.first =~ /help|-h|--help|-V|--version/
          require_config = false
        end
        Puppet::Util.exit_on_fail(_("Could not initialize global default settings")) do
          Puppet.initialize_settings(args, require_config)
        end

        setpriority(Puppet[:priority])

        find_subcommand.run
      end

      # @api private
      def external_subcommand
        Puppet::Util.which("puppet-#{subcommand_name}")
      end

      private

      def find_subcommand
        if subcommand_name.nil?
          if args.include?("--help") || args.include?("-h")
            ApplicationSubcommand.new("help", CommandLine.new("puppet", ["help"]))
          else
            NilSubcommand.new(self)
          end
        elsif Puppet::Application.available_application_names.include?(subcommand_name)
          ApplicationSubcommand.new(subcommand_name, self)
        else
          path_to_subcommand = external_subcommand
          if path_to_subcommand
            ExternalSubcommand.new(path_to_subcommand, self)
          else
            UnknownSubcommand.new(subcommand_name, self)
          end
        end
      end

      # @api private
      class ApplicationSubcommand
        def initialize(subcommand_name, command_line)
          @subcommand_name = subcommand_name
          @command_line = command_line
        end

        def run
          # For most applications, we want to be able to load code from the modulepath,
          # such as apply, describe, resource, and faces.
          # For agent and device in agent mode, we only want to load pluginsync'ed code from libdir.
          # For master, we shouldn't ever be loading per-environment code into the master's
          # ruby process, but that requires fixing (#17210, #12173, #8750). So for now
          # we try to restrict to only code that can be autoloaded from the node's
          # environment.

          # PUP-2114 - at this point in the bootstrapping process we do not
          # have an appropriate application-wide current_environment set.
          # If we cannot find the configured environment, which may not exist,
          # we do not attempt to add plugin directories to the load path.
          unless @subcommand_name == 'master' || @subcommand_name == 'agent' || (@subcommand_name == 'device' && (['--apply', '--facts', '--resource'] - @command_line.args).empty?)
            configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])
            if configured_environment
              configured_environment.each_plugin_directory do |dir|
                $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
              end

              Puppet::ModuleTranslations.load_from_modulepath(configured_environment.modules)
              Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir])

              # Puppet requires Facter, which initializes its lookup paths. Reset Facter to
              # pickup the new $LOAD_PATH.
              Puppet.runtime[:facter].reset
            end
          end

          app = Puppet::Application.find(@subcommand_name).new(@command_line)
          app.run
        end
      end

      # @api private
      class ExternalSubcommand
        def initialize(path_to_subcommand, command_line)
          @path_to_subcommand = path_to_subcommand
          @command_line = command_line
        end

        def run
          Kernel.exec(@path_to_subcommand, *@command_line.args)
        end
      end

      # @api private
      class NilSubcommand
        include Puppet::Util::Colors

        def initialize(command_line)
          @command_line = command_line
        end

        def run
          args = @command_line.args
          if args.include? "--version" or args.include? "-V"
            puts Puppet.version
          elsif @command_line.subcommand_name.nil? && args.count > 0
            # If the subcommand is truly nil and there is an arg, it's an option; print out the invalid option message
            puts colorize(:hred, _("Error: Could not parse application options: invalid option: %{opt}") % { opt: args[0] })
            exit 1
          else
            puts _("See 'puppet help' for help on available puppet subcommands")
          end
        end
      end

      # @api private
      class UnknownSubcommand < NilSubcommand
        def initialize(subcommand_name, command_line)
          @subcommand_name = subcommand_name
          super(command_line)
        end

        def run
          puts colorize(:hred, _("Error: Unknown Puppet subcommand '%{cmd}'") % { cmd: @subcommand_name })
          super
          exit 1
        end
      end
    end
  end
end