Skip to content

Commit

Permalink
Add 'container check' command for checking size and number of layers (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ssbarnea authored Sep 25, 2024
1 parent ae8bc58 commit 439d505
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 23 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
"prettier.proseWrap": "always",
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false
"python.testing.unittestEnabled": false,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
56 changes: 38 additions & 18 deletions docs/tools.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
# Supported Tools

## make
## Embedded commands

### changelog (github)

`changelog` command will produce a `CHANGELOG.md` file based on Github Releases.
You can define `CHANGELOG_FILE` environment variable to make it generate the
file in a different location.

This command is available only when `gh` command line utility is installed.

### containers check

You can use this command to verify if a specific container image has a maximum
size or maximum number of layers. This is useful when you want to prevent
accidental grow of an image your are producing.

```bash
$ mk containers check your-image-id-or-name --max-size=200 --max-layers=1
Image has too many layers: 3 > 1
Image size exceeded the max required size (MB): 301 > 200
FAIL: 1
```

You can also specify the container engine to be used, the default is to use
docker if found or podman if docker is not found.

## Recognized tools

### make

If a [makefile](https://www.gnu.org/software/make/manual/make.html) is found on
the repository root, the tool will expose all its targets that have a trailing
Expand All @@ -10,7 +38,7 @@ the command will not be exposed, as we assume that this is an internal target.
A good example of a project using this pattern is
[podman](https://github.com/containers/podman).

## npm
### npm

If a [package.json](https://docs.npmjs.com/cli/v7/configuring-npm/package-json)
file is found, the tool will expose all the scripts defined in the `scripts`.
Expand All @@ -21,28 +49,28 @@ files, we are unable to provide descriptions for exposed commands. Still, if
others will find a good way to do it that gets some adoption, we will be more
than happy to add support for loading descriptions too.

## shell
### shell

All shell scripts found inside the repository root and`(scripts|tools|bin)/`
sub-folders will be exposed as commands.

## taskfile
### taskfile

[Taskfile](https://taskfile.dev/#/) is a task runner that uses YAML files. It is
similar to make, but it is written in Go and it is more flexible.

## tox
### tox

All tox environments will be exposed as commands and their descriptions will
also be shown. Internally, the tool will run `tox -lav` to get the list of
available environments and their descriptions.

## ansible
### ansible

Any playbook found inside the `playbooks/` sub-folder will be exposed as a
command.

## git
### git

Inside git repositories, the tool will expose the `up` command which can be used
to create an upstream pull request.
Expand All @@ -51,12 +79,12 @@ If the current git repository is using
[Gerrit](https://www.gerritcodereview.com), it will run `git review` and if the
repository is from GitHub, it will run `gh pr create` instead.

## pre-commit
### pre-commit

If a [pre-commit](https://pre-commit.com/) configuration file is found, the tool
will expose the `lint` command for running linting.

## py (python packages)
### py (python packages)

If the current repository is a Python package, the tool will expose a set of
basic commands:
Expand All @@ -65,15 +93,7 @@ basic commands:
- `uninstall`: Uninstall the current package
- `build`: Run `python -m build`

## pytest
### pytest

If a [pytest](https://docs.pytest.org/en/stable/) configuration file is found, a
`test` command will be exposed that runs `pytest`.

## changelog (github)

`changelog` command will produce a `CHANGELOG.md` file based on Github Releases.
You can define `CHANGELOG_FILE` environment variable to make it generate the
file in a different location.

This command is available only when `gh` command line utility is installed.
11 changes: 8 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ repository = "https://github.com/pycontribs/mk"
changelog = "https://github.com/pycontribs/mk/releases"

[tool.black]

# keep this value because typer does not accept new annotations such str | None
# from https://peps.python.org/pep-0604/
target-version = ["py39"]

# Keep this default because xml/report do not know to use load it from config file:
# data_file = ".coverage"
Expand All @@ -74,7 +76,7 @@ source = ["src", ".tox/*/site-packages"]
exclude_also = ["pragma: no cover", "if TYPE_CHECKING:"]
omit = ["test/*"]
# Increase it just so it would pass on any single-python run
fail_under = 46
fail_under = 45
skip_covered = true
skip_empty = true
# During development we might remove code (files) with coverage data, and we dont want to fail:
Expand Down Expand Up @@ -135,10 +137,13 @@ disable = [
]

[tool.ruff]
target-version = "py310"
# keep this as typer does not support new annotations format
target-version = "py39"
# Same as Black.
line-length = 88
lint.ignore = [
# Disabled due to typer not supporting new annotations format
"UP007",
# temporary disabled until we fix them:
"ANN",
"B",
Expand Down
53 changes: 52 additions & 1 deletion src/mk/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

import argparse
import itertools
import json
import logging
import os
import shlex
from typing import Any
import shutil
from typing import Annotated, Any, Optional

import typer
from rich.console import Console
Expand All @@ -16,6 +18,7 @@
from mk import __version__
from mk._typer import CustomTyper
from mk.ctx import ctx
from mk.exec import run_or_fail

handlers: list[logging.Handler]
console_err = Console(stderr=True)
Expand Down Expand Up @@ -87,6 +90,54 @@ def commands() -> None:
print(action.name)


@app.command()
def containers(
command: Annotated[
Optional[str],
typer.Argument(help="Command to run, possible values: check"),
] = None,
image: Annotated[
str,
typer.Argument(help="Specify image name or identifier"),
] = "",
engine: Annotated[
str,
typer.Option(help="Comma separated list of container engines to look for."),
] = "docker,podman",
max_size: Annotated[int, typer.Option(help="Maximum image size in MB")] = 0,
max_layers: Annotated[int, typer.Option(help="Maximum number of layers")] = 0,
) -> None:
"""Provide some container related helpers."""
if command != "check":
typer.echo("Invalid command.")
raise typer.Exit(code=1)
if image:
executable = None
for v in engine.split(","):
if shutil.which(v):
executable = v
break
if not engine:
typer.echo(f"Failed to find any container engine. ({engine})")
raise typer.Exit(code=1)
result = run_or_fail(f"{executable} image inspect {image}")
inspect_json = json.loads(result.stdout)
size = int(inspect_json[0]["Size"] / 1024 / 1024)
layers = len(inspect_json[0]["RootFS"]["Layers"])
failed = False
if max_layers and layers > max_layers:
typer.echo(f"Image has too many layers: {layers} > {max_layers}")
failed = True
if max_size and size > max_size:
typer.echo(
f"Image size exceeded the max required size (MB): {size} > {max_size}",
)
failed = True
if failed:
raise typer.Exit(code=1)
typer.echo("Image check passed")


def cli() -> None: # pylint: disable=too-many-locals
parser = argparse.ArgumentParser(
description="Preprocess arguments to set log level.",
Expand Down

0 comments on commit 439d505

Please sign in to comment.