Skip to content

Megakuul/Cthulhu

Repository files navigation

Cthulhu

Cthulhu Icon

This is just a proof of concept

Cloud based hypervisor system for juju controlled environments.

Development


Coding conventions


Style conventions

  1. Private stuff is written in camelCase
  2. Public stuff is written in PascalCase
  3. Constants & macros are written in SCREAMING_SNAKE_CASE
  4. Namespaces are written in snake_case
  5. Filenames are written in snake_case

Comments

Cthulhu should contain as few comments as possible. If you are unsure about adding a comment, ask yourself if the code is self-explanatory. If not, consider refactoring to make it clearer.

The following situations explicitly require comments:

  • Exported interfaces: All exported types, interfaces, classes, functions, etc., used by other parts of the code (if it has a public BUILD rule) require explicit comments describing HOW to use them.
  • Complex implementation: If a piece of code operates in a complex manner, it should generally be refactored. If refactoring is not possible or would make the code more complex, you must add a comment explaining WHY it functions that way.

Comments are written in godoc for go and doxygen for C++.

Every shared component must contain a README file that concisely describes WHY this component exists.

Following the rules above, when you maintain code, it is essential to check and update the following:

  • Update functionality of an exported interface: Check and update the interface's comment.
  • Breaking change in the functionality of a component: Check and update the README.

Exception system (C++)

C++ components employ exception matching at the level where the exception is logged. Expected exceptions should be in ComponentException format, if they are not, the logged component is set to "UNDEFINED".

  1. Exceptions thrown directly by cthulhu components (exclude independent "util" folder) must be in util::error::ComponentException format.

  2. Internal or external C++ std::exception derivates should be converted to ComponentException at the level of a definable component (e.g. a wave library should NOT return a ComponentException; however, the calling component should immediately convert it to a ComponentException by rethrowing it and adding the component information to it).

Exception system (Go)

Go components employ error matching at the level where the error is logged. Expected errors should be in ComponentError format, if they are not, the logged component is set to "UNDEFINED".

  1. Errors returned directly by cthulhu components (exclude independent "util" folder) must be in error.ComponentError format.

  2. Regular Go errors should be converted to ComponentError at the level of a definable component (e.g. a rune library should NOT return a ComponentError; however, the calling component should immediately convert it to a ComponentError with the provided error.Errorf() function).

Exception messages

Error and exception messages should not be overloaded. However, when low-level errors or errors from external libraries occur, prefix them with a short message describing the error from the perspective of the current component.

Example: The output of getKeyType is very general, so it is enhanced with a short message:

keyType, err := getKeyType(
  metaconfig.GetString("ACME_CERTIFICATE_KEY_TYPE", defOpts.ACME_CERTIFICATE_KEY_TYPE))
if err!=nil {
  return nil, cterror.Errorf("CERT", "Failed to process ACME_CERTIFICATE_KEY_TYPE: ", err.Error())
}

That way the reader gets a hint that the ACME_CERTIFICATE_KEY_TYPE variable is involved here (instead of just getting "Invalid key type" crap).

The goal is to provide enough information in the error message so that the reader can determine the source and direction of the error, and possibly how to resolve it, without needing to enable debug logs etc.

Database naming

All database key-segments are strictly written in UPPERCASE and words are spaced with '_'. Database keys are named based on those three segments: major prefix, minor prefix and suffix.

  • major prefix: Determines key access. Shared keys start with SHARED followed by the component name. This segment is relevant for RBAC roles. (example: SHARED_RUNE_ || RUNE_).
  • minor prefix: Holds key description / use. (example: CERT_REQUEST_DOMAIN_ || CERT_PROVIDER_EMAIL_).
  • suffix: Holds dynamic identifiers for the key. It usually contains attributes like the hostname to identify the node or the service to identify the cthulhu service. The suffix is very variable and is based on the context like the range queries performed on it. (example: SRVCTH01_RUNE || WAVE_SRVCTH02).

Full examples:

SHARED_RUNE_CERT_REQUEST_EXP_SRVCTH01_WAVE # Certificate request expiration date for wave on srvcth01
RUNE_CERT_ACME_EMAIL_SRVCTH01 # Certificate acme email used from rune on srvcth01
RUNE_CERT_ACME_KEY_SRVCTH01 # Certificate acme accesskey used from rune on srvcth01

(This concept may seem confusing at first, but it enables efficient use of etcd features).

Dependency management


All build steps are managed by the bazel build tool. Other tools shall only be used as bazel plugin.

Go dependencies are managed in the go.mod file at the root of the project, this file is processed by gazelle to generate the required rules under the hood.

C++ dependencies are managed in the MODULE.bazel file at the root of the project.

Every component does have its own Bazel BUILD file.

C++ rules must be manually configured, for Go and Proto rules, bazel run //:gazelle can be used to generate the code automatically.

Completions / IntelliSense


GO

For Go development I suggest using the gopls lang-server and import the repository root. Then you should have completion over the whole project.

C++

For C++ development I recommend using clangd for intellisense / documentation. To generate the compile_commands.json file there are various options, I recommend to use this tool:

bazel-compile-commands

bazel-compile-commands releases

(In case std is by default not ++23, you can use this option to replace the std: bazel-compile-commands -R c++14=c++23 -R -fno-canonical-system-headers="")