Coverage for src/rsnapshot_docker_compose_backup/docker.py: 74%

92 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-07 01:03 +0200

1from dataclasses import dataclass 

2from enum import Enum, auto 

3from pathlib import Path 

4from typing import Any, Dict, List, Optional 

5 

6from rsnapshot_docker_compose_backup.structure.container import Container 

7from rsnapshot_docker_compose_backup.structure.volume import Volume 

8from rsnapshot_docker_compose_backup.utils.docker_api_client import Api 

9 

10 

11class ContainerState(Enum): 

12 CREATED = auto() 

13 RESTARTING = auto() 

14 RUNNING = auto() 

15 REMOVING = auto() 

16 PAUSED = auto() 

17 EXITED = auto() 

18 DEAD = auto() 

19 

20 @staticmethod 

21 def from_str(value: str) -> "ContainerState": 

22 for member in ContainerState: 

23 if member.name.lower() == value.lower(): 

24 return member 

25 raise ValueError("Unknown State: " + value) 

26 

27 

28class DockerMountType(Enum): 

29 VOLUME = auto() 

30 BIND = auto() 

31 NOT_SET = auto() 

32 

33 @staticmethod 

34 def from_str(value: str) -> "DockerMountType": 

35 for member in DockerMountType: 

36 if member.name.lower() == value.lower(): 

37 return member 

38 raise ValueError("Unknown Type: " + value) 

39 

40 

41@dataclass 

42class DockerMount: 

43 name: Optional[str] 

44 type: DockerMountType 

45 source: str 

46 destination: str 

47 driver: Optional[str] 

48 mode: str 

49 rw: bool 

50 propagation: str 

51 

52 @staticmethod 

53 def from_json(json_data: Any) -> "DockerMount": 

54 if "Name" in json_data: 

55 name = json_data["Name"] 

56 else: 

57 name = None 

58 if "Driver" in json_data: 

59 driver = json_data["Driver"] 

60 else: 

61 driver = None 

62 if "Type" in json_data: 

63 mount_type = DockerMountType.from_str(json_data["Type"]) 

64 else: 

65 mount_type = DockerMountType.NOT_SET 

66 return DockerMount( 

67 name=name, 

68 type=mount_type, 

69 source=json_data["Source"], 

70 destination=json_data["Destination"], 

71 driver=driver, 

72 mode=json_data["Mode"], 

73 rw=json_data["RW"], 

74 propagation=json_data["Propagation"], 

75 ) 

76 

77 

78@dataclass 

79class DockerInspect: 

80 id: str 

81 names: List[str] 

82 image: str 

83 image_id: str 

84 state: ContainerState 

85 mounts: List[DockerMount] 

86 labels: Dict[str, str] 

87 

88 @staticmethod 

89 def from_json(json_data: Any) -> "DockerInspect": 

90 print(json_data["Names"]) 

91 print(json_data["State"]) 

92 return DockerInspect( 

93 id=json_data["Id"], 

94 names=[x.lstrip("/") for x in json_data["Names"]], 

95 image=json_data["Image"], 

96 image_id=json_data["ImageID"], 

97 state=ContainerState.from_str(json_data["State"]), 

98 mounts=[DockerMount.from_json(x) for x in json_data["Mounts"]], 

99 labels=json_data["Labels"], 

100 ) 

101 

102 

103def inspect_container(container_id: str) -> DockerInspect: 

104 # converts docker inspect to json and return only first container, 

105 # because this works only with one container 

106 response = Api().get(f"/containers/{container_id}/json") 

107 return DockerInspect.from_json(response.json_body) 

108 

109 

110def get_container(socket_connection: Optional[str] = None) -> List[DockerInspect]: 

111 parameter = {"all": "true"} 

112 if socket_connection is None: 

113 api = Api() 

114 else: 

115 api = Api(socket_connection=socket_connection) 

116 response = api.get("/containers/json", query_parameter=parameter) 

117 return [DockerInspect.from_json(x) for x in response.json_body] 

118 

119 

120def _volumes(mounts: List[DockerMount]) -> List[Volume]: 

121 result: List[Volume] = [] 

122 for mount in mounts: 

123 if mount.type == DockerMountType.VOLUME: 

124 assert mount.name is not None 

125 result.append(Volume(mount.name, mount.source)) 

126 return sorted(result) 

127 

128 

129def get_compose_container() -> List[Container]: 

130 container_list: List[Container] = [] 

131 for container in get_container(): 

132 if "com.docker.compose.project" in container.labels: 

133 print(container.names[0]) 

134 print(container.state) 

135 container_list.append( 

136 Container( 

137 folder=Path( 

138 container.labels["com.docker.compose.project.working_dir"] 

139 ), 

140 container_id=container.id, 

141 container_name=container.names[0], # TODO convert to list 

142 running=container.state != ContainerState.EXITED, 

143 service_name=container.labels["com.docker.compose.service"], 

144 volumes=_volumes(container.mounts), 

145 image=container.image, 

146 docker_compose_file=Path( 

147 container.labels["com.docker.compose.project.config_files"] 

148 ), # TODO check if this can be multiple files 

149 ) 

150 ) 

151 return sorted(container_list)