GithubHelp home page GithubHelp logo

foodcritic-rules's Introduction

#foodcritic-rules

These are the foodcritic rules we've written here at Etsy to enforce rules on our cookbooks. Some of these rules are best practices we've identified for our particular use case, and some were put in place as remediation items after issues we experienced.

Usage

Once you've cloned the repo from github, you can run foodcritic using the following options to test your cookbooks against these rules:

foodcritic -t etsy,FC023 -I <rules.rb> cookbooks

You should make sure that <rules.rb> is replaced with the location of the rules.rb file from this repository.

Note that we're also checking against the built in foodcritic rule FC023 (Prefer conditional attributes). This is to complement the functionality in rule ETSY004 (see description below)

All of the below rules have the "style" tag as they are best practices and recommendations from our experience rather than rules to detect things which might break your cookbooks.

Rules

ETSY001 - Package or yum_package resource used with :upgrade action on non-whitelisted package

We've had several instances of new packages being put into our yum repository and rolled out to servers before we intended due to the use of the :upgrade action in "package" and "yum_package" resources. This rule will detect when :upgrade has been used instead of :install. An optional whitelist is included at the top of the file to allow you to relax this rule manually for specific packages.

The whitelisted package list is defined at the top of rules.rb, and defaults to empty:

@pkgupgrade_whitelist = []

For example, this block would trip this rule:

package "sendmail" do
  action :upgrade
end

ETSY002 - Execute resource used to run git commands

This rule detects when Git commands are run as bash commands inside of an "execute" block, rather than using the "git" resource. We alert on this behaviour as running these commands inside "execute" blocks gives you much less visibility of the commands' output and status.

For example, this block would trip this rule:

execute "checkout" do
  command "git clone git://github.com/foo/bar.git"
  cwd "/home/me"
end

ETSY003 - Execute resource used to run curl or wget commands

This rule detects when wget or curl commands are run as bash commands inside of an "execute" block, rather than using the "remote_file" resource. We alert on this behaviour as running these commands inside "execute" blocks gives you much less visibility of the commands' output and status.

For example, this block would trip this rule:

execute "fetch_file" do
    command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
    not_if "test -f /tmp/foo.tar.gz"
end

ETSY004 - Execute resource defined without conditional or action :nothing

This rule detects when an "execute" block is defined without specifying a conditional (ie not_if or only_if) or action :nothing. This is to avoid the presence of resources which run on every chef run, whether they need to or not. Please note, this rule does not identify "execute" blocks which are themselves wrapped in a conditional, ie an if statement. For this reason, we recommend that you also use the built in foodcritic rule FC023, which detects this case. Rule FC023 recommends that you always use conditionals inside your resources instead of wrapping them, and we'd concur with this.

For example, this block would trip this rule:

execute "fetch_file" do
    command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
end

As would this block, even though there is technically a conditional wrapping the resource.

if !File.exists?("/tmp/foo.tar.gz")
    execute "fetch_file" do
        command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
    end
end

However, the above block would subsequently be flagged by rule FC023.

ETSY005 - Action :restart sent to a core service

This rule identifies when a recipe might send "action :restart" to a service we've identified as a "core" service. We've had instances in the past of chef changes causing hard restarts of services such as memcached or httpd, which can have unexpected side effects. For this reason, we alert if any restart notifications are sent to a defined list of "core" services. Restart notifications sent to any services not in this list will not cause this rule to trip.

The core services list is defined at the top of rules.rb:

@coreservices = ["httpd", "mysql", "memcached", "postgresql-server"]

For example, this block would trip this rule:

cookbook_file "/etc/httpd/conf.d/myvhost.conf" do
  source "myvhost.conf"
  mode 0644
  owner "root"
  group "root"
  notifies :restart, resources(:service => "httpd")
end

