diff options
-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 |