Skip to content

Commit 5dacf8b

Browse files
Merge pull request #521 from inknos/implement-update-healthcheck
Implement container.update()
2 parents 7e307b2 + 8db8d12 commit 5dacf8b

File tree

4 files changed

+270
-8
lines changed

4 files changed

+270
-8
lines changed

.packit.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ jobs:
120120
packages: [python-podman-fedora]
121121
targets:
122122
- fedora-all
123+
tf_extra_params:
124+
environments:
125+
- artifacts:
126+
- type: repository-file
127+
id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/fedora-$releasever/rhcontainerbot-podman-next-fedora-$releasever.repo
128+
123129

124130
- job: tests
125131
trigger: pull_request

plans/main.fmf

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,18 @@ prepare:
99
how: install
1010
package:
1111
- make
12-
- podman
1312
- python3-pip
13+
- podman
14+
15+
- name: enable rhcontainerbot/podman-next update podman
16+
when: initiator == packit
17+
how: shell
18+
script: |
19+
COPR_REPO_FILE="/etc/yum.repos.d/*podman-next*.repo"
20+
if compgen -G $COPR_REPO_FILE > /dev/null; then
21+
sed -i -n '/^priority=/!p;$apriority=1' $COPR_REPO_FILE
22+
fi
23+
dnf -y upgrade --allowerasing
1424

1525
- name: pip dependencies
1626
how: shell

podman/domain/containers.py

Lines changed: 220 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import json
44
import logging
55
import shlex
6+
from collections.abc import Iterable, Iterator, Mapping
67
from contextlib import suppress
78
from typing import Any, Optional, Union
8-
from collections.abc import Iterable, Iterator, Mapping
99

1010
import requests
1111

@@ -506,13 +506,228 @@ def unpause(self) -> None:
506506
response = self.client.post(f"/containers/{self.id}/unpause")
507507
response.raise_for_status()
508508

