Beispiel #1
0
def install_ctl_package(download: str = 'always'):  # always | missing | never
    exists = utils.path_exists('./brewblox-ctl.tar.gz')
    release = utils.getenv(const.CTL_RELEASE_KEY) or utils.getenv(
        const.RELEASE_KEY)
    if download == 'always' or download == 'missing' and not exists:
        sh(f'wget -q -O ./brewblox-ctl.tar.gz https://brewblox.blob.core.windows.net/ctl/{release}/brewblox-ctl.tar.gz'
           )
    sh('python3 -m pip install ./brewblox-ctl.tar.gz')
Beispiel #2
0
def add_node_red(force):
    """
    Create a service for Node-RED.
    """
    utils.check_config()
    utils.confirm_mode()

    name = 'node-red'
    sudo = utils.optsudo()
    host = utils.host_ip()
    port = utils.getenv(const.HTTPS_PORT_KEY)
    config = utils.read_compose()

    if not force:
        check_duplicate(config, name)

    config['services'][name] = {
        'image': 'brewblox/node-red:${BREWBLOX_RELEASE}',
        'restart': 'unless-stopped',
        'volumes': [{
            'type': 'bind',
            'source': f'./{name}',
            'target': '/data',
        }]
    }

    sh(f'mkdir -p ./{name}')
    if [getgid(), getuid()] != [1000, 1000]:
        sh(f'sudo chown -R 1000:1000 ./{name}')

    utils.write_compose(config)
    click.echo(f'Added Node-RED service `{name}`.')
    if utils.confirm('Do you want to run `brewblox-ctl up` now?'):
        sh(f'{sudo}docker-compose up -d')
        click.echo(f'Visit https://{host}:{port}/{name} in your browser to load the editor.')
Beispiel #3
0
def check_ports():
    if utils.path_exists('./docker-compose.yml'):
        utils.info('Stopping services...')
        sh(f'{utils.optsudo()}docker-compose down')

    ports = [
        int(utils.getenv(key, const.ENV_DEFAULTS[key])) for key in [
            const.HTTP_PORT_KEY,
            const.HTTPS_PORT_KEY,
            const.MQTT_PORT_KEY,
        ]
    ]

    try:
        port_connnections = [
            conn for conn in psutil.net_connections()
            if conn.laddr.ip in ['::', '0.0.0.0'] and conn.laddr.port in ports
        ]
    except psutil.AccessDenied:
        utils.warn(
            'Unable to read network connections. You need to run `netstat` or `lsof` manually.'
        )
        port_connnections = []

    if port_connnections:
        port_str = ', '.join(
            set(str(conn.laddr.port) for conn in port_connnections))
        utils.warn(f'Port(s) {port_str} already in use.')
        utils.warn(
            'Run `brewblox-ctl service ports` to configure Brewblox ports.')
        if not utils.confirm('Do you want to continue?'):
            raise SystemExit(1)
Beispiel #4
0
    def check_docker_opts(self):
        self.docker_install = True
        self.docker_group_add = True
        self.docker_pull = True

        if utils.command_exists('docker'):
            utils.info('Docker is already installed.')
            self.docker_install = False
        elif not self.use_defaults:
            self.docker_install = utils.confirm(
                'Do you want to install docker?')

        if utils.is_docker_user():
            user = utils.getenv('USER')
            utils.info(f'{user} already belongs to the docker group.')
            self.docker_group_add = False
        elif not self.use_defaults:
            self.docker_group_add = utils.confirm(
                'Do you want to run docker commands without sudo?')

        if not self.use_defaults:
            self.docker_pull = utils.confirm(
                'Do you want to pull the docker images for your services?')
Beispiel #5
0
def escalate(ex):
    if utils.getenv(const.DEBUG_KEY):
        raise ex
    else:
        raise SystemExit(1)
Beispiel #6
0
def get_env(key, default):
    """Read a single env variable.

    This includes values from .env, but also other shell values.
    """
    click.echo(utils.getenv(key, default))
Beispiel #7
0
def install(ctx: click.Context, snapshot_file):
    """Install Brewblox and its dependencies.

    Brewblox can be installed multiple times on the same computer.
    Settings and databases are stored in a Brewblox directory.

    This command also installs system-wide dependencies.
    A reboot is required after installing docker, or adding the user to the 'docker' group.

    By default, `brewblox-ctl install` attempts to download packages using the apt package manager.
    If you are using a system without apt (eg. Synology NAS), this step will be skipped.
    You will need to manually install any missing libraries.

    When using the `--snapshot ARCHIVE` option, no dir is created.
    Instead, the directory in the snapshot is extracted.
    It will be renamed to the desired name of the Brewblox directory.

    \b
    Steps:
        - Ask confirmation for installation steps.
        - Install apt packages.
        - Install docker.
        - Add user to 'docker' group.
        - Fix host IPv6 settings.
        - Disable host-wide mDNS reflection.
        - Set variables in .env file.
        - If snapshot provided:
            - Load configuration from snapshot.
        - Else:
            - Check for port conflicts.
            - Create docker-compose configuration files.
            - Create datastore (Redis) directory.
            - Create history (Victoria) directory.
            - Create gateway (Traefik) directory.
            - Create SSL certificates.
            - Create eventbus (Mosquitto) directory.
            - Set version number in .env file.
        - Pull docker images.
        - Reboot if needed.
    """
    utils.confirm_mode()
    user = utils.getenv('USER')
    opts = InstallOptions()

    opts.check_confirm_opts()
    opts.check_system_opts()
    opts.check_docker_opts()
    opts.check_reboot_opts()

    if not snapshot_file:
        opts.check_init_opts()

    # Install Apt packages
    if opts.apt_install:
        utils.info('Installing apt packages...')
        apt_deps = ' '.join(const.APT_DEPENDENCIES)
        sh([
            'sudo apt-get update',
            'sudo apt-get upgrade -y',
            f'sudo apt-get install -y {apt_deps}',
        ])
    else:
        utils.info('Skipped: apt-get install.')

    # Install docker
    if opts.docker_install:
        utils.info('Installing docker...')
        sh('curl -sL get.docker.com | sh', check=False)
    else:
        utils.info('Skipped: docker install.')

    # Add user to 'docker' group
    if opts.docker_group_add:
        utils.info(f"Adding {user} to 'docker' group...")
        sh('sudo usermod -aG docker $USER')
    else:
        utils.info(f"Skipped: adding {user} to 'docker' group.")

    # Always apply actions
    actions.disable_ssh_accept_env()
    actions.fix_ipv6(None, False)
    actions.edit_avahi_config()
    actions.add_particle_udev_rules()
    actions.uninstall_old_ctl_package()
    actions.deploy_ctl_wrapper()

    # Set variables in .env file
    # Set version number to 0.0.0 until snapshot load / init is done
    utils.info('Setting .env values...')
    utils.setenv(const.CFG_VERSION_KEY, '0.0.0')
    utils.setenv(const.SKIP_CONFIRM_KEY, str(opts.skip_confirm))
    for key, default_val in const.ENV_DEFAULTS.items():
        utils.setenv(key, utils.getenv(key, default_val))

    # Install process splits here
    # Either load all config files from snapshot or run init
    sudo = utils.optsudo()
    if snapshot_file:
        ctx.invoke(snapshot.load, file=snapshot_file)
    else:
        release = utils.getenv('BREWBLOX_RELEASE')

        utils.info('Checking for port conflicts...')
        actions.check_ports()

        utils.info('Copying docker-compose.shared.yml...')
        sh(f'cp -f {const.CONFIG_DIR}/docker-compose.shared.yml ./')

        if opts.init_compose:
            utils.info('Copying docker-compose.yml...')
            sh(f'cp -f {const.CONFIG_DIR}/docker-compose.yml ./')

        # Stop after we're sure we have a compose file
        utils.info('Stopping services...')
        sh(f'{sudo}docker-compose down')

        if opts.init_datastore:
            utils.info('Creating datastore directory...')
            sh('sudo rm -rf ./redis/; mkdir ./redis/')

        if opts.init_history:
            utils.info('Creating history directory...')
            sh('sudo rm -rf ./victoria/; mkdir ./victoria/')

        if opts.init_gateway:
            utils.info('Creating gateway directory...')
            sh('sudo rm -rf ./traefik/; mkdir ./traefik/')

            utils.info('Creating SSL certificate...')
            actions.makecert('./traefik', release)

        if opts.init_eventbus:
            utils.info('Creating mosquitto config directory...')
            sh('sudo rm -rf ./mosquitto/; mkdir ./mosquitto/')

        # Always copy cert config to traefik dir
        sh(f'cp -f {const.CONFIG_DIR}/traefik-cert.yaml ./traefik/')

        # Init done - now set CFG version
        utils.setenv(const.CFG_VERSION_KEY, const.CURRENT_VERSION)

    if opts.docker_pull:
        utils.info('Pulling docker images...')
        sh(f'{sudo}docker-compose pull')

    utils.info('All done!')

    # Reboot
    if opts.reboot_needed:
        if opts.prompt_reboot:
            utils.info('Press ENTER to reboot.')
            input()
        else:
            utils.info('Rebooting in 10 seconds...')
            sleep(10)
        sh('sudo reboot')
Beispiel #8
0
def check_env_vars():
    utils.info('Checking .env variables...')
    for (key, default_value) in const.ENV_DEFAULTS.items():
        current_value = utils.getenv(key)
        if current_value is None:
            utils.setenv(key, default_value)
Beispiel #9
0
def install(use_defaults,
            apt_install,
            docker_install,
            docker_user,
            no_reboot,
            dir,
            release):
    """Create Brewblox directory; install system dependencies; reboot.

    Brewblox can be installed multiple times on the same computer.
    Settings and databases are stored in a Brewblox directory (default: ./brewblox).

    This command also installs system-wide dependencies (docker).
    After `brewblox-ctl install`, run `brewblox-ctl setup` in the created Brewblox directory.

    A reboot is required after installing docker, or adding the user to the 'docker' group.

    By default, `brewblox-ctl install` attempts to download packages using the apt package manager.
    If you are using a system without apt (eg. Synology NAS), this step will be skipped.
    You will need to manually install any missing libraries.

    \b
    Steps:
        - Install apt packages.
        - Install docker.
        - Add user to 'docker' group.
        - Create Brewblox directory (default ./brewblox).
        - Set variables in .env file.
        - Reboot.
    """
    utils.confirm_mode()

    apt_deps = 'curl net-tools libssl-dev libffi-dev'
    user = utils.getenv('USER')
    default_dir = path.abspath('./brewblox')
    prompt_reboot = True

    if use_defaults is None:
        use_defaults = utils.confirm('Do you want to install with default settings?')

    # Check if packages should be installed
    if not utils.command_exists('apt'):
        utils.info('Apt is not available. You may need to find another way to install dependencies.')
        utils.info('Apt packages: "{}"'.format(apt_deps))
        apt_install = False

    if apt_install is None:
        if use_defaults:
            apt_install = True
        else:
            apt_install = utils.confirm('Do you want to install apt packages "{}"?'.format(apt_deps))

    # Check if docker should be installed
    if utils.command_exists('docker'):
        utils.info('Docker is already installed.')
        docker_install = False

    if docker_install is None:
        if use_defaults:
            docker_install = True
        else:
            docker_install = utils.confirm('Do you want to install docker?')

    # Check if user should be added to docker group
    if utils.is_docker_user():
        utils.info('{} already belongs to the docker group.'.format(user))
        docker_user = False

    if docker_user is None:
        if use_defaults:
            docker_user = True
        else:
            docker_user = utils.confirm('Do you want to run docker commands without sudo?')

    # Check used directory
    if dir is None:
        if use_defaults or utils.confirm("The default directory is '{}'. Do you want to continue?".format(default_dir)):
            dir = default_dir
        else:
            return

    if utils.path_exists(dir):
        if not utils.confirm('{} already exists. Do you want to continue?'.format(dir)):
            return

    if not no_reboot:
        prompt_reboot = utils.confirm('A reboot is required after installation. ' +
                                      'Do you want to be prompted before that happens?')

    # Install Apt packages
    if apt_install:
        utils.info('Installing apt packages...')
        sh([
            'sudo apt update',
            'sudo apt upgrade -y',
            'sudo apt install -y {}'.format(apt_deps),
        ])
    else:
        utils.info('Skipped: apt install.')

    # Install docker
    if docker_install:
        utils.info('Installing docker...')
        sh('curl -sL get.docker.com | sh')
    else:
        utils.info('Skipped: docker install.')

    # Add user to 'docker' group
    if docker_user:
        utils.info("Adding {} to 'docker' group...".format(user))
        sh('sudo usermod -aG docker $USER')
    else:
        utils.info("Skipped: adding {} to 'docker' group.".format(user))

    # Create install directory
    utils.info('Creating Brewblox directory ({})...'.format(dir))
    sh('mkdir -p {}'.format(dir))

    # Set variables in .env file
    utils.info('Setting variables in .env file...')
    dotenv_path = path.abspath('{}/.env'.format(dir))
    sh('touch {}'.format(dotenv_path))
    utils.setenv(const.RELEASE_KEY, release, dotenv_path)
    utils.setenv(const.CFG_VERSION_KEY, '0.0.0', dotenv_path)
    utils.setenv(const.SKIP_CONFIRM_KEY, str(use_defaults), dotenv_path)

    utils.info('Done!')

    # Reboot
    if not no_reboot:
        if prompt_reboot:
            utils.info('Press ENTER to reboot.')
            input()
        else:
            utils.info('Rebooting in 10 seconds...')
            sleep(10)
        sh('sudo reboot')
    else:
        utils.info('Skipped: reboot.')