does your configuration code smell?

49
Does Your Configuration Code Smell? Tushar Sharma

Upload: tushar-sharma

Post on 08-Feb-2017

205 views

Category:

Software


3 download

TRANSCRIPT

Does Your Configuration Code Smell?

Tushar Sharma

“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.”

“Smells”

Configuration Smells

“Infrastructure as Code” (IaC)

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 smells

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

Design configuration smells

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.

class web { }

init.pp

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)

Implications of smells

The straw that broke the camel's back

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/

Let us summarize

ME

LS

U

P

EER

II

P

T

U

I NM YI

IA A BNC

Let us summarize

ME

LS

U

P

EER

II

P

T

U

IA NM YI

IA A BNC

Let us summarize

SME

LS

U

P

EER

L II

P

T

U

IA NM YI

IA A BNC

Let us summarize

SME

LS

U

P

P

EER

L II

P

T

U

IA NM YI

IT A A BNC

Thank you!!

Courtesy: spikedmath.com

Tushar Sharma [email protected]

@Sharma__Tushar