sup

package module
v0.0.0-...-2f1932b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2025 License: MIT Imports: 17 Imported by: 0

README

Update Stack Up

A very simple deployment tool that runs a given set of bash commands on multiple hosts in parallel. It reads yaml configuration, which defines networks (groups of hosts), global variables (which can be changed via arguments), command(s) and targets (groups of commands).

[!NOTE] The goal is to revive the sup project, which has not been supported since 2018. First of all, to solve common problems (for example, an error when connecting via ssh), expand the functionality (for example, add reading the configuration from the url), add support for the Windows system and implement a simple user interface (example for Jenkins).

Install

Run the command in the console to quickly install or update the stable version for Linux or macOS system:

curl -sS https://raw.githubusercontent.com/Lifailon/usup/main/install.sh | bash

To install on Windows, download the binary file from the releases page.

Usage

usup [options] network <command(s)/target>

usup dev date
usup -u https://raw.githubusercontent.com/Lifailon/usup/refs/heads/main/usupfile.yml dev date

This is a simple example that clones a repository and runs tests on two specified machines, which I use to test the lazyjournal project on BSD-based systems:

go-test

Templates & Contributing

This repository also plans to contain a set of templates for installing packages, getting information and configuring the system.

You can contribute and add your template to this repository via a Pull Request.

Supported file names

By default, the following configuration file names will be searched for on startup, in order of priority.

usupfile.yml
usupfile.yaml
Usupfile.yml
Usupfile.yaml
supfile.yml
supfile.yaml
Supfile.yml
Supfile.yaml

Options

List of available flags.

Option Description
-f usupfile.yml Custom path to file configuration
-u https://example.com/usupfile.yml Url path to file configuration
-e, --env=[] Set environment variables
--only REGEXP Filter hosts matching regexp
--except REGEXP Filter out hosts matching regexp
-D, --debug Enable debug/verbose mode
--disable-prefix Disable hostname prefix
-h, --help Show help/usage
-v, --version Print version

Network

Network is a group of hosts that can be static or dynamic from URL or a local file.

