Want to contribute? Great! We will try to make it easy. All contributions, even the smaller ones are more than welcome.
TODO
To contribute, use GitHub Pull Requests, from your own fork. Also, make sure you have set up your Git authorship correctly:
git config --global user.name "Your Full Name"
git config --global user.email your.email@example.com
All submissions, including submissions by project members, need to be reviewed before being merged.
In the interest of supporting Red Hat's Conscious Language Initiative, we are discouraging the use of the following terms in the code, messages, or comments. Note the alternatives we will accept:
- Whitelist -> allowlist
- Blacklist -> blocklist
- Master/Slave terminology suggestions:
- primary / secondary
- source / replica
- initiator, requester / responder
- controller, host / device, worker, proxy
- director / performer
- Separate subject from the body with a blank line
- Use past tense verbs
- Wrap the lines at 72 characters
- Use the body to explain what changed
- In the few cases it makes sense, please include reason the change was needed, or why it was implemented in a certain way
- Prefix the subject to include the Jira card number if it exists (CANDLEPIN-1234:)
- Prefix the subject to include the Bugzilla number if it exists (12345678:)
Example:
CANDLEPIN-1234: short summary
- detail 1
- detail 2
We use GitHub Actions for Continuous Integration.
The workflows are defined in .github/workflows.
The following checks are run on every Pull Request:
- Unit Tests: Runs the unit test suite (
./gradlew test) and generates coverage reports. - Spec Tests: Runs the specification tests against different database backends (PostgreSQL, MariaDB) and modes (Standalone, Hosted). This involves spinning up the necessary containers (Candlepin, Database) and executing the tests.
- Checkstyle: Enforces code style guidelines using Checkstyle (
./gradlew checkstyle). - Woke: Detects non-inclusive language in the source code.
- Validate Translations: Validates translation files (
./gradlew validate_translation). - Jira Check: Validates that the Jira ticket associated with the PR has a valid Target Version.
Other workflows include:
- Sonar Analysis: Runs SonarQube analysis for code coverage and security on long-lived branches.
- I18n: Periodically runs internationalization scripts to sync translations.
You can view the status of these checks at the bottom of your Pull Request.
The basic formatting of Javadoc blocks is as seen in this example:
/**
* Multiple lines of Javadoc text are written here,
* wrapped normally...
*/
public int method(String p1) { ... }... or in this single-line example:
/** An especially short bit of Javadoc. */The basic form is always acceptable. The single-line form may be substituted when the entirety of the Javadoc block (including comment markers) can fit on a single line. Note that this only applies when there are no block tags such as @param.
Javadoc paragraphs should be separated by a single line containing only an unclosed paragraph tag (<p>) at the same indentation of the section in which it occurs. Each subsequent paragraph in a given section should use the same level of indentation. Lines containing only a paragraph tag should not be preceded or followed by a blank line.
When additional HTML is used in a Javadoc block, logical paragraphs beginning with, or immediately following, block-level HTML elements, such as <ul> or <table>, are not preceded with a paragraph tag.
Example:
/**
* A Javadoc block with many paragraphs
* <p>
* Although it may violate HTML best practices, the paragraph tag
* should not be closed in either form (<p/> or <p></p>).
*
* <ul>
* <li>lists, tables, and block-level elements or structures need
* not be separated with paragraph tags.</li>
* </ul>
*
* The paragraph following a block-level HTML element should not be
* explicitly separated by paragraph tags. Blank lines are optional
* if it improves readability.
* <p>
* <strong>Note that</strong> paragraphs beginning with inline
* elements should still be preceded by a paragraph tag.
*/HTML within Javadoc should attempt to follow conventions for well-formed HTML. This is largely outside the scope of this style guide, but some common basics will be covered here. In cases where HTML best practices are at odds with the style defined by this document, those defined in this document take precedence.
Unfortunately, Javadoc's format imposes a three-character prefix (space-asterisk-space) on each line of its content, which makes aligning with the normal tab stops defined in code difficult, unwieldy, and/or inconsistent. As such, block-level indentation in Javadoc will deviate from the indentation rules established for Java code.
When nesting tags, such as when building a table or a list, each set of block-level tags should add an additional level of indentation of +2 spaces from the containing paragraph or block.
Example:
/**
* Javadoc using HTML tags should attempt to keep them well-formed
* by following block-level indentation rules for block tags.
* <p>
* This is a list of things:
* <ul>
* <li>item 1</li>
* <li>item 2</li>
* <li>
* a list item, but one with enough stuff in it to justify
* treating it as a <em>new</em> block.
* </li>
* <li>inline list items with enough content to require continuation
* are also acceptable, and do not require additional indentation</li>
* </ul>
*/Any of the standard "block tags" that are used appear in the order @deprecated, @param, @throws, @return, and these four types never appear with an empty description.
Block tags with a parameter, such as @param and @throws, should define the parameter on the same line as the tag itself, followed by a line break before the description.
The description accompanying all block tags should begin on the following line, indented four spaces from the indentation of the Javadoc block (not the tag declaration). If the description does not fit on a single line, following the column length limits, it should be separated with a line break, and the continuation line should be indented at the same level as the previous line of the description.
Example:
/**
* A well-formed Javadoc description. Long lines should be wrapped
* following normal column length limits; but continuations should
* not have additional indentation.
*
* @deprecated
* An explanation as to why the method is deprecated
*
* @param myParam
* A description of myParam and any additional context. If
* the description spans multiple lines, the lines should be
* indented at the same level.
*
* @throws IllegalArgumentException
* when an argument is illegal
*
* @throws AnotherException
* when some other thing goes horribly wrong
*
* @return
* a description of the value returned by this method
*/Each Javadoc block begins with a brief summary fragment. This fragment is very important: it is the only part of the text that appears in certain contexts such as class and method indexes.
This is a fragment—a noun phrase or verb phrase, not a complete sentence. It does not begin with A {@code Foo} is a..., or This method returns..., nor does it form a complete imperative sentence like Save the record.. However, the fragment is capitalized and punctuated as if it were a complete sentence.
Tip: A common mistake is to write simple Javadoc in the form /** @return the customer ID */. This is incorrect, and should be changed to /** Returns the customer ID */ or /** {@return the customer ID} */.
At a minimum, Javadoc is present for every visible class, member, or record component, with a few exceptions noted below. A top-level class is visible if it is public; a member is visible if it is public or protected and its containing class is visible; and a record component is visible if its containing record is visible.
Additional Javadoc content may also be present, as explained in the Non-required Javadoc section.
Javadoc is optional for "simple, obvious" members and record components, such as a getFoo() method, if there really and truly is nothing else worthwhile to say but "the foo".
It is important to note that this rule should not be used as justification for omitting documentation in cases where the method or its value contain information or require additional context that a typical reader may not have. Some examples:
A record component named canonicalName should be documented if a typical reader may not know what the term "canonical name" means.
A method named getCanonicalName should be documented if its output could deviate with changes to the object's state (e.g. the default state is null, an empty string, or some sentinel value)
Javadoc is not required on a method that overrides a supertype method, unless its implementation adds additional edge cases, details, or restrictions on the method's original contract.
Other classes, members, and record components have Javadoc as needed or desired.
Whenever an implementation comment would be used to define the overall purpose or behavior of a class or member, that comment is written as Javadoc instead (using /**).
Note that non-required Javadoc is still required to follow the formatting rules of Formatting and The summary fragment.
If you have not done so on this machine, you need to:
- Install Git and configure your GitHub access
- Install OpenJDK 17 (for compiling) and OpenJDK 25 (for running Tomcat)
Docker is not strictly necessary: it is used to run the MariaDB and PostgreSQL tests which are not enabled by default.
TODO
TODO
TODO
Candlepin uses gradle & gradle wrapper for building & running unit tests.
To build Candlepin run ./gradlew war from the root of the project.
A number of build flags can be passed to Gradle in order to turn on or off certain features. The available flags are as follows:
-Pdatabase_server=(mysql|postgres)Specify MariaDB/MySQL or PostgreSQL as the database server to target. This defaults to postgres.-Pdb_host="hostname"Specify the hostname for the database server. This defaults to localhost.-Papp_db_name="db_name"Specify the name of the db schema to use. This defaults tocandlepin.-Pcpdb_username="user"Specify the database username. This defaults tocandlepin.-Pcpdb_password="pass"Specify the database password. This defaults to empty.-Phostedtest=trueEnable the hosted test suite.-Pmanifestgen=trueEnable the manifest generator extension.-Pexternal_broker=trueUse an external Artemis message broker.
The generateConfig Gradle task generates a candlepin.conf file from the
template at config/candlepin/candlepin.conf.template. This is the single
source of truth for Candlepin configuration and is used by all environments
(local deploy, GitHub Actions spec tests, and dev containers).
./gradlew generateConfig -Pdb_host=localhost -Pmanifestgen=true -Phostedtest=true -Pcpdb_password=candlepinThe generated file is written to build/candlepin.conf.
./gradlew gettextrunsxgettextto extract strings from source files../gradlew msgmergerunsmsgmergeto merge translation updates back into the primary keys.pot file../gradlew msgfmtrunsmsgfmtto convert the keys .po & .pot files into the generated java source files for compilation into the build. This task is run as a prerequisite of the compileJava task so it is run automatically every time compilation is done.
./gradlew dependencyCheckAnalyze
The dependencyCheckAnalyze task will check a project using the OWASP Dependency Check
to see if any dependencies have CVEs reported against them.
The maximum allowable CVSS score can be modified by setting the max_allowed_cvss to a float value
between 1.0 and 10.0. Any CVEs above the maximum allowed CVSS score will cause the build to fail.
The reports will be generated automatically under the build/reports folder.
./gradlew checkstyleRuns checkstyle for both production, test, and spec test code../gradlew checkstyleMainRuns checkstyle only for production code../gradlew checkstyleTestRuns checkstyle only for test code../gradlew checkstyleSpecRuns checkstyle only for spec test code.
./gradlew testruns all of the unit tests../gradlew test --tests org.candlepin.controller.Cdn*runs only the unit tests matched by the given package/class and wildcard(s).
We use JaCoCo for unit test coverage, by means of the gradle jacoco plugin.
./gradlew test coveragewill run the unit tests and then generate a coverage report based on the unit test report. If you only run a subset of the tests, then the non-exercised classes/methods/lines will look uncovered in the report.
Spec tests are written in Java which are running with the aid of an auto-generated OpenApi client.
To run Java spec tests:
./gradlew specruns all Java tests serially./gradlew spec --tests org.candlepin.spec.StatusSpec*runs only the spec tests matched by the given package/class and wildcard(s).
More information on Java spec tests
Adding a changeset to be run during the liquibase database migration is a two step process.
- Create changeset file in the
src/main/resource/dbdirectory. - Include the new changeset file in the
src/main/resources/db/changelog/changelog-update.xmlfile.
The date and time prefix for the filename is the year, month, day, second, and millisecond that the changeset was created.
You concatenate the numeric values in that order. For example, a date time prefix for a changeset created in 2024 on August 2nd, might look like: 20240802109000
You then append a title of your changes to the date time prefix to complete the filename.
Example changeset filename:
20240802101300-test-change.xml
- We use an
openapi-generatorplugin that generates our REST API along with JAX-RS and swagger annotations based on the spec atapi/candlepin-api-spec.yaml, which is OpenApi Specification 3.0 standard. That yaml file is rendered by Swagger UI athttps://<server_ip>:8443/candlepin/docs, or it can be retrieved in a raw format fromhttps://<server_ip>:8443/candlepin/docs/candlepin-api-spec.yaml.
This topic guides you through configuring and running Candlepin using the official container images.
Candlepin container images come in two types:
- Production Base Image: quay.io/candlepin/candlepin:latest
- Development Image: quay.io/candlepin/candlepin:dev-latest
The Candlepin production base image is designed to be a base image for your own Candlepin image that can run in a production environment. The reason for this is that the image does not include any default configurations and it is expected that you provide configurations that are appropriate for your production environment. The following are the configurations that you will need to provide:
- Candlepin configurations
- Tomcat server.xml configurations
- Certificate and key for TLS communication and Candlepin encryption
Note: When adding your own certificate, you must also update the Java trust store with this certificate. This can be done by copying the certificate to the /etc/pki/ca-trust/source/anchors directory and running update-ca-trust.
The following is an example on how to extend and run the Candlepin production image using your own configurations and certificates.
Example:
FROM quay.io/candlepin/candlepin:latest
USER root
COPY ./candlepin.conf /etc/candlepin/
COPY ./server.xml /opt/tomcat/conf
COPY ./certs /etc/candlepin/certs
# Add the certificate to the Java trust store
RUN ln -s /etc/candlepin/certs/*.crt /etc/pki/ca-trust/source/anchors --force; \
update-ca-trust;
USER tomcat
EXPOSE 8080 8443 5432 3306
ENTRYPOINT ["/opt/tomcat/bin/catalina.sh", "run"]The Candlepin development image is designed to run right after pulling the image using default Candlepin and Tomcat configurations as well as a default certificate and key for TLS communication and encryption. Since this certificate and key is packaged in a publicly available container image, the use of the Candlepin development image should not be used in a production environment to avoid security risks.
The following is an example on how to run the Candlepin development container using a docker compose file or a kubernetes file via podman.
Two compose files are in the dev-container directory. They will pull the latest development Candlepin image and the latest PostgreSQL image. The Candlepin configuration is generated from a single template using the Gradle generateConfig task, ensuring consistency across all environments.
Before starting the dev container, build the WAR file and generate the configuration:
./gradlew war -Ptest_extensions=hostedtest,manifestgen
./gradlew generateConfig -Pdb_host=postgres -Phostedtest=true -Pmanifestgen=true -Pcpdb_password=candlepinThen start the docker container with:
docker compose up --build
and stop it with:
docker compose down
Or the kubernetes container with:
podman play kube candlepin-deployment.yaml []/--down
This topic provides information on how to configure the Candlepin container.
Candlepin has configuration values to control functionality that includes JPA data access, logging levels, OAuth, individual modules, etc. There are two ways you can set Candlepin specific configuration values.
- Configuration file
- Environment variables
Configuration File: The Candlepin configuration file (/etc/candlepin/candlepin.conf) is a list of properties and their values that is read by Candlepin.
Environment Variables: Candlepin uses Smallrye to read environment variables and use them for running Candlepin. This means that we adhere to Smallrye's environment variable naming and conversion rules.
The following are notable paths within the Candlepin images.
| Path | Description |
|---|---|
| /etc/candlepin/ | Directory for Candlepin configurations |
| /etc/candlepin/certs | Default directory for certificate for TLS communication and Candlepin encryption |
| /opt/tomcat/ | Tomcat installation root directory |
| /opt/tomcat/conf | Tomcat configuration directory |
| /opt/tomcat/bin | Directory that includes Tomcat startup, shutdown, and other scripts |
| /var/logs/candlepin | Candlepin log directory |
The Candlepin development image uses a candlepin.conf file generated by the Gradle generateConfig task from the template at config/candlepin/candlepin.conf.template. The dev-container/docker-compose.yml file mounts the generated build/candlepin.conf into the container at /etc/candlepin/candlepin.conf.
To generate the configuration for the dev container with default settings (PostgreSQL, hosted mode):
./gradlew generateConfig -Pdb_host=postgres -Phostedtest=true -Pmanifestgen=true -Pcpdb_password=candlepinThis produces a configuration that includes JPA/Hibernate settings, authentication, Quartz scheduler configuration, and module settings. You can customize the output by changing the flags passed to generateConfig (see Custom Build Properties).
The Postgres container is expected to have the following environment variables set:
| Environment variable | Value |
|---|---|
| POSTGRES_USER | candlepin |
| POSTGRES_PASSWORD | candlepin |
| POSTGRES_DB | candlepin |
Default Tomcat server.xml connector configuration:
<Connector port="8443"
protocol="org.apache.coyote.http11.Http11AprProtocol"
scheme="https"
secure="true"
SSLEnabled="true"
maxThreads="150"
compression="on"
compressableMimeType="application/json,text/html,text/xml">
<SSLHostConfig certificateVerification="optional"
protocols="+TLSv1.2,+TLSv1.3"
sslProtocol="TLS"
caCertificateFile="/etc/candlepin/certs/candlepin-ca-bundle.crt">
<Certificate certificateFile="/etc/candlepin/certs/candlepin-mldsa-65-ca.crt"
certificateKeyFile="/etc/candlepin/certs/candlepin-mldsa-65-ca.key"
type="MLDSA" />
<Certificate certificateFile="/etc/candlepin/certs/candlepin-rsa-ca.crt"
certificateKeyFile="/etc/candlepin/certs/candlepin-rsa-ca.key"
type="RSA" />
</SSLHostConfig>
</Connector>TODO