What happens when gitlab-ctl reconfigure is run?
The Linux package uses Cinc under the hood, which is a free-as-in-beer distribution of the open source software of Chef Software Inc.
In very basic terms, a Cinc client run
happens when gitlab-ctl reconfigure is run. This document elaborates
the process and details the flow of control during a gitlab-ctl reconfigure
run.
gitlab-ctl reconfigure is defined in the
omnibus-ctl project
and as mentioned above, it performs a
cinc-client run
under the hood in the local mode (using the -z flag). This invocation takes
two files as inputs:
- A configuration file named solo.rb.
- An attribute file named dna.json, which is created during build time and loads:
Cinc then follows its two-pass model of execution for the selected cookbook.
In the load phase, the main cookbook and its dependency cookbooks (mentioned in
the metadata.rb file) are loaded. The attributes mentioned in the default
attribute files of these cookbooks are loaded (thus populating the node object
with the default values specified in those attribute files) and the custom
resources are all made available for use. Control then moves to the
execution phase. In the dna.json file, we specify the cookbook name as the
run_list, which makes Cinc use the default recipe as the only entry in the run
list.
The gitlab-ee cookbook extends the gitlab cookbook with EE-only
features. For explanation purposes, let’s first look at the
gitlab cookbook’s default recipe.
The default recipe
The functionality in the default recipe can be summarized as below:
- Call the configrecipe to load the attributes fromgitlab.rband fully populate thenodeobject.
- Check for any deprecations and exit early.
- Check for any problematic settings in the run environment. For example,
if LD_LIBRARY_PATHis defined it can interfere with the included libraries, how software links against them, and raise warnings.
- Check for non-UTF-8 locales and raise warnings.
- Create and configure necessary base directories like /etc/gitlab(unless explicitly disabled),/var/opt/gitlab, and/var/log/gitlab.
- Call other necessary helper recipes and enable or disable recipes for different services, etc.
Note that this summary is not complete. Check out the default recipe code to learn more.
The config recipe
The config recipe populates the node object with the
final values for various settings after merging static default values,
computed default values, and user values specified
in /etc/gitlab/gitlab.rb file.
In the above statement, we mention two types of default values:
- Static: Static default values are specified in various attribute files in different cookbooks and are independently set.
- Computed: Computed default values are used in scenarios where the default value for a setting depends on either the static default value or user-specified value of another setting.
For example, gitlab_rails['gitlab_port'] defaults to the static
value 80. It translates to production.gitlab.port in the rendered
gitlab.yml file that configures the GitLab Rails listener port. The
gitlab_rails['pages_host'] and gitlab_rails['pages-port'] values,
which inform GitLab Rails about GitLab Pages, depend on the user
specified value in from pages_external_url. The computation of these
default values may only happen after gitlab.rb gets parsed.
What goes in to the gitlab.rb file?
The Linux package uses a module named Gitlab to store the settings specified in
gitlab.rb. This module, which extends Mixlib::Config module, can work as a
configuration hash. In the
definition of this module,
we register the various roles, attribute blocks, and top-level attributes that
can be specified in gitlab.rb. The code for this registration is specified in
the SettingsDSL module, and is extended by GitLab module.
When Gitlab.from_file is called in the config recipe, the settings from
gitlab.rb are parsed and loaded to the Gitlab object, and are accessible via
Gitlab['<setting_name>'].
Once gitlab.rb has been parsed and its values are available in Gitlab
object, we can compute the default values of settings dependent on other settings.
Computation of default values
Each component with attributes that require calculation specify a library file
at registration. One method in this file, parse_variables, validates user-provided
input and, in typical usage, sets default values if the user did not already specify
a value. It also detects bad configuration and raises errors.
As mentioned above, parse_variables sets default values based on static
defaults or user-provided values in related settings. The default values
from these related settings are available in the node object after the load
phase of the Cinc run. This node object, while available in the recipe, is
not available in the libraries. To make the static default values available
in the libraries, we attach the node object to the GitLab object in
the config recipe with the code below:
Gitlab[:node] = nodeWith this, the static default values of attributes can be accessed in the libraries
using Gitlab[:node]['<top-level-key>'][<setting>].
It is important to note that:
- The Gitlabobject stores keys as they are mentioned ingitlab.rb.
- nodestores them based on the defined nesting attribute-block-attribute hierarchy.
So, gitlab_rails settings from
gitlab.rb are available as Gitlab['gitlab_rails'][*] while default values of
those settings from attribute files are available at
Gitlab[:node]['gitlab']['gitlab_rails']. The gitlab_rails key is specified
under the gitlab attribute block, so an extra layer of nesting is present
while accessing it via node.
While the Gitlab object is technically only supposed to hold the settings
specified in gitlab.rb, when computing default values of settings based on
other settings, we usually put them under Gitlab key itself.
Issue #3932 is open to change
this behavior.
Handling of secrets
Many attributes required for GitLab functionality are secrets that need to
persist across reconfigure runs and, in multi-node setups, across different
nodes. Moreover, if the user did not specify values for these secrets then
the Linux package must create them to function. For this purpose, each library file
specifies a parse_secrets method similar to the parse_variables method. This
method generates secrets, unless explicitly disabled, if none have been specified
in the gitlab.rb file.
These secrets are written, unless explicitly disabled, to a file named
/etc/gitlab/gitlab-secrets.json. This file is read by subsequent reconfigure
runs and the secret persists across every reconfigure run.
Update the node object with final attribute list
After all libraries parse their respective attributes and secrets, the
final configuration is ready to merge with default attributes already
present in node. The node.consume_attributes method merges the
final configuration with the default configuration populated in the load
phase. Any configuration read from gitlab.rb or computed in the libraries
overwrite the values for keys matched in the node object. At this point,
the node object contains the final attribute list.
gitlab-cluster.json file
A user configures the system with gitlab.rb to match the requirement. In
certain scenarios, however, we need to perform alterations without changes
to the gitlab.rb file. The Linux package writes to a different file,
/etc/gitlab/gitlab-cluster.json, that overrides user-specified values in
gitlab.rb. The gitlab-ctl command or a reconfigure dynamically populates
this file and it gets read and merged over the node attributes at the end
of the config recipe.
The gitlab-ctl geo promote command, when used on a multi-node PostgreSQL
instance with Patroni, must disable the Patroni standby server. In this
example, the standby server would normally be disabled via
patroni['standby_cluster']['enable'] in gitlab.rb. The gitlab.rb file
should remain read-only for the duration of the Cinc run, so this setting
is changed in the gitlab-cluster.json file. Future reconfigure runs parse
the gitlab-cluster.json file at the end and node['patroni']['standby_cluster']['enable']
will evaluate false.
The Cinc run executes helper and service-specific recipes after the config recipe. Once these are complete, the node object is fully populated and may be used in recipes and resources.
The default recipe in EE cookbook essentially calls gitlab::default recipe,
and then handles the EE-specific components separately.