Skip to content

Refactor handling of default values in configuration objects #1354

Description

@sgaist

Summary

renku-data-services uses multiple configuration objects for various aspects and features and some of them have default values.

Currently, most of these default values are used at the "point of use" where the configuration is required. This can be a bit surprising as reading the implementation of the configuration shows one reality (i.e. configuration is set to None because no environment variable was set) VS something is happening (i.e. builds using the default value for the build strategy although the configuration code does not indicate that).

While these defaults do make sense, having them handled in two separate places makes things harder to reason about and, sometimes, tests.

Suggestion

Move the default value handling into the configuration classes and maybe make them frozen as I don't think they are meant to be changed at run time.

Example

@dataclass
class BuildsConfig:
    """Configuration for container image builds."""

    build_output_image_prefix: str

    @classmethod
    def from_env(cls) -> "BuildsConfig":
        """Create a config from environment variables."""
        build_builder_image = os.environ.get("BUILD_BUILDER_IMAGE", constants.BUILD_DEFAULT_BUILDER_IMAGE)

This would avoid having:

        builder_image = self.builds_config.build_builder_image or constants.BUILD_DEFAULT_BUILDER_IMAGE

in the db.py file

Since this is a setting that is technically mandatory we could even have:

@dataclass
class BuildsConfig:
    """Configuration for container image builds."""

    build_output_image_prefix: str

    @classmethod
    def from_env(cls) -> "BuildsConfig":
        """Create a config from environment variables."""
        build_builder_image = os.environ.get("BUILD_BUILDER_IMAGE", constants.BUILD_DEFAULT_BUILDER_IMAGE) or constants.BUILD_DEFAULT_BUILDER_IMAGE

So even in case of empty environment variable we would be good to go.

Since that would be a repeating pattern:

def get_with_default(haystack: dict[str, str], needle: str, default: str) -> str:
    """Returns the default value if needle is not found or the value is None/empty."""
    return haystack.get(needle, default) or default


@dataclass
class BuildsConfig:
    """Configuration for container image builds."""

    build_output_image_prefix: str

    @classmethod
    def from_env(cls) -> "BuildsConfig":
        """Create a config from environment variables."""
        build_builder_image = get_with_default(os.environ, "BUILD_BUILDER_IMAGE", constants.BUILD_DEFAULT_BUILDER_IMAGE)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions