1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
from shlex import quote
from pathlib import Path
from bundlewrap.items import Item
S6RC_ROOT = "/etc/s6-rc"
def generate_request_path(template, service) -> str:
return Path(S6RC_ROOT, "generate", f"{template}.{service}").as_posix()
def bundle_dependency_path(bundle, service) -> str:
return Path(S6RC_ROOT, "src", bundle, "contents.d", service).as_posix()
def have_generate_template(node, template) -> bool:
template_path = Path(S6RC_ROOT, "templates", template)
try:
result = node.run(
f"test -d {quote(template_path.as_posix())}",
)
return result.return_code == 0
except:
return False
def is_service_generated(node, template, service) -> bool:
try:
result = node.run(
f"test -f {quote(generate_request_path(template, service))}",
)
return result.return_code == 0
except:
return False
def generate_service(node, template, service) -> bool:
"""
This doesn't really generate the service from the template. Rather it
requests the s6-rc-generate target to do it on boot.
"""
result = node.run(
f"""
touch {quote(generate_request_path(template, service))}
sv reload
"""
)
return result.return_code == 0
def is_service_registered(node, bundle, service) -> bool:
try:
result = node.run(
f"test -f {quote(bundle_dependency_path(bundle, service))}",
)
return result.return_code == 0
except:
return False
def register_service(node, bundle, service) -> bool:
result = node.run(
f"""
touch {quote(bundle_dependency_path(bundle, service))} &&
sv reload
"""
)
return result.return_code == 0
def is_service_running(node, service) -> bool:
try:
result = node.run(
f"sv ls | grep -q {quote(service)}",
)
return result.return_code == 0
except:
return False
def run_service(node, service) -> bool:
result = node.run(
f"sv up {quote(service)}",
)
return result.return_code == 0
class SvcS6RC(Item):
"""
A service managed by s6-rc.
"""
BUNDLE_ATTRIBUTE_NAME = "svc_s6rc"
ITEM_ATTRIBUTES = {
"bundle": "default",
"generate": None,
"running": True,
}
ITEM_TYPE_NAME = "svc_s6rc"
def __repr__(self):
return "<SvcS6RC name:{} running:{} bundle:{} generate:{}>".format(
self.name,
self.attributes["running"],
self.attributes["bundle"],
self.attributes["generate"],
)
def fix(self, status):
if status.correct:
return
if self.attributes["generate"]:
if not have_generate_template(self.node, self.attributes["generate"]):
raise Exception(f"No template provides `{self.attributes["generate"]}`")
if status.cdict["generated"] != status.sdict["generated"]:
generate_service(self.node, self.attributes["generate"], self.name)
if status.cdict["registered"] != status.sdict["registered"]:
register_service(self.node, self.attributes["bundle"], self.name)
if status.cdict["running"] != status.sdict["running"]:
run_service(self.node, self.name)
def get_canned_actions(self):
return {
"reload": {
"command": f"sv reload",
},
"stop": {
"command": f"sv dn {quote(self.name)}",
"needed_by": {self.id},
},
"restart": {
"command": f"sv re {quote(self.name)}",
"needs": {self.id},
},
}
@property
def expected_state(self):
return {
"running": self.attributes["running"],
"generated": self.attributes["generate"] != None,
"registered": self.attributes["bundle"] != None,
}
@property
def actual_state(self):
return {
"running": is_service_running(self.node, self.name),
"generated": (
is_service_generated(self.node, self.attributes["generate"], self.name)
if self.attributes["generate"] != None
else False
),
"registered": is_service_registered(
self.node, self.attributes["bundle"], self.name
),
}
# TODO: validate attributes
|