def check(): """ Check plans and Docker environment """ # plans loader = Loader() console.info(f'loaded [bold]{len(loader.valid_plans())}[/] valid plans') # docker try: client = docker.from_env() info = client.info() console.info(f'docker server version: [bold]{info.get("ServerVersion")}[/]') # network container client.images.get(config.net_container_name()) console.info(f'network image [bold]\'{config.net_container_name()}\'[/] exists') # dwn docker network client.networks.get(config.net_name()) console.info(f'docker network [bold]\'{config.net_name()}\'[/] exists') except ImageNotFound as _: console.warn(f'network image [bold]\'{config.net_container_name()}\'[/] does not exist. ' f'build it with the [bold]\'network build-container\'[/] command') except NotFound as _: console.warn(f'docker network [bold]\'{config.net_name()}\'[/] not found.' f'use [bold]\'docker network create {config.net_name()}\'[/] to should solve that.') except DockerException as e: console.error(f'docker client error type [dim]{type(e)}[/]: [bold]{e}[/]') console.info('[green]everything seems to be ok to use dwn![/]')
def stop_net(self, outside: int, inside: int): """ Stops a specific network container """ for container in self.containers(): if container.name == self.get_net_container_name_with_ports( outside, inside): console.info( f'stopping network container for [green]{inside}[/]<-[red]{outside}[/]' ) container.stop()
def stop(name, yes): """ Stop a plan """ if not yes: if not click.confirm(f'are you sure you want to stop containers for plan {name}?'): console.info('not stopping any plans') return loader = Loader() if not (plan := loader.get_plan(name)): console.error(f'unable to find plan [bold]{name}[/]') return
def _ensure_net_exists(self): """ Ensures that the network image and docker network exists. """ try: self.get_client().images.get(config.net_container_name()) self.get_client().networks.get(config.net_name()) except ImageNotFound as _: console.info( f'network image [bold]{config.net_container_name()}[/] does not exist, quickly building it' ) _, logs = self.get_client().images.build( path=str(NETWORK_CONTAINER_PATH), pull=True, tag=config.net_container_name(), rm=True, forcerm=True) for log in logs: console.debug(log) console.info( f'network container [bold]{config.net_container_name()}[/] built' ) self._ensure_net_exists() except NotFound as _: console.info( f'docker network [bold]{config.net_name()}[/] does not exist, creating it' ) self.get_client().networks.create(name=config.net_name(), check_duplicate=True) self._ensure_net_exists()
def pull(name): """ Pull plan images. """ plan_targets = [] if not name and not click.confirm('> a plan name was not specified, ' 'pull all valid plan images?'): return try: client = docker.from_env() except DockerException as e: console.error(f'failed to connect to docker: [bold]{e}[/e]') return loader = Loader() if name: plan_targets.append(loader.get_plan(name)) else: [plan_targets.append(n) for n in loader.valid_plans()] for p in plan_targets: try: console.info(f'pulling image [bold]{p.image}:{p.version}[/]') client.images.pull(p.image, tag=p.version) except ImageNotFound as e: console.error(f'failed to pull image: [bold]{e}[/]') continue except DockerException as e: console.error(f'a docker exception occurred: [bold]{e}[/]') continue console.info( f'image [bold]{p.image}:{p.version}[/] for plan [cyan]{p.name}[/] pulled' )
def build_container(): """ Builds the network container """ console.info('building network container') try: client = docker.from_env() except DockerException as e: console.error(f'docker client failed: [bold]{e}[/]') return console.debug(f'path to docker context is: [bold]{NETWORK_CONTAINER_PATH}[/]') console.debug(f'network container will be called [bold]\'{config.net_container_name()}\'[/]') image, logs = client.images.build( path=str(NETWORK_CONTAINER_PATH), pull=True, tag=config.net_container_name()) for log in logs: console.debug(log) console.info(f'network container \'{config.net_container_name()}\' built')
def _ensure_image_exists(self): """ Ensures that an image exists if a plan has an inline dockerfile. """ # if the plan does not have an inline dockerfile, then we can rely on # the call to run() later to pull the image instead. if not self.plan.has_dockerfile(): return console.debug(f'checking if {self.plan.image_version()} is available') try: self.get_client().images.get(self.plan.image_version()) except ImageNotFound as _: console.warn( f'image for plan [cyan]{self.plan.name}[/] does not exist, quickly building it' ) dockerfile = BytesIO(self.plan.dockerfile.encode('utf-8')) console.debug(f'building dockerfile:\n{self.plan.dockerfile}') _, logs = self.get_client().images.build( fileobj=dockerfile, pull=True, tag=self.plan.image_version(), rm=True, forcerm=True) for log in logs: console.debug(log) console.info( f'container for [bold]{self.plan.image_version()}[/] built') self._ensure_net_exists()
@network.command() @click.argument('name') @click.option('--outside', '-o', required=True, help='the outside, host port to open') @click.option('--inside', '-i', required=True, help='the inside, container port to forward to') def add(name, outside, inside): """ Add a port to a plan """ loader = Loader() if not (plan := loader.get_plan(name)): console.error(f'unable to find plan [bold]{name}[/]') return plan.container.run_net(outside, inside) console.info(f'port binding for {outside}->{plan.name}:{inside} created') @network.command() @click.argument('name') @click.option('--outside', '-o', required=True, help='the outside, host port to open') @click.option('--inside', '-i', required=True, help='the inside, container port to forward to') def remove(name, outside, inside): """ Removes a port mapping from a plan """ loader = Loader() if not (plan := loader.get_plan(name)): console.error(f'unable to find plan [bold]{name}[/]') return
@click.command(context_settings=dict(ignore_unknown_options=True, ) ) # allow passing through options to the docker command @click.argument('name') @click.argument('extra_args', nargs=-1) def run(name, extra_args): """ Run a plan """ loader = Loader() if not (plan := loader.get_plan(name)): console.error(f'unable to find plan [bold]{name}[/]') return console.info(f'found plan for [cyan]{name}[/]') if (c := len(plan.container.containers())) > 0: console.error( f'plan [bold]{name}[/] already has [b]{c}[/] containers running') console.info( f'use [bold]dwn show[/] to see running plans. [bold]dwn stop <plan>[/] to stop' ) return plan.add_commands(extra_args) if extra_args else None for v, o in plan.volumes.items(): console.info(f'volume: {v} -> {o["bind"]}') for m in plan.exposed_ports: console.info(f'port: {m[0]}<-{m[1]}')
def update(name): """ Update plan images. """ plan_targets = [] if not name and not click.confirm('> a plan name was not specified, ' 'pull all valid plan images?'): return try: client = docker.from_env() except DockerException as e: console.error(f'failed to connect to docker: [bold]{e}[/e]') return loader = Loader() if name: plan_targets.append(loader.get_plan(name)) else: [plan_targets.append(n) for n in loader.valid_plans()] for p in plan_targets: if p is None: continue try: # build the image if we have an inline dockerfile if p.has_dockerfile(): console.info(f'building image [bold]{p.image_version()}[/]') dockerfile = BytesIO(p.dockerfile.encode('utf-8')) _, logs = client.images.build(fileobj=dockerfile, pull=True, tag=p.image_version(), rm=True, forcerm=True, nocache=True) for log in logs: console.debug(log) console.info( f'container for [bold]{p.image_version()}[/] built') # pull the image instead else: console.info(f'pulling image [bold]{p.image_version()}[/]') client.images.pull(p.image, tag=p.version) except ImageNotFound as e: console.error(f'failed to pull image: [bold]{e}[/]') continue except DockerException as e: console.error(f'a docker exception occurred: [bold]{e}[/]') continue console.info( f'image [bold]{p.image_version()}[/] for plan [cyan]{p.name}[/] updated' )