aboutsummaryrefslogtreecommitdiff
path: root/kolla/common/config.py
blob: e1c0cb4564383c7294a9b3a4811c104ea1d8578c (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import itertools
import os

from oslo_config import cfg
from oslo_config import types

from kolla.common.sources import SOURCES
from kolla.common.users import USERS
from kolla.version import version_info as version


BASE_OS_DISTRO = ['debian', 'rocky', 'ubuntu']
BASE_ARCH = ['x86_64', 'aarch64']
DEBIAN_ARCH = ['amd64', 'arm64']
DEFAULT_BASE_TAGS = {
    'debian': {'name': 'debian', 'tag': 'trixie'},
    'rocky': {'name': 'quay.io/rockylinux/rockylinux', 'tag': '10'},
    'ubuntu': {'name': 'ubuntu', 'tag': '24.04'},
}
# NOTE(hrw): has to match PRETTY_NAME in /etc/os-release
DISTRO_PRETTY_NAME = {
    'debian': 'Debian GNU/Linux 13 (trixie)',
    'rocky': 'Rocky Linux 10.* (Red Quartz)',
    'ubuntu': 'Ubuntu 24.04.* LTS',
}

OPENSTACK_RELEASE = 'master'
OPENSTACK_RELEASE_CODENAME = 'Gazpacho'

# TODO(mandre) check for file integrity instead of downloading from an HTTPS
# source
TARBALLS_BASE = "https://tarballs.opendev.org"

_PROFILE_OPTS = [
    cfg.ListOpt('infra',
                default=[
                    'cron',
                    'etcd',
                    'fluentd',
                    'haproxy',
                    'hacluster',
                    'keepalived',
                    'kolla-toolbox',
                    'letsencrypt',
                    'mariadb',
                    'memcached',
                    'opensearch',
                    'openvswitch',
                    'proxysql',
                    'rabbitmq',
                    'tgtd',
                    'valkey',
                ],
                help='Infra images'),
    cfg.ListOpt('main',
                default=[
                    'ceilometer',
                    'cinder',
                    'glance',
                    'heat',
                    'horizon',
                    'iscsi',
                    'keystone',
                    'neutron',
                    'nova-',
                    'placement',
                ],
                help='Main images'),
    cfg.ListOpt('aux',
                default=[
                    'aodh',
                    'blazar',
                    'cloudkitty',
                    'designate',
                    'gnocchi',
                    'ironic',
                    'kerbside',
                    'magnum',
                    'manila',
                    'masakari',
                    'mistral',
                    'octavia',
                    'tacker',
                    'trove',
                    'valkey',
                ],
                help='Aux Images'),
    cfg.ListOpt('default',
                default=[
                    'cron',
                    'kolla-toolbox',
                    'fluentd',
                    'glance',
                    'haproxy',
                    'heat',
                    'horizon',
                    'keepalived',
                    'keystone',
                    'mariadb',
                    'memcached',
                    'neutron',
                    'nova-',
                    'placement',
                    'proxysql',
                    'openvswitch',
                    'rabbitmq',
                ],
                help='Default images'),
]

hostarch = os.uname()[4]

# NOTE: Apple Silicon reports as arm64 which is aarch64
if hostarch == "arm64":
    hostarch = "aarch64"

if hostarch == 'aarch64':
    debianarch = 'arm64'
elif hostarch == 'x86_64':
    debianarch = 'amd64'

_CLI_OPTS = [
    cfg.StrOpt('base', short='b', default='rocky',
               choices=BASE_OS_DISTRO,
               help='The distro type of the base image.'),
    cfg.StrOpt('base-tag', default='latest',
               help='The base distro image tag'),
    cfg.StrOpt('base-image',
               help='The base image name. Default is the same with base.'),
    cfg.StrOpt('base-arch', default=hostarch,
               choices=BASE_ARCH,
               help='The base architecture. Default is same as host.'),
    cfg.StrOpt('debian-arch', default=debianarch,
               choices=DEBIAN_ARCH,
               help='The base architecture used for downloading external '
               'packages. Default is derived from base-arch.'),
    cfg.BoolOpt('use-dumb-init', default=True,
                help='Use dumb-init as init system in containers'),
    cfg.BoolOpt('debug', short='d', default=False,
                help='Turn on debugging log level'),
    cfg.BoolOpt('skip-parents', default=False,
                help='Do not rebuild parents of matched images'),
    cfg.BoolOpt('skip-existing', default=False,
                help='Do not rebuild images present in the container engine '
                'cache'),
    cfg.DictOpt('build-args',
                help='Set docker build time variables'),
    cfg.BoolOpt('keep', default=False,
                help='Keep failed intermediate containers'),
    cfg.BoolOpt('list-dependencies', short='l',
                help='Show image dependencies (filtering supported)'),
    cfg.BoolOpt('list-images',
                help='Show all available images (filtering supported)'),
    cfg.StrOpt('locals-base', default='./',
               help='Base directory for local source resolution'),
    cfg.StrOpt('namespace', short='n', default='kolla',
               help='The Docker namespace name'),
    cfg.StrOpt('network_mode', default='host',
               help='The network mode for Docker build. Example: host'),
    cfg.BoolOpt('buildkit', default=True,
                help='Use Docker BuildKit (docker buildx build) when building '
                     'images. Requires the docker-buildx-plugin to be '
                     'installed. Only valid with --engine docker.'),
    cfg.StrOpt('buildkit-builder', default=None,
               help='Name of the docker buildx builder instance to use. '
                    'If unset the currently active builder is used. '
                    'Only valid with --buildkit.'),
    cfg.BoolOpt('cache', default=True,
                help='Use the container engine cache when building'),
    cfg.StrOpt('patches-path', default=None,
               help='The path where patch files to be applied are located'),
    cfg.StrOpt('platform', default=None,
               help=('The platform to use for a cross-compile build. Should '
                     'be set in conjunction with "--base-arch" argument. '
                     'Example: "--platform linux/arm64 --base-arch aarch64"')),
    cfg.MultiOpt('profile', types.String(), short='p',
                 help=('Build a pre-defined set of images, see [profiles]'
                       ' section in config. The default profiles are:'
                       ' {}'.format(', '.join(
                           [opt.name for opt in _PROFILE_OPTS])
                       ))),
    cfg.BoolOpt('push', default=False,
                help='Push images after building'),
    cfg.IntOpt('push-threads', default=1, min=1,
               help=('The number of threads to use while pushing images.'
                     ' Note: Docker cannot handle threaded pushing properly')),
    cfg.IntOpt('retries', short='r', default=3, min=0,
               help='The number of times to retry while building'),
    cfg.MultiOpt('regex', types.String(), positional=True, required=False,
                 help=('Build only images matching regex and its'
                       ' dependencies')),
    cfg.StrOpt('registry', help='The container image registry host'),
    cfg.StrOpt('save-dependency',
               help=('Path to the file to store the docker image'
                     ' dependency in Graphviz dot format')),
    cfg.StrOpt('format', short='f', default='json',
               choices=['json', 'none'],
               help='Format to write the final results in.'),
    cfg.StrOpt('summary-json-file',
               help='Name of a file to write the build summary to when format '
                    'is json. If unset, the summary will be written to '
                    'standard output'),
    cfg.StrOpt('tarballs-base', default=TARBALLS_BASE,
               help='Base url to OpenStack tarballs'),
    cfg.IntOpt('threads', short='T', default=8, min=1,
               help=('The number of threads to use while building.'
                     ' (Note: setting to one will allow real time'
                     ' logging)')),
    cfg.StrOpt('tag',
               help='The container image tag'),
    cfg.BoolOpt('template-only', default=False,
                help="Don't build images. Generate Dockerfile only"),
    cfg.IntOpt('timeout', default=120,
               help='Time in seconds after which any operation times out'),
    cfg.MultiOpt('template-override', types.String(),
                 help='Path to template override file'),
    cfg.MultiOpt('docker-dir', types.String(),
                 help=('Path to additional docker file template directory,'
                       ' can be specified multiple times'),
                 short='D'),
    cfg.StrOpt('logs-dir', help='Path to logs directory'),
    cfg.BoolOpt('pull', default=True,
                help='Attempt to pull a newer version of the base image'),
    cfg.StrOpt('work-dir', help=('Path to be used as working directory.'
                                 ' By default, a temporary dir is created')),
    cfg.BoolOpt('squash', default=False,
                help=('Squash the image layers. WARNING: it will consume lots'
                      ' of disk IO. "docker-squash" tool is required, install'
                      ' it by "pip install docker-squash"')),
    cfg.StrOpt('openstack-release', default=OPENSTACK_RELEASE,
               help='OpenStack release for building kolla source images and '
                    'kolla-toolbox image'),
    cfg.StrOpt('openstack-release-codename',
               default=OPENSTACK_RELEASE_CODENAME,
               help='OpenStack release codename for Debian OpenStack extrepo '
                    'and Ubuntu Cloud Archive'),
    cfg.StrOpt('openstack-branch',
               help='Branch for source images (internal; with a dash; '
                    'please set openstack-release instead)'),
    cfg.StrOpt('openstack-branch-slashed',
               help='Branch for source images (internal; with a slash; '
                    'please set openstack-release instead)'),
    cfg.BoolOpt('docker-healthchecks', default=True,
                help='Add Kolla docker healthcheck scripts in the image'),
    cfg.BoolOpt('quiet', short='q', default=False,
                help='Do not print image logs'),
    cfg.BoolOpt('enable-unbuildable', default=False,
                help='Enable images marked as unbuildable'),
    cfg.BoolOpt('summary', default=True,
                help='Show summary at the end of build'),
    cfg.StrOpt('image-name-prefix', default='',
               help='Prefix prepended to image names'),
    cfg.StrOpt('repos-yaml', default='',
               help='Path to repos.yaml override file'),
    cfg.StrOpt('engine', default='docker', choices=['docker', 'podman'],
               help='Container engine to build images on.'),
    cfg.StrOpt('podman_base_url', default='unix:///run/podman/podman.sock',
               help='Path to podman socket.')
]

_BASE_OPTS = [
    cfg.StrOpt('maintainer',
               default='Kolla Project (https://launchpad.net/kolla)',
               help='Content of the maintainer label'),
    cfg.StrOpt('distro_package_manager', default=None,
               help=('Use this parameter to override the default package '
                     'manager used by kolla. For example, if you want to use '
                     'yum on a system with dnf, set this to yum which will '
                     'use yum command in the build process')),
    cfg.StrOpt('base_package_type', default=None,
               help=('Set the package type of the distro. If not set then '
                     'the packaging type is set to "rpm" if a RHEL based '
                     'distro and "deb" if a Debian based distro.')),
    cfg.ListOpt('rpm_setup_config', default=[],
                help=('Comma separated list of .rpm or .repo file(s) '
                      'or URL(s) to install before building containers')),
    cfg.StrOpt('apt_sources_list', help=('Path to custom sources.list')),
    cfg.StrOpt('apt_preferences', help=('Path to custom apt/preferences')),
    cfg.BoolOpt('squash-cleanup', default=True,
                help='Remove source image from Docker after squashing'),
    cfg.StrOpt('squash-tmp-dir',
               help='Temporary directory to be used during squashing'),
    cfg.BoolOpt('clean_package_cache', default=True,
                help='Clean all package cache.'),
    cfg.ListOpt('allowed-to-fail', default=[],
                help='Images which are allowed to fail'),
    cfg.StrOpt('pip_index_url',
               help='URL of the primary pip index (sets ARG PIP_INDEX_URL)'),
    cfg.StrOpt('pip_trusted_host',
               help='Hostname to mark as trusted for pip '
                    '(sets ARG PIP_TRUSTED_HOST)'),
    cfg.StrOpt('pip_extra_index_url',
               help='URL of an extra pip index '
                    '(sets ARG PIP_EXTRA_INDEX_URL)'),
]


def get_source_opts(type_=None, location=None, reference=None, enabled=True,
                    version=None, sha256=None):
    return [cfg.StrOpt('type',
                       choices=['local', 'git', 'url'],
                       default=type_,
                       help='Source location type'),
            cfg.StrOpt('location',
                       default=location,
                       help='The location for source install'),
            cfg.StrOpt('reference',
                       default=reference,
                       help=('Git reference to pull, commit sha, tag '
                             'or branch name')),
            cfg.BoolOpt('enabled',
                        default=enabled,
                        help=('Whether the source is enabled')),
            cfg.StrOpt('version',
                       default=version,
                       help=('Package version to download for GitHub '
                             'sources')),
            cfg.DictOpt('sha256',
                        default=sha256,
                        help=('Dictionary of sha256 sums for GitHub '
                              'sources')),
            ]


def get_user_opts(uid, gid, group):
    return [
        cfg.IntOpt('uid', default=uid, help='The user id'),
        cfg.IntOpt('gid', default=gid, help='The group id'),
        cfg.StrOpt('group', default=group, help='The group name'),
    ]


def gen_all_user_opts():
    for name, params in USERS.items():
        uid = params['uid']
        gid = params['gid']
        try:
            group = params['group']
        except KeyError:
            group = name[:-5]
        yield name, get_user_opts(uid, gid, group)


def gen_all_source_opts():
    for name, params in SOURCES.items():
        type_ = params['type']
        location = params['location']
        reference = params.get('reference')
        enabled = params.get('enabled', True)
        version = params.get('version')
        sha256 = params.get('sha256')
        yield name, get_source_opts(type_, location, reference, enabled,
                                    version, sha256)


def list_opts():
    return itertools.chain([(None, _CLI_OPTS),
                            (None, _BASE_OPTS),
                            ('profiles', _PROFILE_OPTS)],
                           gen_all_source_opts(),
                           gen_all_user_opts(),
                           )


def parse(conf, args, usage=None, prog=None,
          default_config_files=None):
    conf.register_cli_opts(_CLI_OPTS)
    conf.register_opts(_BASE_OPTS)
    conf.register_opts(_PROFILE_OPTS, group='profiles')
    for name, opts in gen_all_source_opts():
        conf.register_opts(opts, name)
    for name, opts in gen_all_user_opts():
        conf.register_opts(opts, name)

    conf(args=args,
         project='kolla',
         usage=usage,
         prog=prog,
         version=version.cached_version_string(),
         default_config_files=default_config_files)

    # NOTE(jeffrey4l): set the default base tag based on the
    # base option
    conf.set_default('base_tag', DEFAULT_BASE_TAGS[conf.base]['tag'])
    conf.set_default('tag',
                     f"{conf.openstack_release}-{conf.base}-{conf.base_tag}")
    prefix = '' if conf.openstack_release == 'master' else 'stable-'
    openstack_branch = '{}{}'.format(prefix, conf.openstack_release)
    openstack_branch_slashed = openstack_branch.replace('-', '/')
    conf.set_default('openstack_branch', openstack_branch)
    conf.set_default('openstack_branch_slashed', openstack_branch_slashed)
    # NOTE(bbezak) Derive debian_arch from base_arch if not set explicitly
    derived_arch = 'arm64' if conf.base_arch == 'aarch64' else 'amd64'
    conf.set_default('debian_arch', derived_arch)

    if not conf.base_image:
        conf.base_image = DEFAULT_BASE_TAGS[conf.base]['name']