File: service.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (310 lines) | stat: -rw-r--r-- 11,814 bytes parent folder | download | duplicates (2)
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# frozen_string_literal: true

# This is our main way of managing processes right now.
#
# a service is distinct from a process in that services
# can only be managed through the interface of an init script
# which is why they have a search path for initscripts and such

module Puppet
  Type.newtype(:service) do
    @doc = "Manage running services.  Service support unfortunately varies
      widely by platform --- some platforms have very little if any concept of a
      running service, and some have a very codified and powerful concept.
      Puppet's service support is usually capable of doing the right thing, but
      the more information you can provide, the better behaviour you will get.

      Puppet 2.7 and newer expect init scripts to have a working status command.
      If this isn't the case for any of your services' init scripts, you will
      need to set `hasstatus` to false and possibly specify a custom status
      command in the `status` attribute. As a last resort, Puppet will attempt to
      search the process table by calling whatever command is listed in the `ps`
      fact. The default search pattern is the name of the service, but you can
      specify it with the `pattern` attribute.

      **Refresh:** `service` resources can respond to refresh events (via
      `notify`, `subscribe`, or the `~>` arrow). If a `service` receives an
      event from another resource, Puppet will restart the service it manages.
      The actual command used to restart the service depends on the platform and
      can be configured:

      * If you set `hasrestart` to true, Puppet will use the init script's restart command.
      * You can provide an explicit command for restarting with the `restart` attribute.
      * If you do neither, the service's stop and start commands will be used."

    feature :refreshable, "The provider can restart the service.",
            :methods => [:restart]

    feature :enableable, "The provider can enable and disable the service.",
            :methods => [:disable, :enable, :enabled?]

    feature :delayed_startable, "The provider can set service to delayed start",
            :methods => [:delayed_start]

    feature :manual_startable, "The provider can set service to manual start",
            :methods => [:manual_start]

    feature :controllable, "The provider uses a control variable."

    feature :flaggable, "The provider can pass flags to the service."

    feature :maskable, "The provider can 'mask' the service.",
            :methods => [:mask]

    feature :configurable_timeout, "The provider can specify a minumum timeout for syncing service properties"

    feature :manages_logon_credentials, "The provider can specify the logon credentials used for a service"

    newproperty(:enable, :required_features => :enableable) do
      desc "Whether a service should be enabled to start at boot.
        This property behaves differently depending on the platform;
        wherever possible, it relies on local tools to enable or disable
        a given service. Default values depend on the platform.

        If you don't specify a value for the `enable` attribute, Puppet leaves
        that aspect of the service alone and your operating system determines
        the behavior."

      newvalue(:true, :event => :service_enabled) do
        provider.enable
      end

      newvalue(:false, :event => :service_disabled) do
        provider.disable
      end

      newvalue(:manual, :event => :service_manual_start, :required_features => :manual_startable) do
        provider.manual_start
      end

      # This only makes sense on systemd systems. Otherwise, it just defaults
      # to disable.
      newvalue(:mask, :event => :service_disabled, :required_features => :maskable) do
        provider.mask
      end

      def retrieve
        provider.enabled?
      end

      newvalue(:delayed, :event => :service_delayed_start, :required_features => :delayed_startable) do
        provider.delayed_start
      end

      def insync?(current)
        return provider.enabled_insync?(current) if provider.respond_to?(:enabled_insync?)

        super(current)
      end
    end

    # Handle whether the service should actually be running right now.
    newproperty(:ensure) do
      desc "Whether a service should be running. Default values depend on the platform."

      newvalue(:stopped, :event => :service_stopped) do
        provider.stop
      end

      newvalue(:running, :event => :service_started, :invalidate_refreshes => true) do
        provider.start
      end

      aliasvalue(:false, :stopped)
      aliasvalue(:true, :running)

      def retrieve
        provider.status
      end

      def sync
        property = @resource.property(:logonaccount)
        if property
          val = property.retrieve
          property.sync unless property.safe_insync?(val)
        end

        event = super()

        property = @resource.property(:enable)
        if property
          val = property.retrieve
          property.sync unless property.safe_insync?(val)
        end

        event
      end
    end

    newproperty(:logonaccount, :required_features => :manages_logon_credentials) do
      desc "Specify an account for service logon"

      def insync?(current)
        return provider.logonaccount_insync?(current) if provider.respond_to?(:logonaccount_insync?)

        super(current)
      end
    end

    newparam(:logonpassword, :required_features => :manages_logon_credentials) do
      desc "Specify a password for service logon. Default value is an empty string (when logonaccount is specified)."

      validate do |value|
        raise ArgumentError, _("Passwords cannot include ':'") if value.is_a?(String) && value.include?(":")
      end

      sensitive true
      defaultto { @resource[:logonaccount] ? "" : nil }
    end

    newproperty(:flags, :required_features => :flaggable) do
      desc "Specify a string of flags to pass to the startup script."
    end

    newparam(:binary) do
      desc "The path to the daemon.  This is only used for
        systems that do not support init scripts.  This binary will be
        used to start the service if no `start` parameter is
        provided."
    end

    newparam(:hasstatus) do
      desc "Declare whether the service's init script has a functional status
        command. This attribute's default value changed in Puppet 2.7.0.

        The init script's status command must return 0 if the service is
        running and a nonzero value otherwise. Ideally, these exit codes
        should conform to [the LSB's specification][lsb-exit-codes] for init
        script status actions, but Puppet only considers the difference
        between 0 and nonzero to be relevant.

        If a service's init script does not support any kind of status command,
        you should set `hasstatus` to false and either provide a specific
        command using the `status` attribute or expect that Puppet will look for
        the service name in the process table. Be aware that 'virtual' init
        scripts (like 'network' under Red Hat systems) will respond poorly to
        refresh events from other resources if you override the default behavior
        without providing a status command."

      newvalues(:true, :false)

      defaultto :true
    end
    newparam(:name) do
      desc <<-EOT
        The name of the service to run.

        This name is used to find the service; on platforms where services
        have short system names and long display names, this should be the
        short name. (To take an example from Windows, you would use "wuauserv"
        rather than "Automatic Updates.")
      EOT
      isnamevar
    end

    newparam(:path) do
      desc "The search path for finding init scripts.  Multiple values should
        be separated by colons or provided as an array."

      munge do |value|
        value = [value] unless value.is_a?(Array)
        value.flatten.collect { |p| p.split(File::PATH_SEPARATOR) }.flatten
      end

      defaultto { provider.class.defpath if provider.class.respond_to?(:defpath) }
    end
    newparam(:pattern) do
      desc "The pattern to search for in the process table.
        This is used for stopping services on platforms that do not
        support init scripts, and is also used for determining service
        status on those service whose init scripts do not include a status
        command.

        Defaults to the name of the service. The pattern can be a simple string
        or any legal Ruby pattern, including regular expressions (which should
        be quoted without enclosing slashes)."

      defaultto { @resource[:binary] || @resource[:name] }
    end
    newparam(:restart) do
      desc "Specify a *restart* command manually.  If left
        unspecified, the service will be stopped and then started."
    end
    newparam(:start) do
      desc "Specify a *start* command manually.  Most service subsystems
        support a `start` command, so this will not need to be
        specified."
    end
    newparam(:status) do
      desc "Specify a *status* command manually.  This command must
        return 0 if the service is running and a nonzero value otherwise.
        Ideally, these exit codes should conform to [the LSB's
        specification][lsb-exit-codes] for init script status actions, but
        Puppet only considers the difference between 0 and nonzero to be
        relevant.

        If left unspecified, the status of the service will be determined
        automatically, usually by looking for the service in the process
        table.

        [lsb-exit-codes]: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html"
    end

    newparam(:stop) do
      desc "Specify a *stop* command manually."
    end

    newparam(:control) do
      desc "The control variable used to manage services (originally for HP-UX).
        Defaults to the upcased service name plus `START` replacing dots with
        underscores, for those providers that support the `controllable` feature."
      defaultto { resource.name.tr(".", "_").upcase + "_START" if resource.provider.controllable? }
    end

    newparam :hasrestart do
      desc "Specify that an init script has a `restart` command.  If this is
        false and you do not specify a command in the `restart` attribute,
        the init script's `stop` and `start` commands will be used."
      newvalues(:true, :false)
    end

    newparam(:manifest) do
      desc "Specify a command to config a service, or a path to a manifest to do so."
    end

    newparam(:timeout, :required_features => :configurable_timeout) do
      desc "Specify an optional minimum timeout (in seconds) for puppet to wait when syncing service properties"
      defaultto { provider.respond_to?(:default_timeout) ? provider.default_timeout : 10 }

      munge do |value|
        value = value.to_i
        raise if value < 1

        value
      rescue
        raise Puppet::Error, _("\"%{value}\" is not a positive integer: the timeout parameter must be specified as a positive integer") % { value: value }
      end
    end

    # Basically just a synonym for restarting.  Used to respond
    # to events.
    def refresh
      # Only restart if we're actually running
      if (@parameters[:ensure] || newattr(:ensure)).retrieve == :running
        provider.restart
      else
        debug "Skipping restart; service is not running"
      end
    end

    def self.needs_ensure_retrieved
      false
    end

    validate do
      if @parameters[:logonpassword] && @parameters[:logonaccount].nil?
        raise Puppet::Error, _("The 'logonaccount' parameter is mandatory when setting 'logonpassword'.")
      end
    end
  end
end