509-
def update(self, **kwargs):
509+
def update(self, **kwargs) -> None:
510510
"""Update resource configuration of the containers.
511+
Keyword Args:
512+
Please refer to Podman API documentation for details:
513+
https://docs.podman.io/en/latest/_static/api.html#tag/containers/operation/ContainerUpdateLibpod
514+
515+
restart_policy (str): New restart policy for the container.
516+
restart_retries (int): New amount of retries for the container's restart policy.
517+
Only allowed if restartPolicy is set to on-failure
518+
519+
blkio_weight_device tuple(str, int):Block IO weight (relative device weight)
520+
in the form: (device_path, weight)
521+
blockio (dict): LinuxBlockIO for Linux cgroup 'blkio' resource management
522+
Example:
523+
blockio = {
524+
"leafWeight": 0
525+
"throttleReadBpsDevice": [{
526+
"major": 0,
527+
"minor": 0,
528+
"rate": 0
529+
}],
530+
"throttleReadIopsDevice": [{
531+
"major": 0,
532+
"minor": 0,
533+
"rate": 0
534+
}],
535+
"throttleWriteBpsDevice": [{
536+
"major": 0,
537+
"minor": 0,
538+
"rate": 0
539+
}],
540+
"throttleWriteIopsDevice": [{
541+
"major": 0,
542+
"minor": 0,
543+
"rate": 0
544+
}],
545+
"weight": 0,
546+
"weightDevice": [{
547+
"leafWeight": 0,
548+
"major": 0,
549+
"minor": 0,
550+
"weight": 0
551+
}],
552+
}
553+
cpu (dict): LinuxCPU for Linux cgroup 'cpu' resource management
554+
Example:
555+
cpu = {
556+
"burst": 0,
557+
"cpus": "string",
558+
"idle": 0,
559+
"mems": "string",
560+
"period": 0
561+
"quota": 0,
562+
"realtimePeriod": 0,
563+
"realtimeRuntime": 0,
564+
"shares": 0
565+
}
566+
device_read_bps (list(dict)): Limit read rate (bytes per second) from a device,
567+
in the form: [{"Path": "string", "Rate": 0}]
568+
device_read_iops (list(dict)): Limit read rate (IO operations per second) from a device,
569+
in the form: [{"Path": "string", "Rate": 0}]
570+
device_write_bps (list(dict)): Limit write rate (bytes per second) to a device,
571+
in the form: [{"Path": "string", "Rate": 0}]
572+
device_write_iops (list(dict)): Limit write rate (IO operations per second) to a device,
573+
in the form: [{"Path": "string", "Rate": 0}]
574+
devices (list(dict)): Devices configures the device allowlist.
575+
Example:
576+
devices = [{
577+
access: "string"
578+
allow: 0,
579+
major: 0,
580+
minor: 0,
581+
type: "string"
582+
}]
583+
health_cmd (str): set a healthcheck command for the container ('None' disables the
584+
existing healthcheck)
585+
health_interval (str): set an interval for the healthcheck (a value of disable results
586+
in no automatic timer setup)(Changing this setting resets timer.) (default "30s")
587+
health_log_destination (str): set the destination of the HealthCheck log. Directory
588+
path, local or events_logger (local use container state file)(Warning: Changing
589+
this setting may cause the loss of previous logs.) (default "local")
590+
health_max_log_count (int): set maximum number of attempts in the HealthCheck log file.
591+
('0' value means an infinite number of attempts in the log file) (default 5)
592+
health_max_logs_size (int): set maximum length in characters of stored HealthCheck log.
593+
('0' value means an infinite log length) (default 500)
594+
health_on_failure (str): action to take once the container turns unhealthy
595+
(default "none")
596+
health_retries (int): the number of retries allowed before a healthcheck is considered
597+
to be unhealthy (default 3)
598+
health_start_period (str): the initialization time needed for a container to bootstrap
599+
(default "0s")
600+
health_startup_cmd (str): Set a startup healthcheck command for the container
601+
health_startup_interval (str): Set an interval for the startup healthcheck. Changing
602+
this setting resets the timer, depending on the state of the container.
603+
(default "30s")
604+
health_startup_retries (int): Set the maximum number of retries before the startup
605+
healthcheck will restart the container
606+
health_startup_success (int): Set the number of consecutive successes before the
607+
startup healthcheck is marked as successful and the normal healthcheck begins
608+
(0 indicates any success will start the regular healthcheck)
609+
health_startup_timeout (str): Set the maximum amount of time that the startup
610+
healthcheck may take before it is considered failed (default "30s")
611+
health_timeout (str): the maximum time allowed to complete the healthcheck before an
612+
interval is considered failed (default "30s")
613+
no_healthcheck (bool): Disable healthchecks on container
614+
hugepage_limits (list(dict)): Hugetlb limits (in bytes).
615+
Default to reservation limits if supported.
616+
Example:
617+
huugepage_limits = [{"limit": 0, "pageSize": "string"}]
618+
memory (dict): LinuxMemory for Linux cgroup 'memory' resource management
619+
Example:
620+
memory = {
621+
"checkBeforeUpdate": True,
622+
"disableOOMKiller": True,
623+
"kernel": 0,
624+
"kernelTCP": 0,
625+
"limit": 0,
626+
"reservation": 0,
627+
"swap": 0,
628+
"swappiness": 0,
629+
"useHierarchy": True,
630+
}
631+
network (dict): LinuxNetwork identification and priority configuration
632+
Example:
633+
network = {
634+
"classID": 0,
635+
"priorities": {
636+
"name": "string",
637+
"priority": 0
638+
}
639+
)
640+
pids (dict): LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3)
641+
Example:
642+
pids = {
643+
"limit": 0
644+
}
645+
rdma (dict): Rdma resource restriction configuration. Limits are a set of key value
646+
pairs that define RDMA resource limits, where the key is device name and value
647+
is resource limits.
648+
Example:
649+
rdma = {
650+
"property1": {
651+
"hcaHandles": 0
652+
"hcaObjects": 0
653+
},
654+
"property2": {
655+
"hcaHandles": 0
656+
"hcaObjects": 0
657+
},
658+
...
659+
}
660+
unified (dict): Unified resources.
661+
Example:
662+
unified = {
663+
"property1": "value1",
664+
"property2": "value2",
665+
...
666+
}
511667
512-
Raises:
513-
NotImplementedError: Podman service unsupported operation.
514668
"""
515-
raise NotImplementedError("Container.update() is not supported by Podman service.")
669+
670+
data = {}
671+
params = {}
672+
673+
health_commands_data = [
674+
"health_cmd",
675+
"health_interval",
676+
"health_log_destination",
677+
"health_max_log_count",
678+
"health_max_logs_size",
679+
"health_on_failure",
680+
"health_retries",
681+
"health_start_period",
682+
"health_startup_cmd",
683+
"health_startup_interval",
684+
"health_startup_retries",
685+
"health_startup_success",
686+
"health_startup_timeout",
687+
"health_timeout",
688+
]
689+
# the healthcheck section of parameters accepted can be either no_healthcheck or a series
690+
# of healthcheck parameters
691+
if kwargs.get("no_healthcheck"):
692+
for command in health_commands_data:
693+
if command in kwargs:
694+
raise ValueError(f"Cannot set {command} when no_healthcheck is True")
695+
data["no_healthcheck"] = kwargs.get("no_healthcheck")
696+
else:
697+
for hc in health_commands_data:
698+
if hc in kwargs:
699+
data[hc] = kwargs.get(hc)
700+
701+
data_mapping = {
702+
"BlkIOWeightDevice": "blkio_weight_device",
703+
"blockio": "blockIO",
704+
"cpu": "cpu",
705+
"device_read_bps": "DeviceReadBPs",
706+
"device_read_iops": "DeviceReadIOps",
707+
"device_write_bps": "DeviceWriteBPs",
708+
"device_write_iops": "DeviceWriteIOps",
709+
"devices": "devices",
710+
"hugepage_limits": "hugepageLimits",
711+
"memory": "memory",
712+
"network": "network",
713+
"pids": "pids",
714+
"rdma": "rdma",
715+
"unified": "unified",
716+
}
717+
for kwarg_key, data_key in data_mapping.items():
718+
value = kwargs.get(kwarg_key)
719+
if value is not None:
720+
data[data_key] = value
721+
722+
if kwargs.get("restart_policy"):
723+
params["restartPolicy"] = kwargs.get("restart_policy")
724+
if kwargs.get("restart_retries"):
725+
params["restartRetries"] = kwargs.get("restart_retries")
726+
727+
response = self.client.post(
728+
f"/containers/{self.id}/update", params=params, data=json.dumps(data)
729+
)
730+
response.raise_for_status()
516731

