mirror of https://github.com/Icinga/icinga2.git
Merge pull request #7199 from Icinga/feature/docs-dev-style-guide
Docs: Add styleguide with development details
This commit is contained in:
commit
794374a32c
|
@ -246,11 +246,8 @@ git push -f origin bugfix/notifications
|
||||||
|
|
||||||
## <a id="contributing-testing"></a> Testing
|
## <a id="contributing-testing"></a> Testing
|
||||||
|
|
||||||
Basic unit test coverage is provided by running `make test` during package builds.
|
Please follow the [documentation](https://icinga.com/docs/icinga2/snapshot/doc/21-development/#test-icinga-2)
|
||||||
Read the [INSTALL.md](INSTALL.md) file for more information about development builds.
|
for build and test instructions.
|
||||||
|
|
||||||
Snapshot packages from the latest development branch are available inside the
|
|
||||||
[package repository](https://packages.icinga.com).
|
|
||||||
|
|
||||||
You can help test-drive the latest Icinga 2 snapshot packages inside the
|
You can help test-drive the latest Icinga 2 snapshot packages inside the
|
||||||
[Icinga 2 Vagrant boxes](https://github.com/icinga/icinga-vagrant).
|
[Icinga 2 Vagrant boxes](https://github.com/icinga/icinga-vagrant).
|
||||||
|
@ -258,17 +255,11 @@ You can help test-drive the latest Icinga 2 snapshot packages inside the
|
||||||
|
|
||||||
## <a id="contributing-patches-source-code"></a> Source Code Patches
|
## <a id="contributing-patches-source-code"></a> Source Code Patches
|
||||||
|
|
||||||
Icinga 2 is written in C++ and uses the Boost libraries. We are also using the C++11 standard where applicable (please
|
|
||||||
note the minimum required compiler versions in the [INSTALL.md](INSTALL.md) file.
|
|
||||||
|
|
||||||
Icinga 2 can be built on Linux/Unix nodes and Windows clients. In order to develop patches for Icinga 2,
|
Icinga 2 can be built on Linux/Unix nodes and Windows clients. In order to develop patches for Icinga 2,
|
||||||
you should prepare your own local build environment and know how to work with C++.
|
you should prepare your own local build environment and know how to work with C++.
|
||||||
|
|
||||||
More tips:
|
Please follow the [development documentation](https://icinga.com/docs/icinga2/latest/doc/21-development/)
|
||||||
|
for development environments, the style guide and more advanced insights.
|
||||||
* Requirements and source code installation for Linux/Unix is explained inside the [INSTALL.md](INSTALL.md) file.
|
|
||||||
* Debug requirements and GDB instructions can be found in the [documentation](https://github.com/Icinga/icinga2/blob/master/doc/20-development.md).
|
|
||||||
* If you are planning to develop and debug the Windows client, setup a Windows environment with [Visual Studio](https://www.visualstudio.com/vs/community/). An example can be found in [this blogpost](https://blog.netways.de/2015/08/24/developing-icinga-2-on-windows-10-using-visual-studio-2015/).
|
|
||||||
|
|
||||||
## <a id="contributing-patches-documentation"></a> Documentation Patches
|
## <a id="contributing-patches-documentation"></a> Documentation Patches
|
||||||
|
|
||||||
|
|
|
@ -308,7 +308,7 @@ consider passing them in the request body. For GET requests, this method is expl
|
||||||
[here](12-icinga2-api.md#icinga2-api-requests-method-override).
|
[here](12-icinga2-api.md#icinga2-api-requests-method-override).
|
||||||
|
|
||||||
You can use [jo](https://github.com/jpmens/jo) to format JSON strings on the shell. An example
|
You can use [jo](https://github.com/jpmens/jo) to format JSON strings on the shell. An example
|
||||||
for API actions shown [here](#icinga2-api-actions-unix-timestamps).
|
for API actions shown [here](12-icinga2-api.md#icinga2-api-actions-unix-timestamps).
|
||||||
|
|
||||||
|
|
||||||
### Global Parameters <a id="icinga2-api-parameters-global"></a>
|
### Global Parameters <a id="icinga2-api-parameters-global"></a>
|
||||||
|
|
|
@ -4,20 +4,27 @@ This chapter provides hints on Icinga 2 debugging,
|
||||||
development, package builds and tests.
|
development, package builds and tests.
|
||||||
|
|
||||||
* [Debug Icinga 2](21-development.md#development-debug)
|
* [Debug Icinga 2](21-development.md#development-debug)
|
||||||
* [GDB Backtrace](21-development.md#development-debug-gdb-backtrace)
|
* [GDB Backtrace](21-development.md#development-debug-gdb-backtrace)
|
||||||
* [Core Dump](21-development.md#development-debug-core-dump)
|
* [Core Dump](21-development.md#development-debug-core-dump)
|
||||||
* [Test Icinga 2](21-development.md#development-tests)
|
* [Test Icinga 2](21-development.md#development-tests)
|
||||||
* [Snapshot Packages (Nightly Builds)](21-development.md#development-tests-snapshot-packages)
|
* [Snapshot Packages (Nightly Builds)](21-development.md#development-tests-snapshot-packages)
|
||||||
* [Develop Icinga 2](21-development.md#development-develop)
|
* [Develop Icinga 2](21-development.md#development-develop)
|
||||||
* [Linux Dev Environment](21-development.md#development-linux-dev-env)
|
* [Preparations](21-development.md#development-develop-styleguide)
|
||||||
* [macOS Dev Environment](21-development.md#development-macos-dev-env)
|
* [Design Patterns](21-development.md#development-develop-design-patterns)
|
||||||
* [Windows Dev Environment](21-development.md#development-windows-dev-env)
|
* [Build Tools](21-development.md#development-develop-builds-tools)
|
||||||
|
* [Unit Tests](21-development.md#development-develop-tests)
|
||||||
|
* [Style Guide](21-development.md#development-develop-styleguide)
|
||||||
|
* [Development Environment](21-development.md#development-environment)
|
||||||
|
* [Linux Dev Environment](21-development.md#development-linux-dev-env)
|
||||||
|
* [macOS Dev Environment](21-development.md#development-macos-dev-env)
|
||||||
|
* [Windows Dev Environment](21-development.md#development-windows-dev-env)
|
||||||
* [Package Builds](21-development.md#development-package-builds)
|
* [Package Builds](21-development.md#development-package-builds)
|
||||||
* [RPM](21-development.md#development-package-builds-rpms)
|
* [RPM](21-development.md#development-package-builds-rpms)
|
||||||
* [DEB](21-development.md#development-package-builds-deb)
|
* [DEB](21-development.md#development-package-builds-deb)
|
||||||
* [Windows](21-development.md#development-package-builds-windows)
|
* [Windows](21-development.md#development-package-builds-windows)
|
||||||
* [Advanced Tips](21-development.md#development-advanced)
|
* [Advanced Tips](21-development.md#development-advanced)
|
||||||
|
|
||||||
|
<!-- mkdocs requires 4 spaces indent for nested lists: https://github.com/Python-Markdown/markdown/issues/3 -->
|
||||||
|
|
||||||
## Debug Icinga 2 <a id="development-debug"></a>
|
## Debug Icinga 2 <a id="development-debug"></a>
|
||||||
|
|
||||||
|
@ -559,7 +566,9 @@ not a full-featured master or satellite.
|
||||||
|
|
||||||
Before you start with actual development, there is a couple of pre-requisites.
|
Before you start with actual development, there is a couple of pre-requisites.
|
||||||
|
|
||||||
### Choose your Editor <a id="development-develop-choose-editor"></a>
|
### Preparations <a id="development-develop-prepare"></a>
|
||||||
|
|
||||||
|
#### Choose your Editor <a id="development-develop-choose-editor"></a>
|
||||||
|
|
||||||
Icinga 2 can be developed with your favorite editor. Icinga developers prefer
|
Icinga 2 can be developed with your favorite editor. Icinga developers prefer
|
||||||
these tools:
|
these tools:
|
||||||
|
@ -572,44 +581,14 @@ these tools:
|
||||||
Editors differ on the functionality. The more helpers you get for C++ development,
|
Editors differ on the functionality. The more helpers you get for C++ development,
|
||||||
the faster your development workflow will be.
|
the faster your development workflow will be.
|
||||||
|
|
||||||
|
#### Get to know the architecture <a id="development-develop-get-to-know-the-architecture"></a>
|
||||||
#### Whitespace Cleanup <a id="development-develop-choose-editor-whitespaces"></a>
|
|
||||||
|
|
||||||
Patches must be cleaned up and follow the indent style (tabs instead of spaces).
|
|
||||||
You should also remove any training whitespaces.
|
|
||||||
|
|
||||||
`git diff` allows to highlight such.
|
|
||||||
|
|
||||||
```
|
|
||||||
vim $HOME/.gitconfig
|
|
||||||
|
|
||||||
[color "diff"]
|
|
||||||
whitespace = red reverse
|
|
||||||
[core]
|
|
||||||
whitespace=fix,-indent-with-non-tab,trailing-space,cr-at-eol
|
|
||||||
```
|
|
||||||
|
|
||||||
`vim` also can match these and visually alert you to remove them.
|
|
||||||
|
|
||||||
```
|
|
||||||
vim $HOME/.vimrc
|
|
||||||
|
|
||||||
highlight ExtraWhitespace ctermbg=red guibg=red
|
|
||||||
match ExtraWhitespace /\s\+$/
|
|
||||||
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
|
|
||||||
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
|
|
||||||
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
|
|
||||||
autocmd BufWinLeave * call clearmatches()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get to know the architecture <a id="development-develop-get-to-know-the-architecture"></a>
|
|
||||||
|
|
||||||
Icinga 2 can run standalone or in distributed environments. It contains a whole lot
|
Icinga 2 can run standalone or in distributed environments. It contains a whole lot
|
||||||
more than a simple check execution engine.
|
more than a simple check execution engine.
|
||||||
|
|
||||||
Read more about it in the [Technical Concepts](19-technical-concepts.md#technical-concepts) chapter.
|
Read more about it in the [Technical Concepts](19-technical-concepts.md#technical-concepts) chapter.
|
||||||
|
|
||||||
### Get to know the code <a id="development-develop-get-to-know-the-code"></a>
|
#### Get to know the code <a id="development-develop-get-to-know-the-code"></a>
|
||||||
|
|
||||||
First off, you really need to know C++ and portions of C++11 and the boost libraries.
|
First off, you really need to know C++ and portions of C++11 and the boost libraries.
|
||||||
Best is to start with a book or online tutorial to get into the basics.
|
Best is to start with a book or online tutorial to get into the basics.
|
||||||
|
@ -630,7 +609,6 @@ In addition, it is a good bet to also know SQL when diving into backend developm
|
||||||
|
|
||||||
Last but not least, if you are developing on Windows, get to know the internals about services and the Win32 API.
|
Last but not least, if you are developing on Windows, get to know the internals about services and the Win32 API.
|
||||||
|
|
||||||
|
|
||||||
### Design Patterns <a id="development-develop-design-patterns"></a>
|
### Design Patterns <a id="development-develop-design-patterns"></a>
|
||||||
|
|
||||||
Icinga 2 heavily relies on object-oriented programming and encapsulates common
|
Icinga 2 heavily relies on object-oriented programming and encapsulates common
|
||||||
|
@ -689,8 +667,9 @@ vim lib/perfdata/gelfwriter.cpp
|
||||||
The logic is hidden in `tools/mkclass/` in case you want to learn more about it.
|
The logic is hidden in `tools/mkclass/` in case you want to learn more about it.
|
||||||
The first steps during CMake & make also tell you about code generation.
|
The first steps during CMake & make also tell you about code generation.
|
||||||
|
|
||||||
|
### Build Tools <a id="development-develop-builds-tools"></a>
|
||||||
|
|
||||||
### Builds: CMake <a id="development-develop-builds-cmake"></a>
|
#### CMake <a id="development-develop-builds-cmake"></a>
|
||||||
|
|
||||||
In its early development stages in 2012, Icinga 2 was built with autoconf/automake
|
In its early development stages in 2012, Icinga 2 was built with autoconf/automake
|
||||||
and separate Windows project files. We've found this very fragile, and have changed
|
and separate Windows project files. We've found this very fragile, and have changed
|
||||||
|
@ -704,7 +683,7 @@ The most common benefits:
|
||||||
* Separate binary build directories, the actual source tree stays clean.
|
* Separate binary build directories, the actual source tree stays clean.
|
||||||
* CMake automatically generates a Visual Studio project file `icinga2.sln` on Windows.
|
* CMake automatically generates a Visual Studio project file `icinga2.sln` on Windows.
|
||||||
|
|
||||||
### Builds: Unity Builds <a id="development-develop-builds-unity-builds"></a>
|
#### Unity Builds <a id="development-develop-builds-unity-builds"></a>
|
||||||
|
|
||||||
Another thing you should be aware of: Unity builds on and off.
|
Another thing you should be aware of: Unity builds on and off.
|
||||||
|
|
||||||
|
@ -723,6 +702,520 @@ There's a couple of header files which are included everywhere. If you touch/edi
|
||||||
the cache is invalidated and you need to recompile a lot more files then. `base/utility.hpp`
|
the cache is invalidated and you need to recompile a lot more files then. `base/utility.hpp`
|
||||||
and `remote/zone.hpp` are good candidates for this.
|
and `remote/zone.hpp` are good candidates for this.
|
||||||
|
|
||||||
|
### Unit Tests <a id="development-develop-tests"></a>
|
||||||
|
|
||||||
|
New functions and classes must implement new unit tests. Whenever
|
||||||
|
you decide to add new functions, ensure that you don't need a complex
|
||||||
|
mock or runtime attributes in order to test them. Better isolate
|
||||||
|
code into function interfaces which can be invoked in the Boost tests
|
||||||
|
framework.
|
||||||
|
|
||||||
|
Look into the existing tests in the [test/](https://github.com/Icinga/icinga2/tree/master/test) directory
|
||||||
|
and adopt new test cases.
|
||||||
|
|
||||||
|
Specific tests require special time windows, they are only
|
||||||
|
enabled in debug builds for developers. This is the case e.g.
|
||||||
|
for testing the flapping algorithm with expected state change
|
||||||
|
detection at a specific point from now.
|
||||||
|
|
||||||
|
|
||||||
|
### Style Guide <a id="development-develop-styleguide"></a>
|
||||||
|
|
||||||
|
Overview of project files:
|
||||||
|
|
||||||
|
File Type | File Name/Extension | Description
|
||||||
|
---------------|---------------------|-----------------------------
|
||||||
|
Header | .hpp | Classes, enums, typedefs inside the icinga Namespace.
|
||||||
|
Source | .cpp | Method implementation for class functions, static/global variables.
|
||||||
|
CMake | CMakeLists.txt | Build configuration, source and header file references.
|
||||||
|
CMake Source | .cmake | Source/Header files generated from CMake placeholders.
|
||||||
|
ITL/conf.d | .conf | Template library and example files as configuration
|
||||||
|
Class Compiler | .ti | Object classes in our own language, generates source code as `<filename>-ti.{c,h}pp`.
|
||||||
|
Lexer/Parser | .ll, .yy | Flex/Bison code generated into source code from CMake builds.
|
||||||
|
Docs | .md | Markdown docs and READMEs.
|
||||||
|
|
||||||
|
Anything else are additional tools and scripts for developers and build systems.
|
||||||
|
|
||||||
|
All files must include the copyright header. We don't use the
|
||||||
|
current year as this implies yearly updates we don't want.
|
||||||
|
|
||||||
|
Depending on the file type, this must be a comment.
|
||||||
|
|
||||||
|
```
|
||||||
|
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||||
|
|
||||||
|
# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Code Formatting <a id="development-develop-code-formatting"></a>
|
||||||
|
|
||||||
|
We follow the clang format, with some exceptions.
|
||||||
|
|
||||||
|
- Curly braces for functions and classes always start at a new line.
|
||||||
|
|
||||||
|
```
|
||||||
|
String ConfigObjectUtility::EscapeName(const String& name)
|
||||||
|
{
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const String& fullName,
|
||||||
|
bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs)
|
||||||
|
{
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Too long lines break at a parameter, the new line needs a tab indent.
|
||||||
|
|
||||||
|
```
|
||||||
|
static String CreateObjectConfig(const Type::Ptr& type, const String& fullName,
|
||||||
|
bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Conditions require curly braces if it is not a single if with just one line.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
if (s == "OK") {
|
||||||
|
//...
|
||||||
|
} else {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
```
|
||||||
|
|
||||||
|
- There's a space between `if` and the opening brace `(`. Also after the closing brace `)` and opening curly brace `{`.
|
||||||
|
- Negation with `!` doesn't need an extra space.
|
||||||
|
- Else branches always start in the same line after the closing curly brace.
|
||||||
|
|
||||||
|
|
||||||
|
#### Code Comments <a id="development-develop-code-comments"></a>
|
||||||
|
|
||||||
|
Add comments wherever you think that another developer will have a hard
|
||||||
|
time to understand the complex algorithm. Or you might have forgotten
|
||||||
|
it in a year and struggle again. Also use comments to highlight specific
|
||||||
|
stages in a function. Generally speaking, make things easier for the
|
||||||
|
team and external contributors.
|
||||||
|
|
||||||
|
Comments can also be used to mark additional references and TODOs.
|
||||||
|
If there is a specific GitHub issue or discussion going on,
|
||||||
|
use that information as a summary and link over to it on purpose.
|
||||||
|
|
||||||
|
- Single line comments may use `//` or `/* ... */`
|
||||||
|
- Multi line comments must use this format:
|
||||||
|
|
||||||
|
```
|
||||||
|
/* Ensure to check for XY
|
||||||
|
* This relies on the fact that ABC has been set before.
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Function Docs <a id="development-develop-function-docs"></a>
|
||||||
|
|
||||||
|
Function header documentation must be added. The current code basis
|
||||||
|
needs rework, future functions must provide this.
|
||||||
|
|
||||||
|
Editors like CLion or Visual Studio allow you to type `/**` followed
|
||||||
|
by Enter and generate the skeleton from the implemented function.
|
||||||
|
|
||||||
|
Add a short summary in the first line about the function's purpose.
|
||||||
|
Edit the param section with short description on their intention.
|
||||||
|
The `return` value should describe the value type and additional details.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
/**
|
||||||
|
* Reads a message from the connected peer.
|
||||||
|
*
|
||||||
|
* @param stream ASIO TLS Stream
|
||||||
|
* @param yc Yield Context for ASIO
|
||||||
|
* @param maxMessageLength maximum size of bytes read.
|
||||||
|
*
|
||||||
|
* @return A JSON string
|
||||||
|
*/
|
||||||
|
String JsonRpc::ReadMessage(const std::shared_ptr<AsioTlsStream>& stream, boost::asio::yield_context yc, ssize_t maxMessageLength)
|
||||||
|
```
|
||||||
|
|
||||||
|
While we can generate code docs from it, the main idea behind it is
|
||||||
|
to provide on-point docs to fully understand all parameters and the
|
||||||
|
function's purpose in the same spot.
|
||||||
|
|
||||||
|
|
||||||
|
#### Header <a id="development-develop-styleguide-header"></a>
|
||||||
|
|
||||||
|
Only include other headers which are mandatory for the header definitions.
|
||||||
|
If the source file requires additional headers, add them there to avoid
|
||||||
|
include loops.
|
||||||
|
|
||||||
|
The included header order is important.
|
||||||
|
|
||||||
|
- First, include the library header `i2-<libraryname>.hpp`, e.g. `i2-base.hpp`.
|
||||||
|
- Second, include all headers from Icinga itself, e.g. `remote/apilistener.hpp`. `base` before `icinga` before `remote`, etc.
|
||||||
|
- Third, include third-party and external library headers, e.g. openssl and boost.
|
||||||
|
- Fourth, include STL headers.
|
||||||
|
|
||||||
|
#### Source <a id="development-develop-styleguide-source"></a>
|
||||||
|
|
||||||
|
The included header order is important.
|
||||||
|
|
||||||
|
- First, include the header whose methods are implemented.
|
||||||
|
- Second, include all headers from Icinga itself, e.g. `remote/apilistener.hpp`. `base` before `icinga` before `remote`, etc.
|
||||||
|
- Third, include third-party and external library headers, e.g. openssl and boost.
|
||||||
|
- Fourth, include STL headers.
|
||||||
|
|
||||||
|
Always use an empty line after the header include parts.
|
||||||
|
|
||||||
|
#### Namespace <a id="development-develop-styleguide-namespace"></a>
|
||||||
|
|
||||||
|
The icinga namespace is used globally, as otherwise we would need to write `icinga::Utility::FormatDateTime()`.
|
||||||
|
|
||||||
|
```
|
||||||
|
using namespace icinga;
|
||||||
|
```
|
||||||
|
|
||||||
|
Other namespaces must be declared in the scope they are used. Typically
|
||||||
|
this is inside the function where `boost::asio` and variants would
|
||||||
|
complicate the code.
|
||||||
|
|
||||||
|
```
|
||||||
|
namespace ssl = boost::asio::ssl;
|
||||||
|
|
||||||
|
auto context (std::make_shared<ssl::context>(ssl::context::sslv23));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Functions <a id="development-develop-styleguide-functions"></a>
|
||||||
|
|
||||||
|
Ensure to pass values and pointers as const reference. By default, all
|
||||||
|
values will be copied into the function scope, and we want to avoid this
|
||||||
|
wherever possible.
|
||||||
|
|
||||||
|
```
|
||||||
|
std::vector<EventQueue::Ptr> EventQueue::GetQueuesForType(const String& type)
|
||||||
|
```
|
||||||
|
|
||||||
|
C++ only allows to return a single value. This can be abstracted with
|
||||||
|
returning a specific class object, or with using a map/set. Array and
|
||||||
|
Dictionary objects increase the memory footprint, use them only where needed.
|
||||||
|
|
||||||
|
A common use case for Icinga value types is where a function can return
|
||||||
|
different values - an object, an array, a boolean, etc. This happens in the
|
||||||
|
inner parts of the config compiler expressions, or config validation.
|
||||||
|
|
||||||
|
The function caller is responsible to determine the correct value type
|
||||||
|
and handle possible errors.
|
||||||
|
|
||||||
|
Specific algorithms may require to populate a list, which can be passed
|
||||||
|
by reference to the function. The inner function can then append values.
|
||||||
|
Do not use a global shared resource here, unless this is locked by the caller.
|
||||||
|
|
||||||
|
|
||||||
|
#### Conditions and Cases <a id="development-develop-styleguide-conditions"></a>
|
||||||
|
|
||||||
|
Prefer if-else-if-else branches. When integers are involved,
|
||||||
|
switch-case statements increase readability. Don't forget about `break` though!
|
||||||
|
|
||||||
|
Avoid using ternary operators where possible. Putting a condition
|
||||||
|
after an assignment complicates reading the source. The compiler
|
||||||
|
optimizes this anyways.
|
||||||
|
|
||||||
|
Wrong:
|
||||||
|
|
||||||
|
```
|
||||||
|
int res = s == "OK" ? 0 : s == "WARNING" ? 1;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
```
|
||||||
|
|
||||||
|
Better:
|
||||||
|
|
||||||
|
```
|
||||||
|
int res = 3;
|
||||||
|
|
||||||
|
if (s == "OK") {
|
||||||
|
res = 0;
|
||||||
|
} else if (s == "WARNING") {
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Even better: Create a lookup map instead of if branches. The complexity
|
||||||
|
is reduced to O(log(n)).
|
||||||
|
|
||||||
|
```
|
||||||
|
std::map<String, unsigned int> stateMap = {
|
||||||
|
{ "OK", 1 },
|
||||||
|
{ "WARNING", 2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = stateMap.find(s);
|
||||||
|
|
||||||
|
if (it == stateMap.end()) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return it.second;
|
||||||
|
```
|
||||||
|
|
||||||
|
The code is not as short as with a ternary operator, but one can re-use
|
||||||
|
this design pattern for other generic definitions with e.g. moving the
|
||||||
|
lookup into a utility class.
|
||||||
|
|
||||||
|
Once a unit test is written, everything works as expected in the future.
|
||||||
|
|
||||||
|
#### Locks and Guards <a id="development-develop-locks-guards"></a>
|
||||||
|
|
||||||
|
Lock access to resources where multiple threads can read and write.
|
||||||
|
Icinga objects can be locked with the `ObjectLock` class.
|
||||||
|
|
||||||
|
Object locks and guards must be limited to the scope where they are needed. Otherwise we could create dead locks.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
ObjectLock olock(frame.Locals);
|
||||||
|
for (const Dictionary::Pair& kv : frame.Locals) {
|
||||||
|
AddSuggestion(matches, word, kv.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Objects and Pointers <a id="development-develop-objects-pointers"></a>
|
||||||
|
|
||||||
|
Use shared pointers for objects. Icinga objects implement the `Ptr`
|
||||||
|
typedef returning an `intrusive_ptr` for the class object (object.hpp).
|
||||||
|
This also ensures reference counting for the object's lifetime.
|
||||||
|
|
||||||
|
Use raw pointers with care!
|
||||||
|
|
||||||
|
Some methods and classes require specific shared pointers, especially
|
||||||
|
when interacting with the Boost library.
|
||||||
|
|
||||||
|
#### Value Types <a id="development-develop-styleguide-value-types"></a>
|
||||||
|
|
||||||
|
Icinga has its own value types. These provide methods to allow
|
||||||
|
generic serialization into JSON for example, and other type methods
|
||||||
|
which are made available in the DSL too.
|
||||||
|
|
||||||
|
- Always use `String` instead of `std::string`. If you need a C-string, use the `CStr()` method.
|
||||||
|
- Avoid casts and rather use the `Convert` class methods.
|
||||||
|
|
||||||
|
```
|
||||||
|
double s = static_cast<double>(v); //Wrong
|
||||||
|
|
||||||
|
double s = Convert::ToDouble(v); //Correct, ToDouble also provides overloads with different value types
|
||||||
|
```
|
||||||
|
|
||||||
|
- Prefer STL containers for internal non-user interfaces. Icinga value types add a small overhead which may decrease performance if e.g. the function is called 100k times.
|
||||||
|
- `Array::FromVector` and variants implement conversions, use them.
|
||||||
|
|
||||||
|
#### Utilities <a id="development-develop-styleguide-utilities"></a>
|
||||||
|
|
||||||
|
Don't re-invent the wheel. The `Utility` class provides
|
||||||
|
many helper functions which allow you e.g. to format unix timestamps,
|
||||||
|
search in filesystem paths.
|
||||||
|
|
||||||
|
Also inspect the Icinga objects, they also provide helper functions
|
||||||
|
for formatting, splitting strings, joining arrays into strings, etc.
|
||||||
|
|
||||||
|
#### Libraries <a id="development-develop-styleguide-libraries"></a>
|
||||||
|
|
||||||
|
2.11 depends on [Boost 1.66](https://www.boost.org/doc/libs/1_66_0/).
|
||||||
|
Use the existing libraries and header-only includes
|
||||||
|
for this specific version.
|
||||||
|
|
||||||
|
Note: Prefer C++11 features where possible, e.g. std::atomic and lambda functions.
|
||||||
|
|
||||||
|
General:
|
||||||
|
|
||||||
|
- [exception](https://www.boost.org/doc/libs/1_66_0/libs/exception/doc/boost-exception.html) (header only)
|
||||||
|
- [algorithm](https://www.boost.org/doc/libs/1_66_0/libs/algorithm/doc/html/index.html) (header only)
|
||||||
|
- [lexical_cast](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_lexical_cast.html) (header only)
|
||||||
|
- [regex](https://www.boost.org/doc/libs/1_66_0/libs/regex/doc/html/index.html)
|
||||||
|
- [uuid](https://www.boost.org/doc/libs/1_66_0/libs/uuid/doc/uuid.html) (header only)
|
||||||
|
- [range](https://www.boost.org/doc/libs/1_66_0/libs/range/doc/html/index.html) (header only)
|
||||||
|
- [variant](https://www.boost.org/doc/libs/1_66_0/doc/html/variant.html) (header only)
|
||||||
|
- [multi_index](https://www.boost.org/doc/libs/1_66_0/libs/multi_index/doc/index.html) (header only)
|
||||||
|
- [function_types](https://www.boost.org/doc/libs/1_66_0/libs/function_types/doc/html/index.html) (header only)
|
||||||
|
- [circular_buffer](https://www.boost.org/doc/libs/1_66_0/doc/html/circular_buffer.html) (header only)
|
||||||
|
- [math](https://www.boost.org/doc/libs/1_66_0/libs/math/doc/html/index.html) (header only)
|
||||||
|
|
||||||
|
Events and Runtime:
|
||||||
|
|
||||||
|
- [system](https://www.boost.org/doc/libs/1_66_0/libs/system/doc/index.html)
|
||||||
|
- [thread](https://www.boost.org/doc/libs/1_66_0/doc/html/thread.html)
|
||||||
|
- [signals2](https://www.boost.org/doc/libs/1_66_0/doc/html/signals2.html) (header only)
|
||||||
|
- [program_options](https://www.boost.org/doc/libs/1_66_0/doc/html/program_options.html)
|
||||||
|
- [date_time](https://www.boost.org/doc/libs/1_66_0/doc/html/date_time.html)
|
||||||
|
- [filesystem](https://www.boost.org/doc/libs/1_66_0/libs/filesystem/doc/index.htm)
|
||||||
|
|
||||||
|
Network I/O:
|
||||||
|
|
||||||
|
- [asio](https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio.html) (header only)
|
||||||
|
- [beast](https://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/index.html) (header only)
|
||||||
|
- [coroutine](https://www.boost.org/doc/libs/1_66_0/libs/coroutine/doc/html/index.html)
|
||||||
|
- [context](https://www.boost.org/doc/libs/1_66_0/libs/context/doc/html/index.html)
|
||||||
|
|
||||||
|
Consider abstracting their usage into `*utility.{c,h}pp` files with
|
||||||
|
wrapping existing Icinga types. That also allows later changes without
|
||||||
|
rewriting large code parts.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
>
|
||||||
|
> A new Boost library should be explained in a PR and discussed with the team.
|
||||||
|
>
|
||||||
|
> This requires package dependency changes.
|
||||||
|
|
||||||
|
If you consider an external library or code to be included with Icinga, the following
|
||||||
|
requirements must be fulfilled:
|
||||||
|
|
||||||
|
- License is compatible with GPLv2+. Boost license, MIT works, Apache is not.
|
||||||
|
- C++11 is supported, C++14 or later doesn't work
|
||||||
|
- Header only implementations are preferred, external libraries require packages on every distribution.
|
||||||
|
- No additional frameworks, Boost is the only allowed.
|
||||||
|
- The code is proven to be robust and the GitHub repository is alive, or has 1k+ stars. Good libraries also provide a user list, if e.g. Ceph is using it, this is a good candidate.
|
||||||
|
|
||||||
|
|
||||||
|
#### Log <a id="development-develop-styleguide-log"></a>
|
||||||
|
|
||||||
|
Icinga allows the user to configure logging backends, e.g. syslog or file.
|
||||||
|
|
||||||
|
Any log message inside the code must use the `Log()` function.
|
||||||
|
|
||||||
|
- The first parameter is the severity level, use them with care.
|
||||||
|
- The second parameter defines the location/scope where the log
|
||||||
|
happened. Typically we use the class name here, to better analyse
|
||||||
|
the logs the user provide in GitHub issues and on the community
|
||||||
|
channels.
|
||||||
|
- The third parameter takes a log message string
|
||||||
|
|
||||||
|
If the message string needs to be computed from existing values,
|
||||||
|
everything must be converted to the String type beforehand.
|
||||||
|
This conversion for every value is very expensive which is why
|
||||||
|
we try to avoid it.
|
||||||
|
|
||||||
|
Instead, use Log() with the shift operator where everything is written
|
||||||
|
on the stream and conversions are explicitly done with templates
|
||||||
|
in the background.
|
||||||
|
|
||||||
|
The trick here is that the Log object is destroyed immediately
|
||||||
|
after being constructed once. The destructor actually
|
||||||
|
evaluates the values and sends it to registers loggers.
|
||||||
|
|
||||||
|
Since flushing the stream every time a log entry occurs is
|
||||||
|
very expensive, a timer takes care of flushing the stream
|
||||||
|
every second.
|
||||||
|
|
||||||
|
> **Tip**
|
||||||
|
>
|
||||||
|
> If logging stopped, the flush timer thread may be dead.
|
||||||
|
> Inspect that with gdb/lldb.
|
||||||
|
|
||||||
|
Avoid log messages which could irritate the user. During
|
||||||
|
implementation, developers can change log levels to better
|
||||||
|
see what's going one, but remember to change this back to `debug`
|
||||||
|
or remove it entirely.
|
||||||
|
|
||||||
|
|
||||||
|
#### Goto <a id="development-develop-styleguide-goto"></a>
|
||||||
|
|
||||||
|
Avoid using `goto` statements. There are rare occasions where
|
||||||
|
they are allowed:
|
||||||
|
|
||||||
|
- The code would become overly complicated within nested loops and conditions.
|
||||||
|
- Event processing and C interfaces.
|
||||||
|
- Question/Answer loops within interactive CLI commands.
|
||||||
|
|
||||||
|
#### Typedef and Auto Keywords <a id="development-develop-styleguide-typedef-auto"></a>
|
||||||
|
|
||||||
|
Typedefs allow developers to use shorter names for specific types,
|
||||||
|
classes and structs.
|
||||||
|
|
||||||
|
```
|
||||||
|
typedef std::map<String, std::shared_ptr<NamespaceValue> >::iterator Iterator;
|
||||||
|
```
|
||||||
|
|
||||||
|
These typedefs should be part of the Class definition in the header,
|
||||||
|
or may be defined in the source scope where they are needed.
|
||||||
|
|
||||||
|
Avoid declaring global typedefs, unless necessary.
|
||||||
|
|
||||||
|
Using the `auto` keyword allows to ignore a specific value type.
|
||||||
|
This comes in handy with maps/sets where no specific access
|
||||||
|
is required.
|
||||||
|
|
||||||
|
The following example iterates over a map returned from `GetTypes()`.
|
||||||
|
|
||||||
|
```
|
||||||
|
for (const auto& kv : GetTypes()) {
|
||||||
|
result.insert(kv.second);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The long example would require us to define a map iterator, and a slightly
|
||||||
|
different algorithm.
|
||||||
|
|
||||||
|
```
|
||||||
|
typedef std::map<String, DbType::Ptr> TypeMap;
|
||||||
|
typedef std::map<String, DbType::Ptr>::const_iterator TypeMapIterator;
|
||||||
|
|
||||||
|
TypeMap types = GetTypes();
|
||||||
|
|
||||||
|
for (TypeMapIterator it = types.begin(); it != types.end(); it++) {
|
||||||
|
result.insert(it.second);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We could also use a pair here, but requiring to know
|
||||||
|
the specific types of the map keys and values.
|
||||||
|
|
||||||
|
```
|
||||||
|
typedef std::pair<String, DbType::Ptr> kv_pair;
|
||||||
|
|
||||||
|
for (const kv_pair& kv : GetTypes()) {
|
||||||
|
result.insert(kv.second);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After all, `auto` shortens the code and one does not always need to know
|
||||||
|
about the specific types. Function documentation for `GetTypes()` is
|
||||||
|
required though.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Whitespace Cleanup <a id="development-develop-choose-editor-whitespaces"></a>
|
||||||
|
|
||||||
|
Patches must be cleaned up and follow the indent style (tabs instead of spaces).
|
||||||
|
You should also remove any training whitespaces.
|
||||||
|
|
||||||
|
`git diff` allows to highlight such.
|
||||||
|
|
||||||
|
```
|
||||||
|
vim $HOME/.gitconfig
|
||||||
|
|
||||||
|
[color "diff"]
|
||||||
|
whitespace = red reverse
|
||||||
|
[core]
|
||||||
|
whitespace=fix,-indent-with-non-tab,trailing-space,cr-at-eol
|
||||||
|
```
|
||||||
|
|
||||||
|
`vim` also can match these and visually alert you to remove them.
|
||||||
|
|
||||||
|
```
|
||||||
|
vim $HOME/.vimrc
|
||||||
|
|
||||||
|
highlight ExtraWhitespace ctermbg=red guibg=red
|
||||||
|
match ExtraWhitespace /\s\+$/
|
||||||
|
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
|
||||||
|
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
|
||||||
|
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
|
||||||
|
autocmd BufWinLeave * call clearmatches()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Development Environment <a id="development-environment"></a>
|
||||||
|
|
||||||
### Linux Dev Environment <a id="development-linux-dev-env"></a>
|
### Linux Dev Environment <a id="development-linux-dev-env"></a>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue