def update(self, service_name, image_id=None, scale=None): """ update the given service with the given image :param image_id: :return: """ logger.info("upgrading %s to %s scale=%s", service_name, image_id, scale) service = self._get(service_name=service_name) attrs = {} if image_id is not None: attrs['image'] = image_id if scale is not None: if scale == -1: attrs['mode'] = ServiceMode('global', 1) else: attrs['mode'] = ServiceMode('replicated', scale) service.update(fetch_current_spec=True, **attrs)
def _service_mode(self, mode: Dict) -> None: """ Converts a couple of parameters - deploy['mode'] and deploy['replicas'] into a ServiceMode class as expected by docker-py lib. Defaults to a 'replicated' service with 1 replica. """ svc_mode = mode.get('mode') or Modes.REPLICATED svc_replicas = mode.get('replicas') or 1 \ if svc_mode != Modes.GLOBAL else None self.mode = ServiceMode(mode=svc_mode, replicas=svc_replicas)
def _send_signal_in_swarm(self): current_container_id = get_current_container_id() if not current_container_id: return current_container = self.client.containers.get(current_container_id) if not current_container: return image = current_container.attrs['Config'].get('Image') if not image: return command = [sys.executable, __file__, '--label', self.label_name] log_driver = current_container.attrs['HostConfig']['LogConfig']['Type'] log_config = current_container.attrs['HostConfig']['LogConfig'][ 'Config'] sender = self.client.services.create( image=image, command=command, name='domain-automation-signal-%d-%d' % (int(time.time() * 1000), random.randint(100, 999)), env=['PYTHONPATH=%s' % os.environ.get('PYTHONPATH', '.')], log_driver=log_driver, log_driver_options=log_config, mode=ServiceMode('global'), restart_policy=RestartPolicy(condition='none', max_attempts=0), mounts=['/var/run/docker.sock:/var/run/docker.sock:ro']) max_wait = 60 start_time = time.time() while abs(time.time() - start_time) < max_wait: if all(task['DesiredState'] == 'shutdown' for task in sender.tasks()): break time.sleep(1) states = list(task['Status']['State'] for task in sender.tasks()) logs = ''.join( item.decode() if hasattr(item, 'decode') else item for item in sender.logs(stdout=True, stderr=True)).strip() sender.remove() logger.info('Signalled containers with label %s - result: %s' % (self.label_name, ', '.join(map(str, states)))) if logs: logger.info('Signal logs: %s' % logs)
def test_scale_method_global_service(self): client = docker.from_env(version=TEST_API_VERSION) mode = ServiceMode('global') service = client.services.create(name=helpers.random_name(), image="alpine", command="sleep 300", mode=mode) tasks = [] while len(tasks) == 0: tasks = service.tasks() assert len(tasks) == 1 with pytest.raises(InvalidArgument): service.scale(2) assert len(tasks) == 1 service.reload() spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec'] assert spec.get('Command') == ['sleep', '300']
def deploy_VDU(self, name, sw_image, networks, placement_policy, app="sleep infinity", arguments=None, endpoint=None, mode="replicated", replicas=1): """ :param name: :param sw_image: :param networks: :param placement_policy: :param mode: :param replicas: :return: """ placement_constraints = [] if placement_policy is not None: for constraint in placement_policy: label, value = constraint.split("==") label = "node.labels." + label placement_constraints.append(label + "==" + value) oneRep = ServiceMode(mode, replicas) service_port = None if endpoint is not None: service_port = docker.types.EndpointSpec( mode='vip', ports={endpoint: endpoint}) docker_vdu = client.services.create(sw_image, command=app, args=arguments, name=name, networks=networks, mode=oneRep, constraints=placement_constraints, endpoint_spec=service_port)
def configure(cls, image, service, secrets=None, mounts=None, **kwargs): self = ServiceKwargs() options = service.options deploy_opts = options.get("deploy", {}) prefs = deploy_opts.get("placement", {}).get("preferences", {}) # Map compose options to service options self.image = image self.constraints = deploy_opts.get("placement", {}).get("constraints") self.preferences = [kv for pref in prefs for kv in pref.items()] self.container_labels = options.get("labels") self.endpoint_spec = EndpointSpec( deploy_opts.get("endpoint_mode"), {p.published: p.target for p in options.get("ports", [])}) self.env = options.get("environment", None) self.hostname = options.get("hostname") self.isolation = options.get("isolation") self.labels = { k: v for k, v in (kv.split('=') for kv in deploy_opts.get("labels", [])) } self.log_driver = options.get("logging", {}).get("driver") self.log_driver_options = options.get("logging", {}).get("options") self.mode = ServiceMode(deploy_opts.get("mode", "replicated"), deploy_opts.get("replicas", 1)) self.networks = [ config.SWARM_NETWORK ] # Similar to mounts. I don't see the use case but see the issues resource_opts = deploy_opts.get("resources", {}) if resource_opts: # Unpack any generic_resources defined i.e. gpus and such reservation_opts = resource_opts.get("reservations", {}) generic_resources = {} for generic_resource in reservation_opts.get( "generic_resources", {}): discrete_resource_spec = generic_resource[ "discrete_resource_spec"] generic_resources[discrete_resource_spec[ "kind"]] = discrete_resource_spec["value"] cpu_limit = sfloat(resource_opts.get("limits", {}).get("cpus"), 0) cpu_reservation = sfloat(reservation_opts.get("cpus"), 0) nano_cpu_limit = sint(cpu_limit * 1e9, 0) if cpu_limit is not None else None nano_cpu_reservation = sint( cpu_reservation * 1e9, 0) if cpu_reservation is not None else None self.resources = Resources( cpu_limit=nano_cpu_limit, mem_limit=parse_bytes( resource_opts.get("limits", {}).get("memory", '')), cpu_reservation=nano_cpu_reservation, mem_reservation=parse_bytes(reservation_opts.get("memory", '')), generic_resources=generic_resources) restart_opts = deploy_opts.get("restart_policy", {}) if restart_opts: # Parse the restart policy delay = timeparse(restart_opts.get("delay", "0s")) window = timeparse(restart_opts.get("restart_opts", "0s")) self.restart_policy = RestartPolicy( condition=restart_opts.get("condition", ), delay=delay, max_attempts=sint(restart_opts.get("max_attempts", 0), 0), window=window) self.secrets = secrets self.mounts = mounts # Grab any key word arguments that may have been given [setattr(self, k, v) for k, v in kwargs.items() if hasattr(self, k)] service_kwargs = _get_create_service_kwargs('create', copy.copy(self.__dict__)) # This is needed because aiodocker assumes the Env is a dictionary for some reason... if self.env is not None: service_kwargs["task_template"]["ContainerSpec"]["Env"] = self.env return service_kwargs
def configure(self, service, secrets=None): """ Parameters: image (str) – The image name to use for the containers. command (list of str or str) – Command to run. args (list of str) – Arguments to the command. constraints (list of str) – Placement constraints. preferences (list of tuple) – Placement preferences. platforms (list of tuple) – A list of platform constraints expressed as (arch, os) tuples. container_labels (dict) – Labels to apply to the container. endpoint_spec (EndpointSpec) – Properties that can be configured to access and load balance a service. Default: None. env (list of str) – Environment variables, in the form KEY=val. hostname (string) – Hostname to set on the container. isolation (string) – Isolation technology used by the service’s containers. Only used for Windows containers. labels (dict) – Labels to apply to the service. log_driver (str) – Log driver to use for containers. log_driver_options (dict) – Log driver options. mode (ServiceMode) – Scheduling mode for the service. Default:None mounts (list of str) – Mounts for the containers, in the form source:target:options, where options is either ro or rw. name (str) – Name to give to the service. networks (list of str) – List of network names or IDs to attach the service to. Default: None. resources (Resources) – Resource limits and reservations. restart_policy (RestartPolicy) – Restart policy for containers. secrets (list of docker.types.SecretReference) – List of secrets accessible to containers for this service. stop_grace_period (int) – Amount of time to wait for containers to terminate before forcefully killing them. update_config (UpdateConfig) – Specification for the update strategy of the service. Default: None rollback_config (RollbackConfig) – Specification for the rollback strategy of the service. Default: None user (str) – User to run commands as. workdir (str) – Working directory for commands to run. tty (boolean) – Whether a pseudo-TTY should be allocated. groups (list) – A list of additional groups that the container process will run as. open_stdin (boolean) – Open stdin read_only (boolean) – Mount the container’s root filesystem as read only. stop_signal (string) – Set signal to stop the service’s containers healthcheck (Healthcheck) – Healthcheck configuration for this service. hosts (dict) – A set of host to IP mappings to add to the container’s hosts file. dns_config (DNSConfig) – Specification for DNS related configurations in resolver configuration file. configs (list) – List of ConfigReference that will be exposed to the service. privileges (Privileges) – Security options for the service’s containers. """ options = service.options deploy_opts = options.get("deploy", {}) prefs = deploy_opts.get("placement", {}).get("preferences", {}) # Map compose options to service options self.constraints = deploy_opts.get("placement", {}).get("constraints") self.preferences = [kv for pref in prefs for kv in pref.items()] self.container_labels = options.get("labels") self.endpoint_spec = EndpointSpec( deploy_opts.get("endpoint_mode"), {p.published: p.target for p in options.get("ports", [])}) self.env = [f"{k}={v}" for k, v in options.get("environment").items()] self.hostname = options.get("hostname") self.isolation = options.get("isolation") self.labels = { k: v for k, v in (kv.split('=') for kv in deploy_opts.get("labels", [])) } self.log_driver = options.get("logging", {}).get("driver") self.log_driver_options = options.get("logging", {}).get("options") self.mode = ServiceMode(deploy_opts.get("mode", "replicated"), deploy_opts.get("replicas", 1)) self.mounts = None # I'm not sure we should allow mounting volumes to apps until I see a use case self.name = service.name self.networks = None # Similar to mounts. I don't see the use case but see the issues resource_opts = deploy_opts.get("resources", {}) if resource_opts: # Unpack any generic_resources defined i.e. gpus and such reservation_opts = resource_opts.get("reservations", {}) generic_resources = {} for generic_resource in reservation_opts.get( "generic_resources", {}): discrete_resource_spec = generic_resource[ "discrete_resource_spec"] generic_resources[discrete_resource_spec[ "kind"]] = discrete_resource_spec["value"] cpu_limit = sfloat(resource_opts.get("limits", {}).get("cpus")) cpu_reservation = sfloat(reservation_opts.get("cpus")) nano_cpu_limit = sint(cpu_limit * 1e9) if cpu_limit is not None else None nano_cpu_reservation = sint( cpu_reservation * 1e9) if cpu_reservation is not None else None self.resources = Resources( cpu_limit=nano_cpu_limit, mem_limit=parse_bytes( resource_opts.get("limits", {}).get("memory", '')), cpu_reservation=nano_cpu_reservation, mem_reservation=parse_bytes(reservation_opts.get("memory", '')), generic_resources=generic_resources) restart_opts = deploy_opts.get("restart_policy", {}) if restart_opts: # Parse the restart policy delay = timeparse.timeparse(restart_opts.get("delay", "0s")) window = timeparse.timeparse(restart_opts.get( "restart_opts", "0s")) self.restart_policy = RestartPolicy( condition=restart_opts.get("condition", ), delay=delay, max_attempts=sint(restart_opts.get("max_attempts", 0)), window=window) # self.secrets = [SecretReference(secret_id=s["secret"].uid, secret_name=s["secret"].name, filename=s.get("file", s["secret"].name), uid=s["secret"].uid, gid=s["secret"].gid, mode=s["secret"].mode) for s in service.secrets] # self.secrets = self.load_secrets(service) return { key: value for key, value in self.as_dict().items() if value is not None }
def test_service_global_mode(self): mode = {'mode': 'global'} svc = Service(name=self.svc_name, deploy=mode) svc_mode = ServiceMode('global') self.assertEquals(svc.mode, svc_mode)
def test_service_replicas(self): replicas = {'replicas': 3} svc = Service(name=self.svc_name, deploy=replicas) svc_mode = ServiceMode('replicated', 3) self.assertEquals(svc.mode, svc_mode)