|
6 | 6 |
|
7 | 7 | from __future__ import annotations |
8 | 8 |
|
| 9 | +from shlex import quote as shlex_quote |
| 10 | + |
9 | 11 | from pyinfra import host |
10 | | -from pyinfra.api import operation |
| 12 | +from pyinfra.api import MaskString, OperationError, QuoteString, StringCommand, operation |
11 | 13 | from pyinfra.facts.docker import ( |
| 14 | + DockerAuths, |
12 | 15 | DockerContainer, |
13 | 16 | DockerImage, |
14 | 17 | DockerNetwork, |
15 | 18 | DockerPlugin, |
16 | 19 | DockerVolume, |
17 | 20 | ) |
18 | 21 |
|
| 22 | +DOCKER_HUB_SERVER = "https://index.docker.io/v1/" |
| 23 | + |
19 | 24 | from .util.docker import ContainerSpec, handle_docker, parse_image_reference |
20 | 25 |
|
21 | 26 |
|
@@ -554,3 +559,112 @@ def plugin( |
554 | 559 | command="remove", |
555 | 560 | plugin=plugin_name, |
556 | 561 | ) |
| 562 | + |
| 563 | + |
| 564 | +@operation() |
| 565 | +def login( |
| 566 | + username: str, |
| 567 | + password: str, |
| 568 | + server: str | None = None, |
| 569 | + force: bool = False, |
| 570 | +): |
| 571 | + """ |
| 572 | + Log in to a Docker registry. |
| 573 | +
|
| 574 | + + username: username to authenticate with |
| 575 | + + password: password to authenticate with |
| 576 | + + server: registry server to log in to (defaults to Docker Hub) |
| 577 | + + force: log in even if ``~/.docker/config.json`` already has an entry for the server |
| 578 | +
|
| 579 | + Idempotency is checked against the ``auths`` section of |
| 580 | + ``${DOCKER_CONFIG:-$HOME/.docker}/config.json``: if the server is already |
| 581 | + present, the operation is a no-op. Use ``force=True`` to re-run ``docker |
| 582 | + login`` (e.g. after rotating credentials). |
| 583 | +
|
| 584 | + The password is piped to ``docker login --password-stdin`` so it is not |
| 585 | + exposed on the command line, and is masked in pyinfra's command log. |
| 586 | +
|
| 587 | + **Examples:** |
| 588 | +
|
| 589 | + .. code:: python |
| 590 | +
|
| 591 | + from pyinfra.operations import docker |
| 592 | +
|
| 593 | + # Log in to a private registry |
| 594 | + docker.login( |
| 595 | + name="Log in to private registry", |
| 596 | + server="myregistry.io:5000", |
| 597 | + username="ci", |
| 598 | + password="s3cret", |
| 599 | + ) |
| 600 | +
|
| 601 | + # Log in to Docker Hub |
| 602 | + docker.login( |
| 603 | + name="Log in to Docker Hub", |
| 604 | + username="ci", |
| 605 | + password="s3cret", |
| 606 | + ) |
| 607 | + """ |
| 608 | + if not username: |
| 609 | + raise OperationError("docker.login requires a username") |
| 610 | + if not password: |
| 611 | + raise OperationError("docker.login requires a password") |
| 612 | + |
| 613 | + target_server = server or DOCKER_HUB_SERVER |
| 614 | + |
| 615 | + if not force: |
| 616 | + existing_auths = host.get_fact(DockerAuths) |
| 617 | + if target_server in existing_auths: |
| 618 | + host.noop(f"Already logged in to Docker registry {target_server}") |
| 619 | + return |
| 620 | + |
| 621 | + command_bits: list = [ |
| 622 | + "printf '%s'", |
| 623 | + MaskString(shlex_quote(password)), |
| 624 | + "| docker login --username", |
| 625 | + QuoteString(username), |
| 626 | + "--password-stdin", |
| 627 | + ] |
| 628 | + if server: |
| 629 | + command_bits.append(QuoteString(server)) |
| 630 | + |
| 631 | + yield StringCommand(*command_bits) |
| 632 | + |
| 633 | + |
| 634 | +@operation() |
| 635 | +def logout(server: str | None = None): |
| 636 | + """ |
| 637 | + Log out of a Docker registry. |
| 638 | +
|
| 639 | + + server: registry server to log out of (defaults to Docker Hub) |
| 640 | +
|
| 641 | + No-ops when the server is not present in |
| 642 | + ``${DOCKER_CONFIG:-$HOME/.docker}/config.json``. |
| 643 | +
|
| 644 | + **Examples:** |
| 645 | +
|
| 646 | + .. code:: python |
| 647 | +
|
| 648 | + from pyinfra.operations import docker |
| 649 | +
|
| 650 | + # Log out of a private registry |
| 651 | + docker.logout( |
| 652 | + name="Log out of private registry", |
| 653 | + server="myregistry.io:5000", |
| 654 | + ) |
| 655 | +
|
| 656 | + # Log out of Docker Hub |
| 657 | + docker.logout(name="Log out of Docker Hub") |
| 658 | + """ |
| 659 | + target_server = server or DOCKER_HUB_SERVER |
| 660 | + |
| 661 | + existing_auths = host.get_fact(DockerAuths) |
| 662 | + if target_server not in existing_auths: |
| 663 | + host.noop(f"Not logged in to Docker registry {target_server}") |
| 664 | + return |
| 665 | + |
| 666 | + command_bits: list = ["docker logout"] |
| 667 | + if server: |
| 668 | + command_bits.append(QuoteString(server)) |
| 669 | + |
| 670 | + yield StringCommand(*command_bits) |
0 commit comments