summaryrefslogtreecommitdiffstats
path: root/docs/best_practices_guide.adoc
blob: 301c6ccdae7546b75e8e4cadeedf60391d61ee8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// vim: ft=asciidoc

= Openshift-Ansible Best Practices Guide

The purpose of this guide is to describe the preferred patterns and best practices used in this repository (both in ansible and python).

It is important to note that this repository may not currently comply with all best practices, but the intention is that it will.

All new pull requests created against this repository MUST comply with this guide.

This guide complies with https://www.ietf.org/rfc/rfc2119.txt[RFC2119].


== Pull Requests

[cols="2v,v"]
|===
| **Rule**
| All pull requests MUST pass the build bot *before* they are merged.
|===


The purpose of this rule is to avoid cases where the build bot will fail pull requests for code modified in a previous pull request.

The tooling is flexible enough that exceptions can be made so that the tool the build bot is running will ignore certain areas or certain checks, but the build bot itself must pass for the pull request to be merged.



== Python

=== PyLint
http://www.pylint.org/[PyLint] is used in an attempt to keep the python code as clean and as managable as possible. The build bot runs each pull request through PyLint and any warnings or errors cause the build bot to fail the pull request.

'''
[cols="2v,v"]
|===
| **Rule**
| PyLint rules MUST NOT be disabled on a whole file.
|===

Instead, http://docs.pylint.org/faq.html#is-it-possible-to-locally-disable-a-particular-message[disable the PyLint check on the line where PyLint is complaining].

'''
[cols="2v,v"]
|===
| **Rule**
| PyLint rules MUST NOT be disabled unless they meet one of the following exceptions
|===

.Exceptions:
1. When PyLint fails because of a dependency that can't be installed on the build bot
1. When PyLint fails because of including a module that is outside of control (like Ansible)

'''
[cols="2v,v"]
|===
| **Rule**
| All PyLint rule disables MUST be documented in the code.
|===

The purpose of this rule is to inform future developers about the disable.

.Specifically, the following MUST accompany every PyLint disable:
1. Why is the check being disabled?
1. Is disabling this check meant to be permanent or temporary?

.Example:
[source,python]
----
# Reason: disable pylint maybe-no-member because overloaded use of
#     the module name causes pylint to not detect that 'results'
#     is an array or hash
# Status: permanently disabled unless a way is found to fix this.
# pylint: disable=maybe-no-member
metadata[line] = results.pop()
----


== Ansible

=== Roles
.Context
* http://docs.ansible.com/playbooks_best_practices.html#directory-layout[Ansible Suggested Directory Layout]

'''
[cols="2v,v"]
|===
| **Rule**
| The Ansible roles directory MUST maintain a flat structure.
|===

.The purpose of this rule is to:
* Comply with the upstream best practices
* Make it familiar for new contributors
* Make it compatible with Ansible Galaxy

'''
[cols="2v,v"]
|===
| **Rule**
| Ansible Roles SHOULD be named like technology_component[_subcomponent].
|===

For clarity, it is suggested to follow a pattern when naming roles. It is important to note that this is a recommendation for role naming, and follows the pattern used by upstream.

Many times the `technology` portion of the pattern will line up with a package name. It is advised that whenever possible, the package name should be used.

.Examples:
* The role to configure an OpenShift Master is called `openshift_master`
* The role to configure OpenShift specific yum repositories is called `openshift_repos`

=== Filters
.Context:
* https://docs.ansible.com/playbooks_filters.html[Ansible Playbook Filters]
* http://jinja.pocoo.org/docs/dev/templates/#builtin-filters[Jinja2 Builtin Filters]

'''
[cols="2v,v"]
|===
| **Rule**
| The `default` filter SHOULD replace empty strings, lists, etc.
|===

When using the jinja2 `default` filter, unless the variable is a boolean, specify `true` as the second parameter. This will cause the default filter to replace empty strings, lists, etc with the provided default.

This is because it is preferable to either have a sane default set than to have an empty string, list, etc. For example, it is preferable to have a config value set to a sane default than to have it simply set as an empty string.

.From the http://jinja.pocoo.org/docs/dev/templates/[Jinja2 Docs]:
[quote]
If you want to use default with variables that evaluate to false you have to set the second parameter to true

.Example:
[source,yaml]
----
---
- hosts: localhost
  gather_facts: no
  vars:
    somevar: ''
  tasks:
  - debug: var=somevar

  - name: "Will output 'somevar: []'"
    debug: "msg='somevar: [{{ somevar | default('the string was empty') }}]'"

  - name: "Will output 'somevar: [the string was empty]'"
    debug: "msg='somevar: [{{ somevar | default('the string was empty', true) }}]'"
----


In other words, normally the `default` filter will only replace the value if it's undefined. By setting the second parameter to `true`, it will also replace the value if it defaults to a false value in python, so None, empty list, empty string, etc.

This is almost always more desirable than an empty list, string, etc.