def wait_for_service(self, app_name, port, expected_output=None, timeout=100): """ Block until service is not ready to accept requests, raises an exc ProbeTimeout if timeout is reached :param app_name: str, name of the app :param port: str or int, port of the service :param expected_output: If not None method will check output returned from request and try to find matching string. :param timeout: int or float (seconds), time to wait for pod to run :return: None """ logger.info('Waiting for service to get ready') try: Probe(timeout=timeout, pause=10, fnc=self.request_service, app_name=app_name, port=port, expected_output=expected_output, expected_retval=True).run() except ProbeTimeout: logger.warning("Timeout: Request to service unsuccessful.") raise ConuException("Timeout: Request to service unsuccessful.")
def start_build(self, build, args=None): """ Start new build, raise exception if build failed :param build: str, name of the build :param args: list of str, another args of 'oc start-build' commands :return: None """ args = args or [] c = self._oc_command(["start-build"] + [build] + args) logger.info("Executing build %s", build) logger.info("Build command: %s", " ".join(c)) try: Probe(timeout=-1, pause=5, count=2, expected_exceptions=subprocess.CalledProcessError, expected_retval=None, fnc=run_cmd, cmd=c).run() except ProbeTimeout as e: raise ConuException("Cannot start build of application: %s" % e)
def create_namespace(self): """ Create namespace with random name :return: name of new created namespace """ random_string = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(4)) name = 'namespace-{random_string}'.format(random_string=random_string) namespace = client.V1Namespace(metadata=client.V1ObjectMeta(name=name)) self.core_api.create_namespace(namespace) logger.info("Creating namespace: %s", name) # save all namespaces created with this backend self.managed_namespaces.append(name) # wait for namespace to be ready Probe(timeout=30, pause=5, expected_retval=True, fnc=self._namespace_ready, namespace=name).run() return name
def wait(self, timeout=15): """ block until pod is not running, raises an exc ProbeTimeout if timeout is reached :param timeout: int or float (seconds), time to wait for pod to run :return: None """ Probe(timeout=timeout, fnc=self.get_phase, expected_retval=PodPhase.RUNNING).run()
def wait(self, timeout=15): """ block until pod is not ready, raises an exc ProbeTimeout if timeout is reached :param timeout: int or float (seconds), time to wait for pod to run :return: None """ Probe(timeout=timeout, fnc=self.is_ready, expected_retval=True).run()
def wait_for_port(self, port, timeout=10, **probe_kwargs): """ block until specified port starts accepting connections, raises an exc ProbeTimeout if timeout is reached :param port: int, port number :param timeout: int or float (seconds), time to wait for establishing the connection :param probe_kwargs: arguments passed to Probe constructor :return: None """ Probe(timeout=timeout, fnc=functools.partial(self.is_port_open, port), **probe_kwargs).run()
def test_wait_for_status(podman_backend): image = podman_backend.ImageClass(FEDORA_MINIMAL_REPOSITORY, tag=FEDORA_MINIMAL_REPOSITORY_TAG) cmd = ['true'] cont = image.run_via_binary(command=cmd) try: p = Probe(timeout=6, fnc=cont.is_running, expected_retval=False) p.run() # let's wait for the container to exit assert cont.exit_code() == 0 # let's make sure it exited fine finally: cont.delete(force=True)
def test_container_logs(podman_backend): image = podman_backend.ImageClass(FEDORA_MINIMAL_REPOSITORY, tag=FEDORA_MINIMAL_REPOSITORY_TAG) command = ["bash", "-c", "for x in `seq 1 5`; do echo $x; done"] cont = image.run_via_binary(command=command) try: Probe(timeout=5, fnc=cont.is_running, expected_retval=False).run() assert not cont.is_running() assert list(cont.logs()) == [ '1', '\n', '2', '\n', '3', '\n', '4', '\n', '5', '\n' ] finally: cont.delete(force=True)
def _run_container(self, run_command_instance, callback): """ this is internal method """ tmpfile = os.path.join(get_backend_tmpdir(), random_tmp_filename()) # the cid file must not exist run_command_instance.options += ["--cidfile=%s" % tmpfile] logger.debug("docker command: %s" % run_command_instance) response = callback() # and we need to wait now; inotify would be better but is way more complicated and # adds dependency Probe(timeout=10, count=10, pause=0.1, fnc=lambda: os.path.exists(tmpfile)).run() with open(tmpfile, 'r') as fd: container_id = fd.read() return container_id, response
def wait_for_service(self, app_name, expected_output=None, timeout=100): """ Block until service is not ready to accept requests, raises an exc ProbeTimeout if timeout is reached :param app_name: str, name of app :param expected_output: If not None method will check output returned from request and try to find matching string. :param timeout: int or float (seconds), time to wait for pod to run :return: None """ logger.info('Waiting for service to get ready') Probe(timeout=timeout, fnc=self.request_service, app_name=app_name, expected_output=expected_output, expected_retval=True).run()
def run_via_binary(self, run_command_instance=None, command=None, volumes=None, additional_opts=None, **kwargs): """ create a buildah container using this image :param run_command_instance: not used :param command: not used, please use exec :param volumes: tuple or list of tuples in the form: * `("/path/to/directory", )` * `("/host/path", "/container/path")` * `("/host/path", "/container/path", "mode")` * `(conu.Directory('/host/path'), "/container/path")` (source can be also Directory instance) :param additional_opts: list of str, additional options for `buildah from` :return: instance of BuildahContainer """ logger.info("create buildah container") if not run_command_instance: run_command_instance = BuildahRunBuilder( command=command, additional_opts=additional_opts) run_command_instance.image_name = self.get_full_name() tmpfile = os.path.join(get_backend_tmpdir(), random_tmp_filename()) run_command_instance.options += ["--cidfile=%s" % tmpfile] # TODO: add support for the skopeo transport names cmd = run_command_instance.build() # TODO: fix this using the run builder # if volumes: # cmd += self.get_volume_options(volumes=volumes) run_cmd(cmd) Probe(timeout=10, count=10, pause=0.1, fnc=lambda: self._file_not_empty(tmpfile)).run() with open(tmpfile, 'r') as fd: container_id = fd.read() return BuildahContainer(self, container_id, image_class=self.__class__)
def _run_container(self, run_command_instance, callback): """ this is internal method """ tmpfile = os.path.join(get_backend_tmpdir(), random_tmp_filename()) # the cid file must not exist run_command_instance.options += ["--cidfile=%s" % tmpfile] logger.debug("podman command: %s" % run_command_instance) response = callback() # and we need to wait now; inotify would be better but is way more complicated and # adds dependency try: Probe(timeout=10, count=25, pause=0.2, fnc=lambda: self._file_not_empty(tmpfile)).run() except (CountExceeded, ProbeTimeout) as ex: logger.info("exception while running a container: %s", ex) raise ConuException("Container was not created, please see the logs.") with open(tmpfile, 'r') as fd: container_id = fd.read() return container_id, response
def test_exit_code(podman_backend): image = podman_backend.ImageClass(FEDORA_MINIMAL_REPOSITORY, tag=FEDORA_MINIMAL_REPOSITORY_TAG) cmd = ['sleep', '0.3'] cont = image.run_via_binary(command=cmd) try: p = Probe(timeout=5, fnc=cont.is_running, expected_retval=False) p.run() assert not cont.is_running() and cont.exit_code() == 0 finally: cont.delete(force=True) cmd = ['bash', '-c', "exit 42"] cont = image.run_via_binary(command=cmd) try: cont.wait() assert cont.exit_code() == 42 finally: cont.delete(force=True)
def _run_container(self, run_command_instance, callback): """ this is internal method """ tmpfile = os.path.join(get_backend_tmpdir(), random_tmp_filename()) # the cid file must not exist run_command_instance.options += ["--cidfile=%s" % tmpfile] logger.debug("docker command: %s" % run_command_instance) response = callback() def get_cont_id(): if not os.path.exists(tmpfile): return False with open(tmpfile, 'r') as fd: content = fd.read() return bool(content) Probe(timeout=2, count=10, pause=0.1, fnc=get_cont_id).run() with open(tmpfile, 'r') as fd: container_id = fd.read() if not container_id: raise ConuException("We could not get container's ID, it probably was not created") return container_id, response
def new_app(self, image, project, source=None, template=None, name_in_template=None, other_images=None, oc_new_app_args=None): """ Deploy app in OpenShift cluster using 'oc new-app' :param image: image to be used as builder image :param project: project where app should be created :param source: source used to extend the image, can be path or url :param template: str, url or local path to a template to use :param name_in_template: dict, {repository:tag} image name used in the template :param other_images: list of dict, some templates need other image to be pushed into the OpenShift registry, specify them in this parameter as list of dict [{<image>:<tag>}], where "<image>" is DockerImage and "<tag>" is a tag under which the image should be available in the OpenShift registry. :param oc_new_app_args: additional parameters for the `oc new-app` :return: str, name of app """ if template is not None and source is not None: raise ConuException( 'cannot combine template parameter with source parameter') # app name is generated randomly random_string = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(4)) name = 'app-{random_string}'.format(random_string=random_string) oc_new_app_args = oc_new_app_args or [] if template is not None: if name_in_template is None: raise ConuException('You need to specify name_in_template') self._create_app_from_template(image, name, template, name_in_template, other_images, oc_new_app_args, project) else: new_image = OpenshiftBackend.push_to_registry( image, image.name.split('/')[-1], image.tag, project) c = self._oc_command(["new-app"] + oc_new_app_args + [new_image.name + "~" + source] + ["-n"] + [project] + ["--name=%s" % name]) logger.info("Creating new app in project %s", project) try: o = run_cmd(c, return_output=True) logger.debug(o) except subprocess.CalledProcessError as ex: raise ConuException("oc new-app failed: %s" % ex) if os.path.isdir(source): c = self._oc_command(["-n"] + [project] + ["start-build"] + [name] + ["--from-dir=%s" % source]) logger.info( "Build application from local source in project %s", project) try: Probe(timeout=-1, pause=5, count=2, expected_exceptions=subprocess.CalledProcessError, expected_retval=None, fnc=run_cmd, cmd=c).run() except ProbeTimeout as e: raise ConuException( "Cannot start build of application: %s" % e) return name
def _create_app_from_template(self, image, name, template, name_in_template, other_images, oc_new_app_args, project): """ Helper function to create app from template :param image: image to be used as builder image :param template: str, url or local path to a template to use :param name_in_template: dict, {repository:tag} image name used in the template :param other_images: list of dict, some templates need other image to be pushed into the OpenShift registry, specify them in this parameter as list of dict [{<image>:<tag>}], where "<image>" is DockerImage and "<tag>" is a tag under which the image should be available in the OpenShift registry. :param oc_new_app_args: additional parameters for the `oc new-app` :param project: project where app should be created :return: None """ # push images to registry repository, tag = list(name_in_template.items())[0] OpenshiftBackend.push_to_registry(image, repository, tag, project) other_images = other_images or [] for o in other_images: image, tag = list(o.items())[0] OpenshiftBackend.push_to_registry(image, tag.split(':')[0], tag.split(':')[1], project) oc_new_app_args += [ "-p", "NAME=%s" % name, "-p", "NAMESPACE=%s" % project ] c = self._oc_command(["new-app"] + [template] + oc_new_app_args + ["-n"] + [project] + ["--name=%s" % name]) logger.info("Creating new app in project %s", project) try: # ignore status because sometimes oc new-app can fail when image # is already pushed in register o = run_cmd(c, return_output=True, ignore_status=True) logger.debug(o) except subprocess.CalledProcessError as ex: raise ConuException("oc new-app failed: %s" % ex) c = self._oc_command(["start-build"] + [name]) logger.info("Build application from local source in project %s", project) try: Probe(timeout=-1, pause=5, count=2, expected_exceptions=subprocess.CalledProcessError, expected_retval=None, fnc=run_cmd, cmd=c).run() except ProbeTimeout as e: raise ConuException("Cannot start build of application: %s" % e)