networks:
  local:
    hosts:
      - localhost
  dev:
    hosts:
      - [email protected]:2121
      - [email protected]:2121
  remote-host-list:
    # Read host list from URL in Linux
    inventory: printf '%s\n' $(curl -s https://raw.githubusercontent.com/Lifailon/usup/refs/heads/main/hostlist)
    # Windows PowerShell or PowerShell Core
    # inventory: Invoke-RestMethod https://raw.githubusercontent.com/Lifailon/usup/refs/heads/main/hostlist
  local-host-list:
    # Linux
    inventory: printf '%s\n' $(cat ./hostlist)
    # Windows
    # inventory: Get-Content ./hostlist

Variables and Command

env:
  FILE_NAME: test
  FILE_FORMAT: txt

networks:
  dev:
    hosts:
      - [email protected]:2121
      - [email protected]:2121

commands:
  echo:
    desc: Print filename from env vars
    run: echo $FILE_NAME.$FILE_FORMAT

Output the contents of variables:

usup dev echo
[email protected]:2121 | test.txt
[email protected]:2121 | test.txt

Change the contents of variables:

usup -e "FILE_NAME=new_test" -e "FILE_FORMAT=temp" dev echo
[email protected]:2121 | new_test.temp
[email protected]:2121 | new_test.temp

Default environment variables available:

Variable Name Description
$SUP_HOST Current host
$SUP_NETWORK Current network
$SUP_USER User who invoked command
$SUP_TIME Date/time of command invocation
$SUP_ENV Environment variables provided on command invocation

Serial and once command

serial: N constraints a command to be run on N hosts at a time at maximum.

commands:
  echo:
    desc: Print filename from env vars
    run: echo $FILE_NAME.$FILE_FORMAT
    serial: 2

once: true constraints a command to be run only on one host.

commands:
  file:
    desc: Creat new test file
    run: echo "This is test" > ./$FILE_NAME.$FILE_FORMAT
    once: true

usup dev echo file

Upload files

Uploads files/directories to all remote hosts (uses tar under the hood).

commands:
  upload:
    desc: Upload dist files to all hosts
    upload:
      - src: ./$FILE_NAME.$FILE_FORMAT
        dst: /tmp/

Local command

Runs command always on localhost.

commands:
    build:
        desc: Build in Windows
        local: go build -o ./bin/sup.exe ./cmd/sup

Interactive Bash

You can pass any bash commands from stdin to execute them on all hosts:

commands:
  bash:
    desc: Interactive Bash on all hosts
    stdin: true
    run: bash

Send commands to all hosts simultaneously for execution.

echo 'sudo apt-get update -y && sudo apt-get upgrade -y' | usup production bash
# or
usup dev bash
ls
exit

Target

Target is an alias for a set of commands. Each command will be run on all hosts in parallel, will check the return status from all hosts and continue running subsequent commands only if successful (any error on any host will abort the process).

targets:
  get:
    - uptime
    - date
  up:
    - upload
    - cat

usup dev get get uptime and current time in the system from all hosts simultaneously (run uptime and date commands).

usup dev up download and read the file.

Jenkins

You can use the Jenkins generic pipeline, which uploads a list of all available configuration files (in yaml/yml format) to a specified GitHub repository, to select the file you want and define all the parameters available in it to run. A list of networks, commands and targets to choose from is available, as well as a list of available variables and their values, which can be overridden.

Plugins used (dependencies)
Import

You can import the config file in the jenkins_home/jobs/<New_Job_Name> directory and reload the configuration from disk in the interface or connect using SCM from GitHub.

jenkins-import

Parameters

jenkins-params

Example of execution results:

jenkins-build

License

Licensed under the MIT License.

Documentation

Index

Constants

View Source
const VERSION = "0.6.0"

Variables

View Source
var (
	Colors = []string{
		"\033[32m",
		"\033[33m",
		"\033[36m",
		"\033[35m",
		"\033[31m",
		"\033[34m",
	}
	ResetColor = "\033[0m"
)

Functions

func LocalTarCmdArgs

func LocalTarCmdArgs(path, exclude string) []string

func NewTarStreamReader

func NewTarStreamReader(cwd, path, exclude string) (io.Reader, error)

NewTarStreamReader создает устройство чтения потока tar из локального пути TODO: Вместо этого использовать "archive/tar".

func RemoteTarCommand

func RemoteTarCommand(dir string) string

RemoteTarCommand возвращает команду, которая должна быть запущена на удаленном SSH-хосте TODO: Проверка относительной директории.

func ResolveLocalPath

func ResolveLocalPath(cwd, path, env string) (string, error)

Types

type Client

type Client interface {
	Connect(host string) error
	Run(task *Task) error
	Wait() error
	Close() error
	Prefix() (string, int)
	Write(p []byte) (n int, err error)
	WriteClose() error
	Stdin() io.WriteCloser
	Stderr() io.Reader
	Stdout() io.Reader
	Signal(os.Signal) error
}

type Command

type Command struct {
	Name    string   `yaml:"-"`        // Название команды
	Desc    string   `yaml:"desc"`     // Описание команды
	Local   string   `yaml:"local"`    // Для локального запуска
	Run     string   `yaml:"run"`      // Для удаленного запуска
	Script  string   `yaml:"script"`   // Загрузить команду из скрипта и запустить ее удаленно
	Upload  []Upload `yaml:"upload"`   // Структура Upload
	Stdin   bool     `yaml:"stdin"`    // да/нет - присоединить STDOUT локального хоста к STDIN удаленных команд
	Once    bool     `yaml:"once"`     // да/нет - команда должна быть запущена один раз (только на одном хосте).
	Serial  int      `yaml:"serial"`   // Максимальное количество клиентов, обрабатывающих задачу параллельно
	RunOnce bool     `yaml:"run_once"` // да/нет - команда должна быть выполнена только один раз
}

Command представляет команду/команды для удаленного выполнения

type Commands

type Commands struct {
	Names []string
	// contains filtered or unexported fields
}

Список команд (определяемых пользователем)

func (*Commands) Get

func (c *Commands) Get(name string) (Command, bool)

func (*Commands) UnmarshalYAML

func (c *Commands) UnmarshalYAML(unmarshal func(interface{}) error) error

type EnvList

type EnvList []*EnvVar

EnvList - это список переменных окружения, который отображается на карту YAML, но сохраняет порядок, позволяя поздним переменным ссылаться на ранние.

func (*EnvList) AsExport

func (e *EnvList) AsExport() string

func (*EnvList) ResolveValues

func (e *EnvList) ResolveValues() error

func (*EnvList) Set

func (e *EnvList) Set(key, value string)

Задать для ключа значение, равное значению в этом списке

func (EnvList) Slice

func (e EnvList) Slice() []string

func (*EnvList) UnmarshalYAML

func (e *EnvList) UnmarshalYAML(unmarshal func(interface{}) error) error

type EnvVar

type EnvVar struct {
	Key   string
	Value string
}

Переменная окружения

func (EnvVar) AsExport

func (e EnvVar) AsExport() string

Возвращает переменную окружения в виде оператора экспорта bash

func (EnvVar) String

func (e EnvVar) String() string

type ErrConnect

type ErrConnect struct {
	User   string
	Host   string
	Reason string
}

func (ErrConnect) Error

func (e ErrConnect) Error() string

type ErrMustUpdate

type ErrMustUpdate struct {
	Msg string
}

func (ErrMustUpdate) Error

func (e ErrMustUpdate) Error() string

type ErrTask

type ErrTask struct {
	Task   *Task
	Reason string
}

func (ErrTask) Error

func (e ErrTask) Error() string

type ErrUnsupportedSupfileVersion

type ErrUnsupportedSupfileVersion struct {
	Msg string
}

func (ErrUnsupportedSupfileVersion) Error

type LocalhostClient

type LocalhostClient struct {
	// contains filtered or unexported fields
}

Основная структура клиента

func (*LocalhostClient) Close

func (c *LocalhostClient) Close() error

func (*LocalhostClient) Connect

func (c *LocalhostClient) Connect(_ string) error

func (*LocalhostClient) Prefix

func (c *LocalhostClient) Prefix() (string, int)

func (*LocalhostClient) Run

func (c *LocalhostClient) Run(task *Task) error

func (*LocalhostClient) Signal

func (c *LocalhostClient) Signal(sig os.Signal) error

func (*LocalhostClient) Stderr

func (c *LocalhostClient) Stderr() io.Reader

func (*LocalhostClient) Stdin

func (c *LocalhostClient) Stdin() io.WriteCloser

func (*LocalhostClient) Stdout

func (c *LocalhostClient) Stdout() io.Reader

func (*LocalhostClient) Wait

func (c *LocalhostClient) Wait() error

func (*LocalhostClient) Write

func (c *LocalhostClient) Write(p []byte) (n int, err error)

func (*LocalhostClient) WriteClose

func (c *LocalhostClient) WriteClose() error

type Network

type Network struct {
	Env          EnvList  `yaml:"env"`
	Inventory    string   `yaml:"inventory"`
	Hosts        []string `yaml:"hosts"`
	Bastion      string   `yaml:"bastion"` // Jump host for the environment
	User         string   // `yaml:"user"`
	IdentityFile string   // `yaml:"identity_file"`
}

Сеть - это группа хостов с дополнительными пользовательскими параметрами переменных (env)

func (Network) ParseInventory

func (n Network) ParseInventory() ([]string, error)

Запустить команду инвентаризации (если она была предоставлена), и добавить выходные строки команды к списку хостов, заданному вручную

type Networks

type Networks struct {
	Names []string
	// contains filtered or unexported fields
}

Сети - это список сетей, определенных пользователем

func (*Networks) Get

func (n *Networks) Get(name string) (Network, bool)

func (*Networks) UnmarshalYAML

func (n *Networks) UnmarshalYAML(unmarshal func(interface{}) error) error

type SSHClient

type SSHClient struct {
	// contains filtered or unexported fields
}

Клиент - это обертка над ssh соединением/сессией

func (*SSHClient) Close

func (c *SSHClient) Close() error

Close closes the underlying SSH connection and session.

func (*SSHClient) Connect

func (c *SSHClient) Connect(host string) error

Connect creates SSH connection to a specified host. It expects the host of the form "[ssh://]host[:port]".

func (*SSHClient) ConnectWith

func (c *SSHClient) ConnectWith(host string, dialer SSHDialFunc) error

Создает ssh соединение с указанным хостом с использованием dialer для авторизации по ключу

func (*SSHClient) DialThrough

func (sc *SSHClient) DialThrough(net, addr string, config *ssh.ClientConfig) (*ssh.Client, error)

Создает новое ssh соединение с сервером через уже существующие ssh подключение

func (*SSHClient) Prefix

func (c *SSHClient) Prefix() (string, int)

func (*SSHClient) Run

func (c *SSHClient) Run(task *Task) error

Run запускает команду task.Run удаленно на хосте c.host

func (*SSHClient) Signal

func (c *SSHClient) Signal(sig os.Signal) error

func (*SSHClient) Stderr

func (c *SSHClient) Stderr() io.Reader

func (*SSHClient) Stdin

func (c *SSHClient) Stdin() io.WriteCloser

func (*SSHClient) Stdout

func (c *SSHClient) Stdout() io.Reader

func (*SSHClient) Wait

func (c *SSHClient) Wait() error

Дожидается окончания выполнения удаленной команды и выходит из системы (закрывает ssh сессию)

func (*SSHClient) Write

func (c *SSHClient) Write(p []byte) (n int, err error)

func (*SSHClient) WriteClose

func (c *SSHClient) WriteClose() error

type SSHDialFunc

type SSHDialFunc func(net, addr string, config *ssh.ClientConfig) (*ssh.Client, error)

SSHDialFunc can dial an ssh server and return a client

type Stackup

type Stackup struct {
	// contains filtered or unexported fields
}

func New

func New(conf *Supfile) (*Stackup, error)

func (*Stackup) Debug

func (sup *Stackup) Debug(value bool)

func (*Stackup) Prefix

func (sup *Stackup) Prefix(value bool)

func (*Stackup) Run

func (sup *Stackup) Run(network *Network, envVars EnvList, commands ...*Command) error

Последовательный запуск набора команд на нескольких хостах, определенных в network TODO: Раздробить функциюю на несколько маленьких

type Supfile

type Supfile struct {
	Networks Networks `yaml:"networks"`
	Commands Commands `yaml:"commands"`
	Targets  Targets  `yaml:"targets"`
	Env      EnvList  `yaml:"env"`
	Version  string   `yaml:"version"`
}

Структура Supfile в формате YAML

func NewSupfile

func NewSupfile(data []byte) (*Supfile, error)

Парсим конфигурационный файл и возвращаем Supfile или ошибку

type Targets

type Targets struct {
	Names []string
	// contains filtered or unexported fields
}

Список целей (определяемых пользователем)

func (*Targets) Get

func (t *Targets) Get(name string) ([]string, bool)

func (*Targets) UnmarshalYAML

func (t *Targets) UnmarshalYAML(unmarshal func(interface{}) error) error

type Task

type Task struct {
	Run     string
	Input   io.Reader
	Clients []Client
	TTY     bool
}

Задание представляет собой набор команд, которые нужно выполнить Основная структура Task

type Upload

type Upload struct {
	Src string `yaml:"src"`
	Dst string `yaml:"dst"`
	Exc string `yaml:"exclude"`
}

Операция копирования файла из локального хоста по пути из Src в Dst

Directories

Path Synopsis
cmd
sup command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL