does your configuration code smell?
TRANSCRIPT
“Smells”
Code Smell
…certain structures in the code that suggest (sometimes they scream for) the possibility of refactoring.
“Smells”
Design Smells
“Design smells are certain structures in the design that indicate violation of fundamental design principles and negatively impact design quality.”
Puppet example
package { 'apache2': require => Exec['apt-update'],
ensure => installed, }
service { 'apache2':
ensure => running, }
user { 'tushar':
ensure => present, uid => '1000', gid => '1000',
shell => '/bin/bash', home => '/home/tushar' }
Configuration management tools: Ansible, Chef, CFEngine, Puppet
Software System
IaC and Traditional SE
Production code
Infrastructure, configuration code, tools and services
Apply traditional software
engineering practices
Configuration smells
“Configuration smells are the characteristics of a configuration program or script that violate the
recommended best practices and potentially affect the program’s quality in a negative way.”
Implementation configuration smellsimport 'classes/*' # TODO: fix deprecated statement class app-studio (String $version = ‘latest') { $primary_config_file = 'config' $source_config = 'config.source' if $version == ’44’ or $version == ‘4.2’ or $version != ‘4.5’ or $version == ‘4.9’{ case $::operatingsystem { 'debian': { apt::source { 'packages.dotdeb.org-repo.app': location => 'http://repo.app.com/dotdeb/', release => $::lsbdistcodename, repos => 'all', include_src => true include_src => true } } }
Deprecated Statement Usage
Implementation configuration smellsimport 'classes/*' # TODO: fix deprecated statement class app-studio (String $version = ‘latest') { $primary_config_file = 'config' $source_config = 'config.source' if $version == ’44’ or $version == ‘4.2’ or $version != ‘4.5’ or $version == ‘4.9’{ case $::operatingsystem { 'debian': { apt::source { 'packages.dotdeb.org-repo.app': location => 'http://repo.app.com/dotdeb/', release => $::lsbdistcodename, repos => 'all', include_src => true include_src => true } } }
Incomplete Task
Implementation configuration smellsimport 'classes/*' # TODO: fix deprecated statement class app-studio (String $version = ‘latest') { $primary_config_file = 'config' $source_config = 'config.source' if $version == ’44’ or $version == ‘4.2’ or $version != ‘4.5’ or $version == ‘4.9’{ case $::operatingsystem { 'debian': { apt::source { 'packages.dotdeb.org-repo.app': location => 'http://repo.app.com/dotdeb/', release => $::lsbdistcodename, repos => 'all', include_src => true include_src => true } } }
Long Statement
Complex Expression
Implementation configuration smellsimport 'classes/*' # TODO: fix deprecated statement class app-studio (String $version = ‘latest') { $primary_config_file = 'config' $source_config = 'config.source' if $version == ’44’ or $version == ‘4.2’ or $version != ‘4.5’ or $version == ‘4.9’{ case $::operatingsystem { 'debian': { apt::source { 'packages.dotdeb.org-repo.app': location => 'http://repo.app.com/dotdeb/', release => $::lsbdistcodename, repos => 'all', include_src => true include_src => true } } }
Missing Default Case
Implementation configuration smellsimport 'classes/*' # TODO: fix deprecated statement class app-studio (String $version = ‘latest') { $primary_config_file = 'config' $source_config = 'config.source' if $version == ’44’ or $version == ‘4.2’ or $version != ‘4.5’ or $version == ‘4.9’{ case $::operatingsystem { 'debian': { apt::source { 'packages.dotdeb.org-repo.app': location => 'http://repo.app.com/dotdeb/', release => $::lsbdistcodename, repos => 'all', include_src => true include_src => true } } }
Duplicate Entity
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Missing Conditional
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Improper Quote Usage
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Unguarded Variable
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Improper Alignment
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Invalid Property Value
Implementation configuration smells elsif $version in ['33', '3.3'] { } if $::kernelversion =~ /^(2.2)/ { $appversion = '3.5' } elsif $::kernelversion =~ /^(2.1)/ { exec {"download_app_studio": command => "wget $url", timeout => 0, } } $version = '3.4' ? {undef => $primary_config_file, default => $source_config}
file { "/root/.app": mode => '644', ensure => present } }
Misplaced Attribute
class package::web { … } class package::mail { … } class package::environment { … } class package::user { … }
package.pp
class apache { package { 'apache2': … } service { 'apache2': … } service { 'mysql': … } package { 'php5': … } file { ‘/etc/apache2/ports.conf': … } user { ‘mitchell': … }
}
apache.pp
Multifaceted Abstraction
•
Each abstraction should be designed to specify the properties of a single piece of software.
Unnecessary Abstraction
A class, ‘define’, or module must contain declarations or statements specifying the properties of a desired system.
class web { exec { ‘hadoop-yarn’: … } exec { ‘apache-util-set’: … } exec { ‘smail-invoke’: … } exec { ‘postfix-set’: … }
}
init.pp
Imperative Abstraction
An abstraction containing numerous imperative statements suffers from imperative abstraction smell.
package { 'apache2': … } service { 'apache2': … } service { 'mysql': … } package { 'php5': … } file { ‘/etc/apache2/ports.conf': … } user { ‘mitchell': … }
init.pp
Missing Abstraction
A module suffers from the missing abstraction smell when resources and language elements are declared and used
without encapsulating them in an abstraction.
class apache { package { 'apache2': … } service { 'apache2': … } file { ‘/etc/apache2/ports.conf': … } …
}
web.pp
class apache { package { 'apache2': … } service { 'apache2': … } file { ‘/etc/apache2/ports.conf': … } …
}
server.pp
Duplicate Block
A duplicate block of statements more than a threshold indicates that probably a suitable abstraction definition is
missing.
package { 'apache2': … } service { 'apache2': … } service { 'mysql': … } package { 'php5': … } file { ‘/etc/apache2/ports.conf': … } user { ‘mitchell': … } package { 'apache2': … } service { 'apache2': … } service { 'mysql': … } package { 'php5': … } file { ‘/etc/apache2/ports.conf': … } user { ‘mitchell': … } package { 'apache2': … } service { 'apache2': … } service { 'mysql': … } package { 'php5': … } file { ‘/etc/apache2/ports.conf': … } user { ‘mitchell': … }
init.pp
Insufficient Modularisation
An abstraction suffers from this smell when it is large or complex and thus can be modularized further.
Unstructured ModuleEach module in a configuration repository must have a
well-defined and consistent module structure.
RepoName
Manifests Modules README …
Apache Adobe …
files lib manifests spec templates tests README …
Dense StructureThis smell arises when a configuration code repository
has excessive and dense dependencies.
Weekend ModularityThis smell arises when a module exhibits high
coupling and low cohesion.
Modularity ratio (A) = Cohesion(A)
Coupling(A)
Detecting configuration smells
Implementation configuration smells • Puppet-lint [1] • Additional custom rules in Puppeteer
Design configuration smells • Puppeteer [2] — an open source tool
1. http://puppet-lint.com/
2. https://github.com/tushartushar/Puppeteer
Mining GitHub RepositoriesRepositories 4,621
Puppet files 142,662
Class declarations 132,323
Define declarations 39,263
File resources 117,286
Package resources 49,841
Service resources 18,737
Exec declarations 43,468
Lines of code (Puppet only) 8,948,611
http://www.tusharma.in/research/does-your-configuration-code-smell-msr-2016/