Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
pyckles / contexts / freck.py
Size: Mime:
# -*- coding: utf-8 -*-
import copy
import json
import os
import re
import subprocess
import sys
from collections import Mapping
from threading import Thread, Event
from plumbum import local
from six import string_types

try:
    from queue import Queue
except (Exception):
    # Python 2.7
    from Queue import Queue

from pyckles.contexts import PycklesContext

FRECK_PATH = os.path.realpath(
    os.path.join(os.path.dirname(__file__), "..", "external", "freck", "freck")
)


def cleanup_string(line):
    regex = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]")
    return regex.sub("", line)


class PipeReader(Thread):
    def __init__(self, _fd):

        super(PipeReader, self).__init__()
        self._fd = _fd
        self._queue = Queue()
        self._stop_event = Event()

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

    def run(self):

        for line in iter(self._fd.readline, None):
            if line:
                self._queue.put(line.decode("UTF-8"))
            else:
                if self.stopped():
                    break


class FreckContext(PycklesContext):
    def __init__(
        self,
        pycklets_package_names=None,
        config=None,
        debug=None,
        update_freckles=False,
        **kwargs
    ):

        super(FreckContext, self).__init__(
            pycklets_package_names=pycklets_package_names, config=config, debug=debug
        )
        self._update_freckles = update_freckles

    def _run(self, frecklet_list, inventory=None, run_config=None):

        freck_args = ["freckles"]
        # freck_args = []

        config = copy.deepcopy(self.context_config)
        config["repos"] = self.context_repos
        if not self.debug and False:
            config["callback"] = ["result"]
        else:
            config["callback"] = [
                "result",
                {"default": {"profile": "full", "use_stderr": True}},
            ]

        if isinstance(run_config, string_types):
            freck_args.append("--target")
            freck_args.append(run_config)
        elif isinstance(run_config, Mapping):
            host = run_config.get("host", "localhost")
            user = run_config.get("user", None)
            if user is None:
                target_string = host
            else:
                target_string = "{}@{}".format(user, host)

            port = run_config.get("port", None)
            if port is not None:
                target_string = "{}:{}".format(target_string, port)

            freck_args.append("--target")
            freck_args.append(target_string)

            if "become_pass" in run_config.keys():
                raise Exception(
                    "'become_pass' provided, currently it is not possible to use 'become_pass' and 'login_pass' with the 'freck' pyckles context."
                )

            if "login_pass" in run_config.keys():
                raise Exception(
                    "'login_pass' provided, currently it is not possible to use 'become_pass' and 'login_pass' with the 'freck' pyckles context."
                )

            if "ssh_key" in run_config.keys():
                raise Exception(
                    "'ssh_key' provided, currently it is not possible to use 'ssh_key' with the 'freck' pyckles context."
                )

            if "elevated" in run_config.keys() and run_config["elvated"] is True:
                freck_args.append("--elevated")
            elif "elevated" in run_config.keys() and run_config["elvated"] is False:
                freck_args.append("--not-elevated")

        # freck_args.append("--no-run")
        freck_args.append("--context")
        freck_args.append(json.dumps(config))
        freck_args.append("read-stdin")

        freck = local[FRECK_PATH]
        # freck = local["/home/markus/.local/share/freckles/bin/freckles"]

        frecklet_data = json.dumps(frecklet_list)

        try:
            run_env = os.environ.copy()
            run_env["SILENT"] = "true"
            run_env["FRECK_HIDE_CURSOR"] = "false"
            if self._update_freckles is True:
                run_env["UPDATE"] = "true"
            process = freck.popen(
                freck_args,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                env=run_env,
            )
            process.stdin.write(frecklet_data.encode(encoding="UTF-8"))
            process.stdin.close()

            stdout_reader = PipeReader(process.stdout)
            stderr_reader = PipeReader(process.stderr)

            stdout_reader.start()
            stderr_reader.start()

            stdout = []
            stderr = []

            while process.poll() is None:
                if not stderr_reader._queue.empty():
                    line = stderr_reader._queue.get()
                    stderr.append(line)
                    sys.stdout.write(line)
                # if not stdout_reader._queue.empty():
                #     line = stdout_reader._queue.get()
                #     stdout.append(line)
                #     sys.stdout.write(line)

            process.wait()
            stdout_reader.stop()
            stderr_reader.stop()
            stdout_reader.join()
            stderr_reader.join()

            while not stderr_reader._queue.empty():
                line = stderr_reader._queue.get()
                stderr.append(line)
            stderr = "".join(stderr)
            while not stdout_reader._queue.empty():
                line = stdout_reader._queue.get()
                stdout.append(line)
            stdout = "".join(stdout)

            rc = process.returncode

            if rc != 0:
                raise Exception(
                    "freckles run exit code non-zero ({}): {}".format(rc, stderr)
                )

            stdout = cleanup_string(stdout).strip()

            if not stdout:
                result = {}
            else:
                result = json.loads(stdout)

            return result

        except (Exception) as e:
            raise e

        # print("Exit code: {}".format(rc))
        # if rc != 0:
        #     raise Exception("Error: {}".format(stdout + "\n" + stderr))
        #
        # stdout = cleanup_string(stdout).strip()
        # print(stdout)
        # result = json.loads(stdout)
        #
        # return result