Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Extending Foodcritic with new rules
Search
Andrew Crump
February 04, 2014
Programming
690
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Extending Foodcritic with new rules
Config Management Camp, Gent on 4th February 2014
Andrew Crump
February 04, 2014
More Decks by Andrew Crump
See All by Andrew Crump
Porting a small project from Go to Rust
acrmp
0
33
Docker and Cloud Foundry
acrmp
0
120
Continuous Delivery with Cloud Foundry
acrmp
1
110
Patterns for treating infrastructure as code
acrmp
1
310
Other Decks in Programming
See All in Programming
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
210
音楽のための関数型プログラミング言語mimiumにおける多段階計算の活用
tomoyanonymous
1
330
AWS CDK を「作」ってみた 〜フルスクラッチで見えた CDK の裏側〜 / aws-cdk-from-scratch
gotok365
3
420
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
110
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
1.5k
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
240
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.5k
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
共通化で考えるべきは、実装より公開する型だった
codeegg
0
240
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
1
190
Haskell/Servantを通してWebミドルウェアを捉え直す
pizzacat83
1
580
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
100
Featured
See All Featured
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
630
Unsuck your backbone
ammeep
672
58k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.4k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
220
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
3.8k
Accessibility Awareness
sabderemane
1
160
Designing for humans not robots
tammielis
254
26k
Between Models and Reality
mayunak
4
370
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Code Review Best Practice
trishagee
74
20k
The Cult of Friendly URLs
andyhume
79
6.9k
Transcript
Extending foodcritic with new rules #cfgmgmtcamp #getchef
Overview
What is foodcritic?
A lint tool for your cookbooks
47 Built-in Rules
Goal: Identify problems in your cookbooks
file "/var/lib/foo" do owner "root" group "root" mode 644 action
:create end
file "/var/lib/foo" do owner "root" group "root" mode 644 action
:create end
$ foodcritic . FC006: Mode should be quoted or fully
specified when setting file permissions: ./ recipes/default.rb:9
file "/var/lib/foo" do owner "root" group "root" mode "644" action
:create end
Goal: Encourage discussion on the subjective stuff
execute "wget 'http:// example.org/' -Ofoo" do action :run end
execute "wget 'http:// example.org/' -Ofoo" do action :run end
$ foodcritic . FC041: Execute resource used to run curl
or wget commands: ./recipes/ default.rb:9
remote_file "foo" do source "http://example.org/" end
Goal: Write your own rules
# Specify rules on # the command line $ foodcritic
-I your_rule.rb . ! # You can also # package your rules # as a gem
How does it work?
Static code analysis
Example: Declaring resources in a loop
# The original recipe %w{foo bar baz}.each do |pkg| package
pkg end
# This is what Chef sees at converge time package[foo]
package[bar] package[baz]
# This is what foodcritic sees %w{foo bar baz}.each do
|pkg| package pkg end
Peter Zotov @whitequark fucking ruby and its fucking parser, how
does it even work?! I *still* have no idea
# recipe with a single resource file '/etc/foo' do mode
0600 content 'bar' end
pry> Ripper.sexp(code)
=> [:program, [[:method_add_block, [:command, [:@ident, "file", [2, 0]], [:args_add_block, [[:string_literal,
[:string_content, [:@tstring_content, "/ etc/foo", [2, 6]]]]], false]], [:do_block, nil, [[:command, [:@ident, "mode", [3, 2]], [:args_add_block, [[:@int, "0600", [3, 7]]], false]], [:command, [:@ident, "content", [4, 2]], [:args_add_block, [[:string_literal, [:string_content, [:@tstring_content, "bar", [4, 11]]]]], false]]]]]]]
Those are some deeply nested arrays
Ruby lets us build terse yet powerful expressions
But the path to a node in the tree is
important
<? XML ?> An elegant weapon for a more civilised
age
XPath //method_add_block/ command/ ident[@value="mode"]
ANTLR 4.2 SNAPSHOT "xpath/pattern matching for parse trees"
The structure of a rule
rule "FC123", "Name" do recipe do |ast| # some #
expression end end
cookbook environment library metadata resource role Hooks recipe do |ast|
ast.xpath(..) end
rule "FC123", "Name" do recipe do |ast| # some #expression
end end
Ruby returns the last expression Foodcritic converts the last expression
to matches
Let’s walk through the code for FC006
rule "FC006", "Mode should be quoted or fully specified when
setting file permissions" do
# We pass an array of tags in, common tags
include 'correctness' and 'style' tags %w{correctness files}
recipe do |ast| ast.xpath(%q{a/ long/xpath/string}) end
# any mode identifier // ident[@value='mode']/ ! # we need
to go up one parent::command/ ! # a literal integer descendant::int
# filtering again [ ! # needs to be less
# than five digits # long string-length(@value) < 5
# match 644 not 0644 and not( starts-with(@value, "0") and
string length(@value) = 4 ) ! # stop filtering ]
# choose the line num # to show to the
end # user /ancestor:: method_add_block # do this if the AST # node does not have # a descendant node # with a position
Pry is awesome and is a must for developing FC
rules
Drop a pry binding in your rule and explore pry>
ls pry> a_method() pry> whereami
rule "FC123", "Name" do recipe do |ast| binding.pry end end
pry> ast.xpath( '//do_block/command/ ident')
Tip: Chef now includes Pry as a runtime dependency as
of 11.8.0.
ancestor child descendant following parent preceding Axes
XPath expressions can be annoying ! Here’s some I prepared
earlier
attribute_access declared_dependencies find_resources notifications resource_attribute? resources_by_type searches API Foodcritic
all? any? find group_by inject map select Ruby Enumerable
Testing it actually works
Trust is really important - false positives undermine that trust
in the tool
Foodcritic core rules test against lots of examples
Examples: | environment_name | show_warning | | production | should
not | | pre_production | should not | | production-eu | should not | | production2 | should not | | Production | should not | | EU West | should | | production (eu-west) | should |
Different syntax in recipes results in different AST trees
Different Ruby versions can mean different AST trees 1.9.2, 1.9.3
and 2.0.0
Ok. I have my automated examples passing. I should be
good right?
Ok. I have my automated examples passing. I should be
good right? Nee! Non! No!
Test against a large corpus of real cookbooks
Ideas
Suggest Chef Sugar FC789: Use Chef Sugar to simplify platform
check
Spot duplication across recipes FC678: Compose recipes to reduce duplication
Validate recipe package names FC567: Package not present in distribution
What’s next?
Backlog of issues that could use your help!
Hack Afternoon
Thanks! @acrmp foodcritic.io freenode #chef
Extending foodcritic with new rules #cfgmgmtcamp #getchef