LEFT | RIGHT |
(no file at all) | |
1 import os | 1 import os |
2 import shutil | 2 import shutil |
3 import logging | 3 import logging |
4 | 4 |
5 import juju | 5 import juju |
6 | 6 |
7 from twisted.internet.defer import inlineCallbacks, returnValue | 7 from twisted.internet.defer import inlineCallbacks, returnValue |
8 | 8 |
9 from juju.charm.bundle import CharmBundle | 9 from juju.charm.bundle import CharmBundle |
10 from juju.errors import ServiceError | 10 from juju.errors import ServiceError |
11 from juju.lib.lxc import LXCContainer, get_containers, LXCError | 11 from juju.lib.lxc import LXCContainer, get_containers |
12 from juju.lib.twistutils import get_module_directory | 12 from juju.lib.twistutils import get_module_directory |
13 from juju.lib.upstart import UpstartService | 13 from juju.lib.upstart import UpstartService |
14 from juju.providers.common.cloudinit import CloudInit | 14 from juju.providers.common.cloudinit import CloudInit |
15 | 15 |
16 from .errors import UnitDeploymentError | 16 from .errors import UnitDeploymentError |
17 | 17 |
18 log = logging.getLogger("unit.deploy") | 18 log = logging.getLogger("unit.deploy") |
19 | 19 |
20 | 20 |
21 def get_deploy_factory(provider_type): | 21 def get_deploy_factory(provider_type): |
22 if provider_type == "local": | 22 if provider_type == "local": |
23 return UnitContainerDeployment | 23 return UnitContainerDeployment |
24 elif provider_type == "subordinate": | 24 elif provider_type == "subordinate": |
25 return SubordinateContainerDeployment | 25 return SubordinateContainerDeployment |
26 | 26 |
27 return UnitMachineDeployment | 27 return UnitMachineDeployment |
28 | 28 |
29 | 29 |
30 def _get_environment(unit_name, juju_home, machine_id, zookeeper_hosts): | 30 def _get_environment(unit_name, juju_home, machine_id, zookeeper_hosts, base=Non
e): |
31 environ = dict() | 31 if not base: |
32 environ["JUJU_MACHINE_ID"] = str(machine_id) | 32 base = {} |
33 environ["JUJU_UNIT_NAME"] = unit_name | 33 base["JUJU_MACHINE_ID"] = str(machine_id) |
34 environ["JUJU_HOME"] = juju_home | 34 base["JUJU_UNIT_NAME"] = unit_name |
35 environ["JUJU_ZOOKEEPER"] = zookeeper_hosts | 35 base["JUJU_HOME"] = juju_home |
36 environ["PYTHONPATH"] = ":".join( | 36 base["JUJU_ZOOKEEPER"] = zookeeper_hosts |
| 37 base["PYTHONPATH"] = ":".join( |
37 filter(None, [ | 38 filter(None, [ |
38 os.path.dirname(get_module_directory(juju)), | 39 os.path.dirname(get_module_directory(juju)), |
39 os.environ.get("PYTHONPATH")])) | 40 os.environ.get("PYTHONPATH")])) |
40 return environ | 41 return base |
41 | 42 |
42 | 43 |
43 class UnitMachineDeployment(object): | 44 class ExtraEnvironmentDeployment(object): |
| 45 |
| 46 def __init__(self): |
| 47 self._extra_environment = {} |
| 48 |
| 49 def set_env(self, name, value): |
| 50 self._extra_environment[name] = value |
| 51 |
| 52 def unset_env(self, name): |
| 53 del self._extra_environment[name] |
| 54 |
| 55 |
| 56 class UnitMachineDeployment(ExtraEnvironmentDeployment): |
44 """ Deploy a unit directly onto a machine. | 57 """ Deploy a unit directly onto a machine. |
45 | 58 |
46 A service unit deployed directly to a machine has full access | 59 A service unit deployed directly to a machine has full access |
47 to the machine resources. | 60 to the machine resources. |
48 | 61 |
49 Units deployed in such a manner have no isolation from other units | 62 Units deployed in such a manner have no isolation from other units |
50 on the machine, and may leave artifacts on the machine even upon | 63 on the machine, and may leave artifacts on the machine even upon |
51 service destruction. | 64 service destruction. |
52 """ | 65 """ |
53 | 66 |
54 unit_agent_module = "juju.agents.unit" | 67 unit_agent_module = "juju.agents.unit" |
55 | 68 |
56 def __init__(self, unit_name, juju_home): | 69 def __init__(self, unit_name, juju_home): |
| 70 super(UnitMachineDeployment, self).__init__() |
57 assert ".." not in unit_name, "Invalid Unit Name" | 71 assert ".." not in unit_name, "Invalid Unit Name" |
58 self.unit_name = unit_name | 72 self.unit_name = unit_name |
59 self.juju_home = juju_home | 73 self.juju_home = juju_home |
60 self.unit_path_name = unit_name.replace("/", "-") | 74 self.unit_path_name = unit_name.replace("/", "-") |
61 self.directory = os.path.join( | 75 self.directory = os.path.join( |
62 self.juju_home, "units", self.unit_path_name) | 76 self.juju_home, "units", self.unit_path_name) |
63 self.service = UpstartService( | 77 self.service = UpstartService( |
64 # NOTE: we need use_sudo to work correctly during tests that | 78 # NOTE: we need use_sudo to work correctly during tests that |
65 # launch actual processes (rather than just mocking/trusting). | 79 # launch actual processes (rather than just mocking/trusting). |
66 "juju-%s" % self.unit_path_name, use_sudo=True) | 80 "juju-%s" % self.unit_path_name, use_sudo=True) |
67 | 81 |
68 @inlineCallbacks | 82 @inlineCallbacks |
69 def start(self, machine_id, zookeeper_hosts, bundle): | 83 def start(self, machine_id, zookeeper_hosts, bundle): |
70 """Start a service unit agent.""" | 84 """Start a service unit agent.""" |
71 self.unpack_charm(bundle) | 85 self.unpack_charm(bundle) |
72 self.service.set_description( | 86 self.service.set_description( |
73 "Juju unit agent for %s" % self.unit_name) | 87 "Juju unit agent for %s" % self.unit_name) |
74 self.service.set_environ(_get_environment( | 88 self.service.set_environ(_get_environment( |
75 self.unit_name, self.juju_home, machine_id, zookeeper_hosts)) | 89 self.unit_name, self.juju_home, machine_id, zookeeper_hosts, self._e
xtra_environment)) |
76 self.service.set_command(" ".join(( | 90 self.service.set_command(" ".join(( |
77 "/usr/bin/python", "-m", self.unit_agent_module, | 91 "/usr/bin/python", "-m", self.unit_agent_module, |
78 "--nodaemon", | 92 "--nodaemon", |
79 "--logfile", os.path.join(self.directory, "charm.log"), | 93 "--logfile", os.path.join(self.directory, "charm.log"), |
80 "--session-file", | 94 "--session-file", |
81 "/var/run/juju/unit-%s-agent.zksession" % self.unit_path_name))) | 95 "/var/run/juju/unit-%s-agent.zksession" % self.unit_path_name))) |
82 try: | 96 try: |
83 yield self.service.start() | 97 yield self.service.start() |
84 except ServiceError as e: | 98 except ServiceError as e: |
85 raise UnitDeploymentError(str(e)) | 99 raise UnitDeploymentError(str(e)) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 self.juju_home = juju_home | 137 self.juju_home = juju_home |
124 self.unit_path_name = unit_name.replace("/", "-") | 138 self.unit_path_name = unit_name.replace("/", "-") |
125 self.directory = os.path.join( | 139 self.directory = os.path.join( |
126 self.juju_home, "units", self.unit_path_name) | 140 self.juju_home, "units", self.unit_path_name) |
127 self.service = UpstartService( | 141 self.service = UpstartService( |
128 # NOTE: we need use_sudo to work correctly during tests that | 142 # NOTE: we need use_sudo to work correctly during tests that |
129 # launch actual processes (rather than just mocking/trusting). | 143 # launch actual processes (rather than just mocking/trusting). |
130 "juju-%s" % self.unit_path_name, use_sudo=True) | 144 "juju-%s" % self.unit_path_name, use_sudo=True) |
131 | 145 |
132 | 146 |
133 class UnitContainerDeployment(object): | 147 class UnitContainerDeployment(ExtraEnvironmentDeployment): |
134 """Deploy a service unit in a container. | 148 """Deploy a service unit in a container. |
135 | 149 |
136 Units deployed in a container have strong isolation between | 150 Units deployed in a container have strong isolation between |
137 others units deployed in a container on the same machine. | 151 others units deployed in a container on the same machine. |
138 | 152 |
139 From the perspective of the service unit, the container deployment | 153 From the perspective of the service unit, the container deployment |
140 should be indistinguishable from a machine deployment. | 154 should be indistinguishable from a machine deployment. |
141 | 155 |
142 Note, strong isolation properties are still fairly trivial | 156 Note, strong isolation properties are still fairly trivial |
143 to escape for a user with a root account within the container. | 157 to escape for a user with a root account within the container. |
144 This is an ongoing development topic for LXC. | 158 This is an ongoing development topic for LXC. |
145 """ | 159 """ |
146 | 160 |
147 def __init__(self, unit_name, juju_home): | 161 def __init__(self, unit_name, juju_home): |
| 162 super(UnitContainerDeployment, self).__init__() |
148 self.unit_name = unit_name | 163 self.unit_name = unit_name |
149 self.juju_home = juju_home | 164 self.juju_home = juju_home |
150 self.unit_path_name = unit_name.replace("/", "-") | 165 self.unit_path_name = unit_name.replace("/", "-") |
151 | 166 |
152 self._juju_origin = os.environ.get("JUJU_ORIGIN") | 167 self._juju_origin = os.environ.get("JUJU_ORIGIN") |
153 self._juju_series = os.environ.get("JUJU_SERIES") | 168 self._juju_series = os.environ.get("JUJU_SERIES") |
154 assert self._juju_series is not None, "Required juju series not found" | 169 assert self._juju_series is not None, "Required juju series not found" |
155 self._unit_namespace = os.environ.get("JUJU_UNIT_NS") | 170 self._unit_namespace = os.environ.get("JUJU_UNIT_NS") |
156 assert self._unit_namespace is not None, "Required unit ns not found" | 171 assert self._unit_namespace is not None, "Required unit ns not found" |
157 self.container_name = "%s-%s" % ( | 172 self.container_name = "%s-%s" % ( |
(...skipping 24 matching lines...) Expand all Loading... |
182 authorized_keys = authorized_keys.strip("'\"") | 197 authorized_keys = authorized_keys.strip("'\"") |
183 | 198 |
184 cloud_init.add_ssh_key(authorized_keys) | 199 cloud_init.add_ssh_key(authorized_keys) |
185 | 200 |
186 zks = [] | 201 zks = [] |
187 for zk in zookeepers.split(','): | 202 for zk in zookeepers.split(','): |
188 if ':' in zk: | 203 if ':' in zk: |
189 (zk, port) = zk.split(':') | 204 (zk, port) = zk.split(':') |
190 else: | 205 else: |
191 port = 2181 | 206 port = 2181 |
192 zks.append((zk,port)) | 207 zks.append((zk, port)) |
193 | 208 |
194 cloud_init.set_zookeeper_hosts(zks) | 209 cloud_init.set_zookeeper_hosts(zks) |
195 # XXX Very hard to access the provider's notion of network | 210 # XXX Very hard to access the provider's notion of network |
196 # or even env configs, so just assume the first ZK is running | 211 # or even env configs, so just assume the first ZK is running |
197 # the apt-cacher-ng since this is meant for local provider. | 212 # the apt-cacher-ng since this is meant for local provider. |
198 cloud_init.set_apt_proxy('http://%s:3142' % zks[0][0]) | 213 cloud_init.set_apt_proxy('http://%s:3142' % zks[0][0]) |
| 214 |
| 215 for name, value in self._extra_environment.iteritems(): |
| 216 cloud_init.set_env(name, value) |
199 | 217 |
200 if self._juju_origin: | 218 if self._juju_origin: |
201 if self._juju_origin.startswith("lp:"): | 219 if self._juju_origin.startswith("lp:"): |
202 cloud_init.set_juju_source(branch=self._juju_origin) | 220 cloud_init.set_juju_source(branch=self._juju_origin) |
203 elif self._juju_origin == "ppa": | 221 elif self._juju_origin == "ppa": |
204 cloud_init.set_juju_source(ppa=True) | 222 cloud_init.set_juju_source(ppa=True) |
205 elif self._juju_origin == "proposed": | 223 elif self._juju_origin == "proposed": |
206 cloud_init.set_juju_source(proposed=True) | 224 cloud_init.set_juju_source(proposed=True) |
| 225 elif self._juju_origin == "image": |
| 226 cloud_init.set_juju_source(image=True) |
207 else: | 227 else: |
208 # Ignore other values, just use the distro for sanity | 228 # Ignore other values, just use the distro for sanity |
209 cloud_init.set_juju_source(distro=True) | 229 cloud_init.set_juju_source(distro=True) |
210 cloud_init.set_unit_name(self.unit_name) | 230 cloud_init.set_unit_name(self.unit_name) |
211 cloud_init.set_juju_home(self._container_juju_home) | 231 cloud_init.set_juju_home(self._container_juju_home) |
212 return cloud_init | 232 return cloud_init |
213 | 233 |
214 @inlineCallbacks | 234 @inlineCallbacks |
215 def _get_container(self, machine_id, cloud_init): | 235 def _get_container(self, machine_id, cloud_init): |
216 container = LXCContainer( | 236 container = LXCContainer( |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 @inlineCallbacks | 307 @inlineCallbacks |
288 def is_running(self): | 308 def is_running(self): |
289 """Is the unit container running?""" | 309 """Is the unit container running?""" |
290 # TODO: container running may not imply agent running. | 310 # TODO: container running may not imply agent running. |
291 # query zookeeper for the unit agent presence node? | 311 # query zookeeper for the unit agent presence node? |
292 if not self.container: | 312 if not self.container: |
293 returnValue(False) | 313 returnValue(False) |
294 container_map = yield get_containers( | 314 container_map = yield get_containers( |
295 prefix=self.container.container_name) | 315 prefix=self.container.container_name) |
296 returnValue(container_map.get(self.container.container_name, False)) | 316 returnValue(container_map.get(self.container.container_name, False)) |
LEFT | RIGHT |