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')
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.')
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)
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?')
def escalate(ex): if utils.getenv(const.DEBUG_KEY): raise ex else: raise SystemExit(1)
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))
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')
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)
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.')