summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2018-08-13 17:04:43 -0700
committerGitHub <noreply@github.com>2018-08-13 17:04:43 -0700
commita4c21a9b329f39d4736842e3bbed493856d2cb81 (patch)
tree7d8be67fa2f1efbbf1ade3d502f70ec8e0583844
parent8f292c96472ec35888224c001081f58b7765eb6c (diff)
parent51518beeaf68d8a55efafd7428a1ba045943895d (diff)
downloadchef-a4c21a9b329f39d4736842e3bbed493856d2cb81.tar.gz
Merge pull request #7253 from chef/cron_d
Add cron_d and cron_access resources
-rw-r--r--kitchen-tests/cookbooks/end_to_end/metadata.rb1
-rw-r--r--kitchen-tests/cookbooks/end_to_end/recipes/default.rb10
-rw-r--r--kitchen-tests/kitchen.travis.yml3
-rw-r--r--lib/chef/resource/cron_access.rb70
-rw-r--r--lib/chef/resource/cron_d.rb233
-rw-r--r--lib/chef/resource/support/cron.d.erb28
-rw-r--r--lib/chef/resource/support/cron_access.erb4
-rw-r--r--lib/chef/resources.rb4
-rw-r--r--spec/unit/resource/cron_access_spec.rb36
-rw-r--r--spec/unit/resource/cron_d_spec.rb90
10 files changed, 475 insertions, 4 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/metadata.rb b/kitchen-tests/cookbooks/end_to_end/metadata.rb
index c5300705c6..83cea5c9a8 100644
--- a/kitchen-tests/cookbooks/end_to_end/metadata.rb
+++ b/kitchen-tests/cookbooks/end_to_end/metadata.rb
@@ -15,7 +15,6 @@ depends "resolver"
depends "selinux"
depends "ubuntu"
depends "users"
-depends "cron"
depends "git"
supports "ubuntu"
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
index 77c5d2438e..3985cefd5a 100644
--- a/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
+++ b/kitchen-tests/cookbooks/end_to_end/recipes/default.rb
@@ -63,12 +63,18 @@ include_recipe "nscd"
include_recipe "logrotate"
-include_recipe "cron"
-
include_recipe "git"
directory "/etc/ssl"
+cron_access "bob"
+
+cron_d "some random cron job" do
+ minute 0
+ hour 23
+ command "/usr/bin/true"
+end
+
# Generate new key and certificate
openssl_dhparam "/etc/ssl/dhparam.pem" do
key_length 1024
diff --git a/kitchen-tests/kitchen.travis.yml b/kitchen-tests/kitchen.travis.yml
index aa15da8795..83c6ee839d 100644
--- a/kitchen-tests/kitchen.travis.yml
+++ b/kitchen-tests/kitchen.travis.yml
@@ -35,6 +35,7 @@ platforms:
pid_one_command: /sbin/init
intermediate_instructions:
- RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+ - RUN yum install -y cronie # TODO: remove this anytime past 8/13
- name: amazonlinux-2
driver:
@@ -63,6 +64,7 @@ platforms:
pid_one_command: /sbin/init
intermediate_instructions:
- RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+ - RUN yum install -y cronie # TODO: remove this anytime past 8/13
- name: centos-7
driver:
@@ -78,6 +80,7 @@ platforms:
pid_one_command: /usr/lib/systemd/systemd
intermediate_instructions:
- RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers
+ - RUN yum install -y cronie # TODO: remove this anytime past 8/13
- name: ubuntu-14.04
driver:
diff --git a/lib/chef/resource/cron_access.rb b/lib/chef/resource/cron_access.rb
new file mode 100644
index 0000000000..dbcc0709b7
--- /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.4"
+ description "Use the cron_access resource to manage the /etc/cron.allow and /etc/cron.deny files."
+
+ 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..ba78008ab8
--- /dev/null
+++ b/lib/chef/resource/cron_d.rb
@@ -0,0 +1,233 @@
+#
+# 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.4"
+ description "Use the cron_d resource to manage cron definitions in /etc/cron.d. This is similar to the 'cron' resource, but it does not use the monolithic /etc/crontab file."
+
+ # validate a provided value is between two other provided values
+ # we also allow * as a valid input
+ # @param spec the value to validate
+ # @param min the lowest value allowed
+ # @param max the highest value allowed
+ # @return [Boolean] valid or not?
+ def self.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
+
+ # validate the provided month value to be jan - dec, 1 - 12, or *
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def self.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
+
+ # validate the provided day of the week is sun-sat, 0-7, or *
+ # @param spec the value to validate
+ # @return [Boolean] valid or not?
+ def self.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
+
+ 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: "The minute to schedule the cron job to run at. Valid values: 0-59.",
+ default: "*", callbacks: {
+ "should be a valid minute spec" => ->(spec) { validate_numeric(spec, 0, 59) },
+ }
+
+ property :hour, [Integer, String],
+ description: "The hour to schedule the cron job to run at. Valid values: 0-23.",
+ default: "*", callbacks: {
+ "should be a valid hour spec" => ->(spec) { validate_numeric(spec, 0, 23) },
+ }
+
+ property :day, [Integer, String],
+ description: "The day to schedule the cron job to run at. Valid values: 1-31.",
+ default: "*", callbacks: {
+ "should be a valid day spec" => ->(spec) { validate_numeric(spec, 1, 31) },
+ }
+
+ property :month, [Integer, String],
+ description: "The month to schedule the cron job to run at. Valid values: 1-12, jan-dec, or *.",
+ default: "*", callbacks: {
+ "should be a valid month spec" => ->(spec) { validate_month(spec) },
+ }
+
+ property :weekday, [Integer, String],
+ description: "The day to schedule the cron job to run at. Valid values: 0-7, mon-sun, or *.",
+ 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: "Set the HOME environment variable in the cron.d file."
+
+ 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"
+
+ # warn if someone passes the deprecated cookbook property
+ 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
+
+ action :create do
+ description "Add a cron definition file to /etc/cron.d."
+
+ create_template(:create)
+ end
+
+ action :create_if_missing do
+ description "Add a cron definition file to /etc/cron.d, but do not update an existing file."
+
+ create_template(:create_if_missing)
+ end
+
+ action :delete do
+ description "Remove a cron definition file from /etc/cron.d if it exists."
+
+ # 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
+
+ # @todo this is Chef 12 era cleanup. Someday we should remove it all
+ 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..8fd65a9c60
--- /dev/null
+++ b/spec/unit/resource/cron_access_spec.rb
@@ -0,0 +1,36 @@
+#
+# 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([:allow])
+ 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
+
+ it "the user property is the name_property" do
+ expect(resource.user).to eql("bob")
+ 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..6d29a17aaf
--- /dev/null
+++ b/spec/unit/resource/cron_d_spec.rb
@@ -0,0 +1,90 @@
+#
+# 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
+
+ it "the cron_name property is the name_property" do
+ expect(resource.cron_name).to eql("cronify")
+ end
+
+ context "#validate_dow" do
+ it "it accepts a string day" do
+ expect(Chef::Resource::CronD.validate_dow("mon")).to be true
+ end
+
+ it "it accepts an integer day" do
+ expect(Chef::Resource::CronD.validate_dow(0)).to be true
+ end
+
+ it "it accepts the string of *" do
+ expect(Chef::Resource::CronD.validate_dow("*")).to be true
+ end
+
+ it "returns false for an out of range integer" do
+ expect(Chef::Resource::CronD.validate_dow(8)).to be false
+ end
+
+ it "returns false for an invalid string" do
+ expect(Chef::Resource::CronD.validate_dow("monday")).to be false
+ end
+ end
+
+ context "#validate_month" do
+ it "it accepts a string month" do
+ expect(Chef::Resource::CronD.validate_month("feb")).to be true
+ end
+
+ it "it accepts an integer month" do
+ expect(Chef::Resource::CronD.validate_month(2)).to be true
+ end
+
+ it "it accepts the string of *" do
+ expect(Chef::Resource::CronD.validate_month("*")).to be true
+ end
+
+ it "returns false for an out of range integer" do
+ expect(Chef::Resource::CronD.validate_month(13)).to be false
+ end
+
+ it "returns false for an invalid string" do
+ expect(Chef::Resource::CronD.validate_month("janurary")).to be false
+ end
+ end
+
+ context "#validate_numeric" do
+ it "returns true if the value is in the allowed range" do
+ expect(Chef::Resource::CronD.validate_numeric(5, 1, 100)).to be true
+ end
+
+ it "returns false if the value is out of the allowed range" do
+ expect(Chef::Resource::CronD.validate_numeric(-1, 1, 100)).to be false
+ end
+ end
+end