<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def push_or_pull(self, conf, action=None, ignore_missing=False):
 """Push or pull this image"""

        if action not in ("push", "pull"):
            raise ProgrammerError("Should have called push_or_pull with action to either push or pull, got {0}".format(action))

        if not conf.image_index:
            raise BadImage("Can't {0} without an image_index configuration".format(action), image=conf.name)

        if conf.image_name == "scratch":
            log.warning("Not pulling/pushing scratch, this is a reserved image!")
            return

        sync_stream = SyncProgressStream()

        for attempt in range(3):
            if attempt > 0:
                log.info("Attempting sync again\taction=%s\tattempt=%d", action, attempt)

            # Login before pulling or pushing
            # Have this in the for loop incase it fails and the push/pull also fails as a result
            conf.login(conf.image_name, is_pushing=action=='push')

            try:
                for line in getattr(conf.harpoon.docker_api, action)(
                        conf.image_name
                        , tag = None if conf.tag is NotSpecified else conf.tag
                        , stream = True
                        ):

                    for line in line.split(six.binary_type("\r\n", "utf-8")):
                        if not line:
                            continue

                        try:
                            sync_stream.feed(line)
                        except Failure as error:
                            if ignore_missing and action == "pull":
                                log.error("Failed to %s an image\timage=%s\timage_name=%s\tmsg=%s", action, conf.name, conf.image_name, error)
                                break
                            else:
                                raise FailedImage("Failed to {0} an image".format(action), image=conf.name, image_name=conf.image_name, msg=error)
                        except Unknown as error:
                            log.warning("Unknown line\tline=%s", error)

                        for part in sync_stream.printable():
                            if six.PY3:
                                conf.harpoon.stdout.write(part)
                            else:
                                conf.harpoon.stdout.write(part.encode('utf-8', 'replace'))
                        conf.harpoon.stdout.flush()

                # And stop the loop!
                break

            except KeyboardInterrupt:
                raise
            except FailedImage as error:
                log.exception(error)
                if attempt == 2:
                    raise