def _run_port_mapper(cls, container, ports): network_name = f'{container.name}_network' guest_ip = container.attrs['NetworkSettings']['Networks'][ network_name]['IPAddress'] containers_names = [] for port in ports: # TODO: Use a single container for all port mappings instead of # spinning a container for each port name = f'{container.name}_port_mapper_{port}' cmd = f'TCP-LISTEN:1234,fork TCP-CONNECT:{guest_ip}:{port}' kwargs = { 'command': cmd, 'ports': { '1234': f'{port}/tcp' }, 'name': name, 'detach': True, 'auto_remove': True, 'network': network_name, } Client.get_instance().containers.run('alpine/socat', **kwargs) containers_names.append(name) return containers_names
def assertPortMapperExists(self, env_name, port): port_mapper_container_name = (definitions.CONTAINERS_PREFIX + env_name + f'_port_mapper_{port}') try: Client.get_instance().containers.get(port_mapper_container_name) except docker.errors.NotFound: self.fail( f'Cannot find port mapper for environment {env_name} with' f'port {port}')
def save(name, output): current_env = StateConfig.get_current_env() click.echo(f'Saving environment {current_env}...') image_name = _commit(name, current_env) _export(image_name, output) click.echo(f'Removing image {image_name}...') Client.get_instance().images.remove(image_name) click.echo(f'Image {image_name} removed')
def create_env(image, name, project_dir): workdir = os.path.abspath(project_dir) mounts = [Mount('/usr/src', workdir, type='bind')] kwargs = { 'command': '/bin/sh', 'stdin_open': True, 'name': definitions.CONTAINERS_PREFIX + name, 'mounts': mounts, 'network': definitions.CONTAINERS_PREFIX + name + '_network', } Client.get_instance().containers.create(image, **kwargs) StateConfig.get_instance().update_conf( {definitions.CONTAINERS_PREFIX + name: { 'workdir': workdir }})
def execute(cls, *args, **kwargs): client = Client.get_instance() current_env = StateConfig.get_current_env() try: container = client.containers.get(definitions.CONTAINERS_PREFIX + current_env) except docker.errors.ImageNotFound: click.echo(f'Container {current_env} not found, exiting...') raise # This cannot be done with docker python sdk host_base_wd = StateConfig.get_instance().get_env_conf( definitions.CONTAINERS_PREFIX + current_env)['workdir'] current_wd = os.getcwd() if not current_wd.startswith(host_base_wd): raise RuntimeError( f'Cannot run commands outside of {host_base_wd}') relative_wd = current_wd[len(host_base_wd):] guest_wd = f'/usr/src{relative_wd}' detach = kwargs.get('detach') env_vars = cls._build_env_vars(kwargs.get('env_vars')) with cls._with_mapped_ports(container, kwargs.get('ports'), detach): cmd = ['docker', 'exec', '-w', guest_wd] if detach: cmd.append('-d') else: cmd.extend(['-i', '-t']) cmd = (cmd + env_vars + [(definitions.CONTAINERS_PREFIX + current_env)] + list(args)) subprocess.check_call(cmd)
def tearDown(self): os.chdir(self._cwd) try: for i in range(1, self._env_index): env_name = self._create_env_name(i) try: Client.get_instance().containers.get( definitions.CONTAINERS_PREFIX + env_name).remove(force=True) delete_network(env_name) except docker.errors.NotFound: pass self._remove_port_mappers(env_name) finally: shutil.rmtree(self._test_dir.name)
def load(name, project_dir, input_file): click.echo(f'Loading environment {name} from {input_file}...') with open(input_file, 'rb') as fin: image = Client.get_instance().images.load(fin)[0] create_network(name) create_env(image, name, project_dir) click.echo(f'Environment {name} loaded from {input_file}!')
def activate(name): click.echo('Activating environment...') try: container = Client.get_instance().containers.get( definitions.CONTAINERS_PREFIX + name) except docker.errors.NotFound: click.echo(f'Environment {name} not found, exiting...') else: container.start() click.echo('Environment activated!')
def deactivate(): click.echo('Deactivating current environment...') current_env = StateConfig.get_current_env() try: container = Client.get_instance().containers.get( definitions.CONTAINERS_PREFIX + current_env) except docker.errors.ImageNotFound: click.echo(f'Environment {current_env} not found, exiting...') else: container.stop() click.echo('Environment deactivated!')
def delete_network(env_name): network_name = definitions.CONTAINERS_PREFIX + env_name + '_network' try: network = Client.get_instance().networks.get(network_name) except docker.errors.ImageNotFound: click.echo(f'Network {network_name} not found, exiting...') raise for c in network.containers: network.disconnect(c) network.remove()
def _export(image_name, output): click.echo(f'Saving image {image_name} to {output}...') try: image = Client.get_instance().images.get(image_name) except docker.errors.ImageNotFound: raise output = output or f'{image_name}.tar.gz' with open(output, 'wb') as fout: for chunk in image.save(named=True): fout.write(chunk) click.echo(f'Image {image_name} saved to {output}!')
def _with_mapped_ports(cls, container, ports, detach): if ports: port_mappers_containers_names = cls._run_port_mapper( container, ports) else: port_mappers_containers_names = [] yield if detach: return for container_name in port_mappers_containers_names: container = Client.get_instance().containers.get(container_name) container.stop()
def create(name, project_dir, version): version = version or 'latest' click.echo(f'Creating environment {name} with python version {version}...') image_name = f'python:{version}' client = Client.get_instance() try: image = client.images.get(image_name) except docker.errors.ImageNotFound: click.echo(f'Image {image_name} not found, pulling...') image = client.images.pull('python', tag=version) create_network(name) create_env(image, name, project_dir) click.echo(f'Environment {name} with python version {version} created!')
def remove(name): click.echo(f'Removing environment {name}...') try: container = Client.get_instance().containers.get( definitions.CONTAINERS_PREFIX + name) except docker.errors.NotFound: click.echo(f'Environment {name} not found, exiting...') raise kwargs = { 'force': True, } container.remove(**kwargs) delete_network(name) StateConfig.get_instance().remove_from_conf(definitions.CONTAINERS_PREFIX + name) click.echo(f'Environment {name} removed!')
def list_environments(): click.echo(f'Listing environments...') kwargs = { 'all': True, } containers = Client.get_instance().containers.list(kwargs) current_env = StateConfig.get_current_env() envs = [] for c in containers: if not c.name.startswith(definitions.CONTAINERS_PREFIX): continue env_name = c.name[len(definitions.CONTAINERS_PREFIX):] prefix = '* ' if env_name == current_env else ' ' envs.append(f'{prefix}{env_name}') click.echo('\n'.join(envs)) click.echo(f'Environments listed!')
def _commit(name, current_env): click.echo(f'Saving environment {current_env} as image...') try: container = Client.get_instance().containers.get( definitions.CONTAINERS_PREFIX + current_env) except docker.errors.ImageNotFound: click.echo(f'Container {current_env} not found, exiting...') raise if not name: repository = f'{definitions.CONTAINERS_PREFIX + current_env}' tag = 'latest' else: repository, tag = name.split(':') container.commit(repository=repository, tag=tag) image_name = f'{repository}:{tag}' click.echo(f'Environment {current_env} saved as image {image_name}!') return image_name
def _remove_port_mappers(self, env_name): prefix = definitions.CONTAINERS_PREFIX + env_name + '_port_mapper_' for c in Client.get_instance().containers.list(all=True): if c.name.startswith(prefix): c.remove(force=True)
def create_network(env_name): network_name = definitions.CONTAINERS_PREFIX + env_name + '_network' Client.get_instance().networks.create(network_name, check_duplicate=True)