diff options
author | Tim Smith <tsmith@chef.io> | 2018-05-11 11:06:35 -0700 |
---|---|---|
committer | Tim Smith <tsmith@chef.io> | 2018-08-13 11:09:23 -0700 |
commit | 12a28933b46b83e6745a873d174a1fbe9e46f13d (patch) | |
tree | 20f48f3601337ee47aa12ad195a814d96b2c2ae2 | |
parent | 968f503119b8df8a23ab1c993024238a4759fdd6 (diff) | |
download | chef-12a28933b46b83e6745a873d174a1fbe9e46f13d.tar.gz |
Add cron_d and cron_access resources from the cron cookbook
Cron_d was the #1 requested resource that we already had written. Everyone uses it and we should ship it. It's not a replacement for the existing cron cookbook, but just a different way of managing cron. If people want to manage the whole file or individual snippets we'll give them the option, just as we've always done by having the core resource and this resource.
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | lib/chef/resource/cron_access.rb | 70 | ||||
-rw-r--r-- | lib/chef/resource/cron_d.rb | 213 | ||||
-rw-r--r-- | lib/chef/resource/support/cron.d.erb | 28 | ||||
-rw-r--r-- | lib/chef/resource/support/cron_access.erb | 4 | ||||
-rw-r--r-- | lib/chef/resources.rb | 4 | ||||
-rw-r--r-- | spec/unit/resource/cron_access_spec.rb | 32 | ||||
-rw-r--r-- | spec/unit/resource/cron_d_spec.rb | 32 |
7 files changed, 382 insertions, 1 deletions
diff --git a/lib/chef/resource/cron_access.rb b/lib/chef/resource/cron_access.rb new file mode 100644 index 0000000000..b89cf8e265 --- /dev/null +++ b/lib/chef/resource/cron_access.rb @@ -0,0 +1,70 @@ +# +# Author:: Sander Botman <sbotman@schubergphilis.com> +# Author:: Tim Smith <tsmith@chef.io> +# +# Copyright:: 2014-2018, Sander Botman +# Copyright:: 2018, Chef Software, Inc. +# +# 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/resource" + +class Chef + class Resource + class CronAccess < Chef::Resource + preview_resource true + resource_name :cron_access + provides(:cron_manage) # legacy name @todo in Chef 15 we should { true } this so it wins over the cookbook + + introduced "14.3" + description "" + + property :user, String, + description: "The user to allow or deny. If not provided we'll use the resource name.", + name_property: true + + action :allow do + description "Add the user to the cron.deny file." + + with_run_context :root do + edit_resource(:template, "/etc/cron.allow") do |new_resource| + source ::File.expand_path("../support/cron_access.erb", __FILE__) + local true + mode "0600" + variables["users"] ||= [] + variables["users"] << new_resource.user + action :nothing + delayed_action :create + end + end + end + + action :deny do + description "Add the user to the cron.allow file." + + with_run_context :root do + edit_resource(:template, "/etc/cron.deny") do |new_resource| + source ::File.expand_path("../support/cron_access.erb", __FILE__) + local true + mode "0600" + variables["users"] ||= [] + variables["users"] << new_resource.user + action :nothing + delayed_action :create + end + end + end + end + end +end diff --git a/lib/chef/resource/cron_d.rb b/lib/chef/resource/cron_d.rb new file mode 100644 index 0000000000..f3b102b2b3 --- /dev/null +++ b/lib/chef/resource/cron_d.rb @@ -0,0 +1,213 @@ +# +# Copyright:: 2008-2018, Chef Software, 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/resource" +require "shellwords" + +class Chef + class Resource + class CronD < Chef::Resource + preview_resource true + resource_name :cron_d + + introduced "14.3" + description "" + + property :cron_name, String, + description: "Set the name of the cron job. If this isn't specified we'll use the resource name.", + name_property: true + + property :cookbook, String + + property :predefined_value, String, + description: 'Schedule your cron job with one of the special predefined value instead of ** * pattern. This correspond to "@reboot", "@yearly", "@annually", "@monthly", "@weekly", "@daily", "@midnight" or "@hourly".', + equal_to: %w{ @reboot @yearly @annually @monthly @weekly @daily @midnight @hourly } + + property :minute, [Integer, String], + description: "", + default: "*", callbacks: { + "should be a valid minute spec" => ->(spec) { validate_numeric(spec, 0, 59) } + } + + property :hour, [Integer, String], + description: "", + default: "*", callbacks: { + "should be a valid hour spec" => ->(spec) { validate_numeric(spec, 0, 23) } + } + + property :day, [Integer, String], + description: "", + default: "*", callbacks: { + "should be a valid day spec" => ->(spec) { validate_numeric(spec, 1, 31) } + } + + property :month, [Integer, String], + description: "", + default: "*", callbacks: { + "should be a valid month spec" => ->(spec) { validate_month(spec) } + } + + property :weekday, [Integer, String], + description: "", + default: "*", callbacks: { + "should be a valid weekday spec" => ->(spec) { validate_dow(spec) } + } + + property :command, String, + description: "The command to run.", + required: true + + property :user, String, + description: "The user to run the cron job as.", + default: "root" + + property :mailto, [String, NilClass], + description: "Set the MAILTO environment variable in the cron.d file." + + property :path, [String, NilClass], + description: "Set the PATH environment variable in the cron.d file." + + property :home, [String, NilClass], + description: "" + + property :shell, [String, NilClass], + description: "Set the HOME environment variable in the cron.d file." + + property :comment, [String, NilClass], + description: "A comment to place in the cron.d file." + + property :environment, Hash, + description: "A Hash containing additional arbitrary environment variables under which the cron job will be run.", + default: {} + + property :mode, [String, Integer], + description: "The octal mode of the generated crontab file.", + default: "0600" + + def after_created + raise ArgumentError, "The 'cookbook' property for the cron_d resource is no longer supported now that this resource ships in Chef itself." if new_resource.cookbook + end + + def validate_numeric(spec, min, max) + return true if spec == "*" + # binding.pry + if spec.respond_to? :to_int + return false unless spec >= min && spec <= max + return true + end + + # Lists of invidual values, ranges, and step values all share the validity range for type + spec.split(%r{\/|-|,}).each do |x| + next if x == "*" + return false unless x =~ /^\d+$/ + x = x.to_i + return false unless x >= min && x <= max + end + true + end + + def validate_month(spec) + return true if spec == "*" + if spec.respond_to? :to_int + validate_numeric(spec, 1, 12) + elsif spec.respond_to? :to_str + return true if spec == "*" + # Named abbreviations are permitted but not as part of a range or with stepping + return true if %w{jan feb mar apr may jun jul aug sep oct nov dec}.include? spec.downcase + # 1-12 are legal for months + validate_numeric(spec, 1, 12) + else + false + end + end + + def validate_dow(spec) + return true if spec == "*" + if spec.respond_to? :to_int + validate_numeric(spec, 0, 7) + elsif spec.respond_to? :to_str + return true if spec == "*" + # Named abbreviations are permitted but not as part of a range or with stepping + return true if %w{sun mon tue wed thu fri sat}.include? spec.downcase + # 0-7 are legal for days of week + validate_numeric(spec, 0, 7) + else + false + end + end + + action :create do + create_template(:create) + end + + action :create_if_missing do + create_template(:create_if_missing) + end + + action :delete do + # cleanup the legacy named job if it exists + file "legacy named cron.d file" do + path "/etc/cron.d/#{new_resource.cron_name}" + action :delete + end + + file "/etc/cron.d/#{sanitized_name}" do + action :delete + end + end + + action_class do + def sanitized_name + new_resource.cron_name.tr(".", "-") + end + + def create_template(create_action) + # cleanup the legacy named job if it exists + file "#{new_resource.cron_name} legacy named cron.d file" do + path "/etc/cron.d/#{new_resource.cron_name}" + action :delete + only_if { new_resource.cron_name != sanitized_name } + end + + template "/etc/cron.d/#{sanitized_name}" do + source ::File.expand_path("../support/cron.d.erb", __FILE__) + local true + mode new_resource.mode + variables( + name: sanitized_name, + predefined_value: new_resource.predefined_value, + minute: new_resource.minute, + hour: new_resource.hour, + day: new_resource.day, + month: new_resource.month, + weekday: new_resource.weekday, + command: new_resource.command, + user: new_resource.user, + mailto: new_resource.mailto, + path: new_resource.path, + home: new_resource.home, + shell: new_resource.shell, + comment: new_resource.comment, + environment: new_resource.environment + ) + action create_action + end + end + end + end + end +end diff --git a/lib/chef/resource/support/cron.d.erb b/lib/chef/resource/support/cron.d.erb new file mode 100644 index 0000000000..2f27ecb36b --- /dev/null +++ b/lib/chef/resource/support/cron.d.erb @@ -0,0 +1,28 @@ +# Generated by Chef. Changes will be overwritten. +<% if @mailto -%> +MAILTO=<%= @mailto %> +<% end -%> +<% if @path -%> +PATH=<%= @path %> +<% end -%> +<% if @shell -%> +SHELL=<%= @shell %> +<% end -%> +<% if @home -%> +HOME=<%= @home %> +<% end -%> +<% if @random_delay -%> +RANDOM_DELAY=<%= @random_delay %> +<% end -%> +<% @environment.each do |key, val| -%> +<%= key %>=<%= val.to_s.shellescape %> +<% end -%> + +<% if @comment -%> +# <%= @comment %> +<% end -%> +<% if @predefined_value -%> +<%= @predefined_value %> <%= @user %> <%= @command %> +<% else -%> +<%= @minute %> <%= @hour %> <%= @day %> <%= @month %> <%= @weekday %> <%= @user %> <%= @command %> +<% end -%> diff --git a/lib/chef/resource/support/cron_access.erb b/lib/chef/resource/support/cron_access.erb new file mode 100644 index 0000000000..fdf61172ab --- /dev/null +++ b/lib/chef/resource/support/cron_access.erb @@ -0,0 +1,4 @@ +# Generated by Chef. Changes will be overwritten. +<% @users.sort.uniq.each do |user| -%> +<%= user %> +<% end -%> diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index c13a01c352..069c35691f 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -1,6 +1,6 @@ # # Author:: Daniel DeLeo (<dan@chef.io>) -# Copyright:: Copyright 2010-2016, Chef Software, Inc. +# Copyright:: Copyright 2010-2018, Chef Software, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,8 @@ require "chef/resource/chocolatey_config" require "chef/resource/chocolatey_package" require "chef/resource/chocolatey_source" require "chef/resource/cron" +require "chef/resource/cron_access" +require "chef/resource/cron_d" require "chef/resource/csh" require "chef/resource/directory" require "chef/resource/dmg_package" diff --git a/spec/unit/resource/cron_access_spec.rb b/spec/unit/resource/cron_access_spec.rb new file mode 100644 index 0000000000..1fd16f315f --- /dev/null +++ b/spec/unit/resource/cron_access_spec.rb @@ -0,0 +1,32 @@ +# +# Copyright:: Copyright 2018, Chef Software, 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 "spec_helper" + +describe Chef::Resource::CronAccess do + let(:resource) { Chef::Resource::CronAccess.new("bob") } + + it "has a default action of [:deny]" do + expect(resource.action).to eql([:deny]) + end + + it "accepts create or delete for action" do + expect { resource.action :allow }.not_to raise_error + expect { resource.action :deny }.not_to raise_error + expect { resource.action :lolcat }.to raise_error(ArgumentError) + end +end diff --git a/spec/unit/resource/cron_d_spec.rb b/spec/unit/resource/cron_d_spec.rb new file mode 100644 index 0000000000..99899131f8 --- /dev/null +++ b/spec/unit/resource/cron_d_spec.rb @@ -0,0 +1,32 @@ +# +# Copyright:: Copyright 2018, Chef Software, 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 "spec_helper" + +describe Chef::Resource::CronD do + let(:resource) { Chef::Resource::CronD.new("cronify") } + + it "has a default action of [:create]" do + expect(resource.action).to eql([:create]) + end + + it "accepts create or delete for action" do + expect { resource.action :create }.not_to raise_error + expect { resource.action :delete }.not_to raise_error + expect { resource.action :lolcat }.to raise_error(ArgumentError) + end +end |