Wheras this block would not (as we're using reload instead of restart):

cookbook_file "/etc/httpd/conf.d/myvhost.conf" do
  source "myvhost.conf"
  mode 0644
  owner "root"
  group "root"
  notifies :reload, resources(:service => "httpd")
end

And neither would this one (as sendmail is not a core service):

template "/etc/mail/sendmail.mc" do
  source "sendmail.mc.erb"
  owner "root"
  group "root"
  mode 00644
  notifies :restart, resources(:service => "sendmail")
end

ETSY006 - Execute resource used to run chef-provided command

This rule detects when an "execute" block is using a shell command when Chef already provides a built in resource with equivalent functionality.

The list of commands to check for is defined at the top of rules.rb:

@corecommands = ["yum -y", "yum install", "yum reinstall", "yum remove", "mkdir", "useradd", "usermod", "touch"]

For example, this block would trip this rule:

execute "reinstall_pear" do
  command "yum -y reinstall php-pear"
end

ETSY007 - Package or yum_package resource used to install core package without specific version number

This rule identifies when a recipe is installing a package for something we've identified as a "core" service without specifying a specific version number to install. We've had instances in the past of divergence occurring when action :install is used by itself, and a new package has been added to the repository resulting in new servers installing the new version while old servers are still using the previous version.

The list of package names to check for is defined at the top of rules.rb:

@coreservicepackages = ["httpd", "Percona-Server-server-51", "memcached", "postgresql-server"]

For example, this block would trip this rule:

package "httpd" do
  action :install
end

foodcritic-rules's People

Contributors

jonlives avatar mconigliaro avatar timurb avatar wheresalice avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

foodcritic-rules's Issues

Rubygem

Have you considered packaging these in a rubygem?

ETSY004 does not report multiple violations if one is ok

If you have a recipe with multiple ETSY004 violations, then correct one of them by adding a conditional, the rest of the violations will no longer be reported.

execute "fetch_file" do
  command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
end

execute "fetch_file" do
  command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
end

results in

$ foodcritic -t ETSY004 -I ~/src/etsy/foodcritic-rules/rules.rb .
ETSY004: Execute resource defined without conditional or action :nothing: ./recipes/default.rb:1
ETSY004: Execute resource defined without conditional or action :nothing: ./recipes/default.rb:5
$

Changing the recipe to

execute "fetch_file" do
  command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
  not_if { File.exists?("/tmp/foo.tar.gz") }
end

execute "fetch_file" do
  command "wget -O /tmp/foo.tar.gz http://me.mycorp.com/files/foo.tar.gz"
end

results in

$ foodcritic -t ETSY004 -I ~/src/etsy/foodcritic-rules/rules.rb .

$

FYI

$ gem list | grep foodcritic
foodcritic (1.6.1, 1.4.0, 1.3.0)

False positives on ETSY006

I get false positives for ETSY006 when using "execute" resources on a non-core binary.

For example, I have:

    execute "/usr/sbin/vmtouch -d -L /my/path" do
      not_if "pidof vmtouch"
    end

"touch" is considered to be core, "vmtouch" is not, but still matches.

ETSY003 does not detect use of wget in all situations

Given this resource:

script "install_elasticsearch" do
  interpreter "bash"
  user "root"
  cwd "/tmp"
  code <<-EOH
    wget "#{node['elasticsearch']['download_url']}"
    tar xvzf #{node['elasticsearch']['filename']} -C #{node['elasticsearch']['dir']}
    ln -s "#{node['elasticsearch']['home_dir']}-#{node['elasticsearch']['version']}" #{node['elasticsearch']['home_dir']}
  EOH
  not_if "ls #{node['elasticsearch']['home_dir']}-#{node['elasticsearch']['version']}"
end

You would expect that ETSY003 would trip, but it does not.

ETSY005 AST Corner Cases

This rule does not seem to detect that the following two notification lines are effectively the same thing:

notifies(:restart, resources(:service => "riak"))

notifies :restart, resources(:service => "riak")

The latter is detected by the rule, the former is not.

Additionally something like the following is not detected:

notifies :restart, "service[openvpn]"

Additionally if the service name happens to be a variable it won't detect it, example:

notifies :restart, resources(:service => riak)

Please specify a license

As far as I can tell neither the README nor the code itself contain any reference to the license this code is released under, which means anyone paying attention to their use of third party software (or, more likely, working for someone who pays attention to the licenses of third party software) cannot use this code.

Please could you fix that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.