-
Notifications
You must be signed in to change notification settings - Fork 8
Podman support and option to backup across all projects #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice simplification of the container filter loop. Please see my comment about the shared network stack
This is fine, using socket should be backward compatible |
Doing some testing of this PR now, sorry it's taken me so long to get to this. Everything is going well in my previous test setup. Looks like the container filter changes will also fix #62 which is a nice bonus. Still need to test:
|
@AlexMcDermott your code is working as expected for individual projects, but it fails when backing up databases in other compose projects. This is because docker compose will create different networks for each compose project. You can import other networks into your compose project, but that's not a good user experience. Instead, I think we can give the backup container access to every network that it will need access to, instead of just the network defined for the main backup container. I actually like this better anyways, because then we don't have to mess with the backup container's network even within a single compose project. |
def network_details(self) -> dict: | ||
"""dict: The network details of the container""" | ||
network_settings: dict = self._data.get("NetworkSettings", {}) | ||
networks: dict = network_settings.get("Networks", {}) | ||
first_network = list(networks.values())[0] | ||
return first_network |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to return the full list of networks
to be passed into the backup runner startup step later so we can call Network.connect()
to add our backup runner to each network.
def network_details(self) -> dict: | |
"""dict: The network details of the container""" | |
network_settings: dict = self._data.get("NetworkSettings", {}) | |
networks: dict = network_settings.get("Networks", {}) | |
first_network = list(networks.values())[0] | |
return first_network | |
def network_details(self) -> list: | |
"""list: The network details of the container""" | |
network_settings: dict = self._data.get("NetworkSettings", {}) | |
networks: dict = network_settings.get("Networks", {}) | |
return list(networks.values()) |
@property | ||
def network_name(self) -> str: | ||
"""str: The name of the network the container is connected to""" | ||
return self.network_details.get("NetworkID", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still needed for the initial network creation, but you will need to adapt this to select the first network within this function since network_details
is now returning a list.
@property | |
def network_name(self) -> str: | |
"""str: The name of the network the container is connected to""" | |
return self.network_details.get("NetworkID", "") | |
@property | |
def network_name(self) -> str: | |
"""str: The name of the network the container is connected to""" | |
return self.network_details[0].get("NetworkID", "") |
@property | ||
def ip_address(self) -> str: | ||
"""str: IP address of the container""" | ||
return self.network_details.get("IPAddress", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can adapt this to select the first network within this function since network_details
is now returning a list
@property | |
def ip_address(self) -> str: | |
"""str: IP address of the container""" | |
return self.network_details.get("IPAddress", "") | |
@property | |
def ip_address(self) -> str: | |
"""str: IP address of the container""" | |
return self.network_details[0].get("IPAddress", "") |
working_dir=os.getcwd(), | ||
tty=True, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the above containers.create
, this is where you'll iterate through the networks and Network.connect(container)
.
Then container.start()
networks = client.networks.list(ids=backup_networks) | |
for network in networks: | |
network.connect(container) | |
container.start() | |
environment: dict = None, | ||
labels: dict = None, | ||
source_container_id: str = None, | ||
source_container_network: str = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to pass in the list of networks for the runner to be attached to so it can reach all the containers it is backing up.
source_container_network: str = None, | |
source_container_network: str = None, | |
backup_networks: list = None, |
volumes=volumes, | ||
environment=containers.this_container.environment, | ||
source_container_id=containers.this_container.id, | ||
source_container_network=containers.this_container.network_name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll have to pass the backup_networks
in here from the suggested new RunningContainers.networks_for_backup
method
source_container_network=containers.this_container.network_name, | |
source_container_network=containers.this_container.network_name, | |
backup_networks=containers.networks_for_backup(), |
def containers_for_backup(self): | ||
"""Obtain all containers with backup enabled""" | ||
return [container for container in self.containers if container.backup_enabled] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to have a networks_for_backup
method to get all of the networks needed for the backup runner.
def networks_for_backup(self) -> list: | |
"""Obtain all networks that have database containers to backup""" | |
networks = set() | |
for container in self.containers_for_backup(): | |
if container.database_backup_enabled: | |
for network in container.network_details: | |
networks.add(network.get("NetworkID", "")) | |
return list(networks) | |
logger.info("Starting backup container") | ||
client = utils.docker_client() | ||
|
||
container = client.containers.run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change this to client.containers.create
so that we can do Network.connect
to add the networks it needs before starting the backup process.
container = client.containers.create( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, cool project
Contents
Podman Hostname issue
I was running into this issue and found it more reliable to use the hostname from the socket library instead.
Podman network issue
This was triggered when the internal backup container is created. Because it was trying to use the main backup containers network, without a pod being specified, and not started as part of the same compose file. My solution for this was to pass through the network name of the original container and connect to that. I also switch the DB connection to use the container IP address.