A REST API to manage operator index container images (and some bundle images).
IIB is a recursive acronym. IIB stands for IIB Image Builder.
Note: IIB was originally called "Index Image Build Service" but the name has been changed since its scope widened.
Podman 1.9.2+ is required by IIB.
The codebase conforms to the style enforced by flake8
with the following exceptions:
- The maximum line length allowed is 100 characters instead of 80 characters
In addition to flake8
, docstrings are also enforced by the plugin flake8-docstrings
with
the following exemptions:
- D100: Missing docstring in public module
- D104: Missing docstring in public package
- D105: Missing docstring in magic method
The format of the docstrings should be in the reStructuredText style such as:
"""
Get the IIB build request from the REST API.
:param int request_id: the ID of the IIB request
:return: the request
:rtype: dict
:raises IIBError: if the HTTP request fails
"""
Additionally, black
is used to enforce other coding standards with the following exceptions:
- Single quotes are used instead of double quotes
To verify that your code meets these standards, you may run tox -e black,flake8
.
The testing environment is managed by tox. Simply run
tox
and all the linting and unit tests will run.
If you'd like to run a specific unit test, you can do the following:
tox -e py38 tests/test_web/test_api_v1.py::test_add_bundle_invalid_param
docker-compose is the supported mechanism for setting up a development environment. This will automatically run the following containers:
- iib-api - the IIB REST API. This is accessible at http://localhost:8080.
- iib-worker - the IIB Celery worker.
- rabbitmq - the RabbitMQ instance for communicating between the API and the worker. The
management UI is accessible at http://localhost:8081. The username is
iib
and the password isiib
. - db - the Postgresql database used by the IIB REST API.
- registry - the Docker Registry where the worker pushes its build index images to. This is accessible at docker://localhost:8443.
- message-broker - the Apache ActiveMQ instance for publishing messages for external consumers.
The web console is accessible at http://localhost:8161/admin. The
username is
admin
and the password isadmin
. The docker-compose environment is configured for IIB to publish AMQP 1.0 messages to the Apache ActiveMQ instance at the destinationstopic://VirtualTopic.eng.iib.batch.state
andtopic://VirtualTopic.eng.iib.build.state
.
It's recommended to use the wrapper targets in the Makefile
for pre-requisites. Simply run make
to view available targets.
The Flask application will automatically reload if there is a change in the codebase. If invalid
syntax is added in the code, the iib-api
container may shutdown. The Celery worker will
automatically restart if there is a change under the iib/workers
directory.
To run a built index image from the development registry, you can perform the following:
podman login --tls-verify=false -u iib -p iibpassword localhost:8443
podman pull --tls-verify=false localhost:8443/iib-build:1
If you are using Docker (a modern version is required), you can perform the following:
sudo docker login -u iib -p iibpassword localhost:8443
sudo docker run localhost:8443/iib-build:1
If your development environment requires accessing a private container registry, please read the section titled Registry Authentication.
You may also run the development environment with
podman-compose. Use the script from the devel
branch as it has various fixes and new features required to run IIB. Set the environment variable
IIB_COMPOSE_ENGINE
to the path of the podman-compose
script before running the make
commands.
Setting the IIB_COMPOSE_ENGINE
variable will update compose targets to point to a similarly named
file inside the compose-files
directory. Any changes made to the compose files should be submitted to
all files in the directory.
To manage dependencies, this project uses pip-tools so that the production dependencies are pinned and the hashes of the dependencies are verified during installation.
The unpinned dependencies are recorded in setup.py
, and to generate the requirements.txt
file,
run pip-compile --generate-hashes --output-file=requirements.txt
. This is only necessary when
adding a new package. To upgrade a package, use the -P
argument of the pip-compile
command.
To update requirements-test.txt
, run
pip-compile --generate-hashes requirements-test.in -o requirements-test.txt
.
Both requirements.txt
and requirements-test.txt
can be updated using the tox command
$ tox -e pip-compile
You can also use this to upgrade specific packages and specific package versions
$ tox -e pip-compile -- --upgrade-package <package-name><==package-version>
When installing the dependencies in a production environment, run
pip install --require-hashes -r requirements.txt
. Alternatively, you may use
pip-sync requirements.txt
, which will make sure your virtualenv only has the packages listed in
requirements.txt
.
To ensure the pinned dependencies are not vulnerable, this project uses safety, which runs on every pull-request.
When the models are modified, the database schema also needs to be updated which includes leveraging
alembic
to generate a database migration. In order to simplify this process, a tox
command can be
used
$ tox -e migrate-db <simple message describing the change.>
Before running this command, however, you should SQLALCHEMY_DATABASE_URI
to point to 'sqlite:///'
in iib/web/config.py
. This parameter also needs to be added to the Config
class specification.
NOTE: This tox command was written as a convenience function that might not work in all situations.
If alembic cannot auto-create the version file for the migration with migrate
, you can just create it
and then manually populate the steps with
$ flask db revision -m <simple message describing the change.>
IIB does not handle authentication with container registries directly. If authentication is needed,
configure the ~/.docker/config.json.template
file for the user running the IIB worker. This path
can be customized with the iib_docker_config_template
configuration.
During development, you may choose to add a volume entry of
- /root/.docker/config.json:/root/.docker/config.json.template:ro,z
on the workers in
docker-compose.yml
so that your host's root user's Docker configuration with authentication is
used by the workers. This is only needed if you are working with private images.
To configure the IIB REST API, create a Python file at /etc/iib/settings.py
. Any variables set in
this configuration file will be applied to the Celery worker when running in production mode
(default).
The custom configuration options for the REST API are listed below:
IIB_ADDITIONAL_LOGGERS
- a list of Python loggers that should have the same log level that is set forIIB_LOG_LEVEL
. This defaults to[]
.IIB_AWS_S3_BUCKET_NAME
- the name of the AWS S3 bucket used to fetch artifact files like logs and related_bundles from if specified. This defaults toNone
which means IIB will try to get the files locally ifIIB_REQUEST_LOGS_DIR
andIIB_REQUEST_RELATED_BUNDLES_DIR
are configured. For the REST API, ifIIB_AWS_S3_BUCKET_NAME
is specified, you cannot specifyIIB_REQUEST_LOGS_DIR
andIIB_REQUEST_RELATED_BUNDLES_DIR
.IIB_BINARY_IMAGE_CONFIG
- the mapping,dict(<str>: dict(<str>:<str>))
, of distribution scope to another dictionary mapping ocp_version label to a binary image pull specification. This is useful in setting up customized binary image for different index image images thus reducing complexity for the end user. This defaults to{}
.IIB_GREENWAVE_CONFIG
- the mapping,dict(<str>: dict(<str>:<str>))
, of celery task queues to another dictionary of Greenwave query parameters to their values. This is useful in setting up customized gating for each queue. This defaults to{}
. Use the task queue name asNone
to configure Greenwave config for the default Celery task queue.IIB_LOG_FORMAT
- the format of the logs. This defaults to%(asctime)s %(name)s %(levelname)s %(module)s.%(funcName)s %(message)s
.IIB_LOG_LEVEL
- the Python log level of the REST API (Flask). This defaults toINFO
.IIB_MAX_PER_PAGE
- the maximum number of build requests that can be shown on a single page. This defaults to20
.IIB_REQUEST_DATA_DAYS_TO_LIVE
- the amount of days after which per request temmporary data is considered to be expired and may be removed. This defaults to3
.IIB_REQUEST_LOGS_DIR
- the directory to load the request specific log files. IfNone
, per request log files information will not appear in the API response. This defaults toNone
.IIB_REQUEST_RELATED_BUNDLES_DIR
- the directory to load the request specific related bundles files. IfNone
, per request related bundles files information will not appear in the API response. This defaults toNone
.IIB_RECURSIVE_REQUEST_RELATED_BUNDLES_DIR
- the directory to load the recursive-related-bundles request specific recursive related bundles files. This is a required config variable ifIIB_AWS_S3_BUCKET_NAME
is not specified.IIB_USER_TO_QUEUE
- the mapping,dict(<str>: <str>)
, of usernames to celery task queues. This is useful in isolating the workload from certain users. Some celery tasks must execute serially, while others can execute in parallel. Add the prefixSERIAL:
orPARALLEL:
to the username key in this mapping to create queues based on serial vs parallel tasks. The default queue is used for tasks from users not found in the mapping. This defaults to{}
.IIB_WORKER_USERNAMES
- the list of case-sensitve Kerberos principals that are allowed to update build requests using the PATCH API endpoint. This defaults to[]
.LOGIN_DISABLED
- determines if authentication is required. This defaults toFalse
(i.e. authentication is required).SQLALCHEMY_DATABASE_URI
- the database URI of the database the REST API connects to. See the Flask-SQLAlchemy configuration documentation.
The custom configuration options for AMQP 1.0 messaging are listed below:
IIB_MESSAGING_BATCH_STATE_DESTINATION
- the AMQP 1.0 destination to send the batch state change messages. If this is not set, IIB will not send these types of messages. If this is set,IIB_MESSAGING_URLS
must also be set.IIB_MESSAGING_BUILD_STATE_DESTINATION
- the AMQP 1.0 destination to send the build request state change messages. If this is not set, IIB will not send these types of messages. If this is set,IIB_MESSAGING_URLS
must also be set.IIB_MESSAGING_CA
- the path to a file with the certificate authority that signed the certificate of the AMQP 1.0 message broker. This defaults to/etc/pki/tls/certs/ca-bundle.crt
.IIB_MESSAGING_CERT
- the path to the identity certificate used for authentication with the AMQP 1.0 message broker. This defaults to/etc/iib/messaging.crt
.IIB_MESSAGING_DURABLE
- determines if the messages are durable and cannot be lost due to an unexpected termination or restart by the AMQP 1.0 broker. If the broker is not capable of guaranteeing this, it may not accept the message. In that case, set this configuration option toFalse
. This defaults toTrue
.IIB_MESSAGING_KEY
- the path to the private key of the identity certificate used for authentication with the AMQP 1.0 message broker. This defaults to/etc/iib/messaging.key
.IIB_MESSAGING_TIMEOUT
- the number of seconds before a messaging operation times out. Examples of messaging operations include connecting to the broker and sending a message to the broker. In this case, if the timeout is set to30
, then it could take a maximum of 60 seconds before the operation times out. This is because it can take up to 30 seconds to connect to the broker and also up to 30 seconds for the message to be sent. This defaults to30
.IIB_MESSAGING_URLS
- a list of AMQP(S) URLs to use when connecting to the AMQP 1.0 broker. This must be set if messaging is enabled.
If you wish to configure AWS S3 bucket for storing artifact files, the following environment variables
must be set along with IIB_AWS_S3_BUCKET_NAME
config variable:
AWS_ACCESS_KEY_ID
- the secret access key ID used to access the AWS S3 bucket.AWS_SECRET_ACCESS_KEY
- the secret access key used to access the AWS S3 bucket.AWS_DEFAULT_REGION
- the default region of the AWS S3 bucket
More info on these environment variables can be found in the AWS User Guide
To configure an IIB Celery worker, create a Python file at /etc/iib/celery.py
. The location
can be overridden with the IIB_CELERY_CONFIG
environment variable. This is useful if the worker is
running on the same host as another worker or the REST API.
Any variables set in this configuration file will be applied to the Celery worker when running in production mode (default).
The custom configuration options for the Celery workers are listed below:
-
broker_url
- the AMQP(S) URL to connect to RabbitMQ. See the broker_url configuration documentation. -
iib_api_timeout
- the timeout in seconds for HTTP requests to the REST API. This defaults to60
seconds. -
iib_api_url
- the URL to the IIB REST API (e.g.https://iib.domain.local/api/v1/
). -
iib_aws_s3_bucket_name
- the name of the AWS S3 bucket used to store artifact files like logs and related_bundles if specified.iib_request_logs_dir
andiib_request_related_bundles_dir
are required when this variable is specified. This defaults toNone
which means IIB will try to store the files locally ifiib_request_logs_dir
andiib_request_related_bundles_dir
are configured. -
iib_docker_config_template
- the path to the Docker config.json file for IIB to use as a template. IIB will symlink this file to~/.docker/config.json
at the beginning of every request. Additionally, it will use this file as a base and set theoverwrite_from_index_token
for the registry of thefrom_index
container image when applicable. IIB will never directly modify this file though. This defaults to~/.docker/config.json.template
. -
iib_dogpile_backend
- the configuration for the dogpile.cache backend. The default value is'dogpile.cache.null'
. In case you want to enable caching, set this to'dogpile.cache.memcached'
. -
iib_dogpile_expiration_time
- the number of seconds after which the cached item is expired. -
iib_dogpile_arguments
- additional arguments for the dogpile backend. -
iib_greenwave_url
- the URL to the Greenwave REST API if gating is desired (e.g.https://greenwave.domain.local/api/v1.0/
). This defaults toNone
. -
iib_grpc_init_wait_time
- time to wait for the index image service to be initialized. This defaults to3
seconds. -
iib_grpc_max_port_tries
- maximum ports to try when initializing the index image service. This defaults to100
tries. -
iib_grpc_start_port
- first port to try when starting the service (subsequent are increments). This defaults to50051
. -
iib_grpc_max_tries
- maximum number of times to try to start the index image service before giving up. This defaults to5
attempts. -
iib_index_image_output_registry
- if set, that value will replace the value fromiib_registry
in the outputindex_image
pull specification. This is useful if you'd like users of IIB to pull from a proxy to a registry instead of the registry directly. -
iib_image_push_template
- the Python string template of the push destination for the resulting manifest list. The available variables areregistry
andrequest_id
. The default value is{registry}/iib-build:{request_id}
. -
iib_log_level
- the Python log level foriib.workers
logger. This defaults toINFO
. -
iib_max_recursive_related_bundles
- the maximum number of recursive related bundles IIB will recurse through. This is to avoid DOS attacks. -
iib_organization_customizations
- this is used to customize aspects of the bundle being regenerated. The format is a dictionary where each key is an organization that requires customizations. Each value is a list of dictionaries with thetype
key set to one of the optional valuescsv_annotations
,package_name_suffix
, andregistry_replacements
. The order of the dictionaries in the list will determine the order of customizations applied to the bundle.- The
csv_annotations
customization type is a dictionary where the keyannotations
value is a dictionary where each key is the annotation to set on the ClusterServiceVersion files, and the value is a Python template string of the value to be set. IIB only substitutes{package_name}
in the template string. - The
package_name_suffix
customization type is a dictionary where the keysuffix
value is a string of a suffix to add to the package name of the operator. - The
registry_replacements
customization type is a dictionary where the keyreplacements
value is a dictionary where the keys are the old registries to replace and the values are the registries to replace the old registries with. This replaces the registry in all the ClusterServiceVersion files. - The
image_name_from_labels
customization type is a dictionary where the keytemplate
value is a string which specifies a combination of label names in curly braces which will be substituted with the actual label values from the bundle image. - The
enclose_repo
customization type is a dictionary where the keyenclosure_glue
value is a string which specifies the glue to replace/
(forward slashes) in the pull spec name and repo. The keynamespace
value is also a string which specifies the new namespace for pull specs of the ClusterServiceVersion files. - The
related_bundles
customization type is a dictionary with no additional arguments and it essentially finds all the bundle image pull specifications in the CSV files of the operator bundle image in question. If this customization is not specified in the config for an organization, therelated_bundles
endpoint won't work for regenerate-bundle requests of that organization. If no organization is specified, IIB will try to findrelated_bundles
for all regenerate-bundle requests. - The
resolve_image_pullspecs
customization type is a dictionary with no additional arguments and it basically pins all the pull specs in the CSV files of the operator bundle image in question to their digests. If this customization is not specified in the config for an organization, the pinning will not be done. If no organization is specified, IIB will try and pin the pull specs in the CSV files to their digests. - The
perform_bundle_replacements
customization type is a dictionary with no additional arguments. It enables bundle replacements to be passed to the bundle regeneration APIs with thebundle_replacements
parameter. If the customization type is not set, and bundle replacements specified will be ignored.
Here is an example that ties this all together:
iib_organization_customizations = { 'company-marketplace': [ {'type': 'resolve_image_pullspecs'}, { 'type': 'csv_annotations', 'annotations': { 'marketplace.company.io/remote-workflow': ( 'https://marketplace.company.com/en-us/operators/{package_name}/pricing' ), 'marketplace.company.io/support-workflow': ( 'https://marketplace.company.com/en-us/operators/{package_name}/support' ), }, }, {'type': 'package_name_suffix', 'suffix': '-cmp'}, { 'type': 'registry_replacements', 'replacements': { 'registry.access.company.com': 'registry.marketplace.company.com/cm', }, }, {'type': 'image_name_from_labels', 'template': '{name}-{version}-final'}, {'type': 'enclose_repo', 'enclosure_glue': '----', 'namespace': "company-pending"}, ] }
- The
-
iib_request_related_bundles_dir
- the directory to write the request specific related bundles file. IfNone
, per request related bundles files are not created. This defaults toNone
. -
iib_request_logs_dir
- the directory to write the request specific log files. IfNone
, per request log files are not created. This defaults toNone
. -
iib_request_recursive_related_bundles_dir
- the directory to load the recursive-related-bundles request specific recursive related bundles files. This is a required config variable ifiib_aws_s3_bucket_name
is not specified. -
iib_request_logs_format
- the format for the log messages of the request specific log files. This defaults to%(asctime)s %(name)s %(levelname)s %(module)s.%(funcName)s %(message)s
. -
iib_request_logs_level
- the log level for the request specific log files. This defaults toDEBUG
. -
iib_registry
- the container registry to push images to (e.g.quay.io
). -
iib_sac_queues
- list of names of celery queues which should be created as single-active-consumer -
iib_skopeo_timeout
- the command timeout for skopeo commands run by IIB. This defaults to30s
(30 seconds). -
iib_total_attempts
- the total number of attempts to make at trying a function relating to the container registry before erroring out. This defaults to5
. It's also used as the max number of attempts to buildah when receiving HTTP 50X errors. -
iib_retry_delay
- the delay in seconds between retry attempts. It's just used for buildah when receiving HTTP 50X errors. This defaults to5
. -
iib_retry_jitter
- the extra seconds to be added on delay between retry attempts. It's just used for buildah when receiving HTTP 50X errors. This defaults to5
. -
iib_retry_multiplier
- the constant in the2^x * multiplier
formula, where x stands for attempt number. Formula is used to calculate the seconds to be added on delay between retry attempts. It's just used for buildah when receiving HTTP 50X errors. This defaults to5
.
If you wish to configure AWS S3 bucket for storing artifact files, the following environment variables
must be set along with iib_aws_s3_bucket_name
config variable:
AWS_ACCESS_KEY_ID
- the secret access key ID used to access the AWS S3 bucket.AWS_SECRET_ACCESS_KEY
- the secret access key used to access the AWS S3 bucket.AWS_DEFAULT_REGION
- the default region of the AWS S3 bucket
More info on these environment variables can be found in the AWS User Guide
In addition to building operator index images, IIB can also be used to regenerate operator bundle images. This is useful for applying modifications to the manifests embedded in the bundle image. IIB uses the operator-manifest library to assist in these modifications.
Currently, IIB will not perform any modifications on a ClusterServiceVersion file if spec.relatedImages is set.
If it's not set, IIB will pin any container image pull specification and set
spec.relatedImages.
See the different
pull specifications
to which this process applies to. There are also a variety of customizations that can be made to
the bundle. See the iib_organization_customizations
configuration option for more details.
Bundle images regenerated by IIB will have the label com.redhat.iib.pinned
set to 'true'
.
If the bundle image already has this label set to this value, pinning is skipped. Any other
modifications, such as registry replacement, will still be applied.
IIB has support to send messages to an AMQP 1.0 broker. If configured to do so, IIB will send messages when a build request state changes and when a batch state changes. Please note that if a message can't be sent due to an infrastructure issue, the build request will continue as it is not considered a fatal error.
The build request state change message body is the JSON representation of the build request in
the non-verbose format like in the /builds
API endpoint. The message has the following keys set in
the application properties: batch
, id
, state
, and user
.
The batch state change message body is a JSON object with the following keys: annotations
,
batch
, requests
, state
, and user
. The requests
value is an array of JSON objects with the
keys id
, organization
, and request_type
. The message has the following keys set in the
application properties: batch
, state
, and user
.
In addition to building operator index images, IIB can also gate your bundle images before adding them to the index image. If a Greenwave configuration is setup for your queue, IIB will query Greenwave to check if your bundle image builds have passed the tests in the Greenwave policy you have defined. The IIB request submitted to that queue will succeed only if the policy is satisfied.
This package has documentaiton on Read the Docs
To build and serve the docs, run the following commands:
tox -e docs
google-chrome .tox/docs_out/index.html
To document a new Python module, find the rst
file of the corresponding Python package that
contains the module. Once found, add a section under "Submodules" in alphabetical order such as:
iib.workers.tasks.build module
------------------------------
.. automodule:: iib.workers.tasks.build
:ignore-module-all:
:members:
:private-members:
:show-inheritance:
Some of the options include:
ignore-module-all
- include all members regardless of the definition of__all__
.members
- automatically document the members in that Python module.private-members
- include private functions and methods.show-inheritance
- show the class inheritance.