517732
def wait(self, **kwargs) -> int:
518733
"""Block until the container enters given state.

podman/tests/integration/test_containers.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import io
22
import random
33
import tarfile
4-
import unittest
54
import tempfile
5+
import unittest
66

77
try:
88
# Python >= 3.10
@@ -17,7 +17,6 @@
1717
from podman.domain.images import Image
1818
from podman.errors import NotFound
1919

20-
2120
# @unittest.skipIf(os.geteuid() != 0, 'Skipping, not running as root')
2221

2322

@@ -238,6 +237,38 @@ def test_container_labels(self):
238237
labeled_container.remove(v=True)
239238
unlabeled_container.remove(v=True)
240239

240+
def test_container_update(self):
241+
"""Update container"""
242+
to_update_container = self.client.containers.run(
243+
self.alpine_image, name="to_update_container", detach=True
244+
)
245+
with self.subTest("Test container update changing the healthcheck"):
246+
to_update_container.update(health_cmd="ls")
247+
self.assertEqual(
248+
to_update_container.inspect()['Config']['Healthcheck']['Test'], ['CMD-SHELL', 'ls']
249+
)
250+
251+
with self.subTest("Test container update disabling the healthcheck"):
252+
to_update_container.update(no_healthcheck=True)
253+
self.assertEqual(
254+
to_update_container.inspect()['Config']['Healthcheck']['Test'], ['NONE']
255+
)
256+
with self.subTest("Test container update passing payload and data"):
257+
to_update_container.update(
258+
restart_policy="always", health_cmd="echo", health_timeout="10s"
259+
)
260+
self.assertEqual(
261+
to_update_container.inspect()['Config']['Healthcheck']['Test'],
262+
['CMD-SHELL', 'echo'],
263+
)
264+
self.assertEqual(
265+
to_update_container.inspect()['Config']['Healthcheck']['Timeout'], 10000000000
266+
)
267+
self.assertEqual(
268+
to_update_container.inspect()['HostConfig']['RestartPolicy']['Name'], 'always'
269+
)
270+
to_update_container.remove(v=True)
271+
241272

242273
if __name__ == '__main__':
243274
unittest.main()

0 commit comments

Comments
 (0)