signed-commit remote shell (see also https://github.com/dyne/scorsh)
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
4.7 KiB
197 lines
4.7 KiB
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"golang.org/x/crypto/openpgp"
|
|
"os"
|
|
)
|
|
|
|
// error constants
|
|
const (
|
|
errNoFile = -(1 << iota)
|
|
errKeyring
|
|
errNoRepo
|
|
errNoCommit
|
|
errSignature
|
|
)
|
|
|
|
// spoolMsg type represents messages received from the spool and
|
|
// sent to workers
|
|
type spoolMsg struct {
|
|
ID string `yaml:"m_id"`
|
|
Repo string `yaml:"m_repo"`
|
|
Branch string `yaml:"m_branch"`
|
|
OldRev string `yaml:"m_oldrev"`
|
|
NewRev string `yaml:"m_newrev"`
|
|
Path string
|
|
}
|
|
|
|
// An action represents a script of a command configured on the server side
|
|
type action struct {
|
|
URL string `yaml:"a_url"`
|
|
Hash string `yaml:"a_hash"`
|
|
}
|
|
|
|
// commandCfg represents a command configured on the server side
|
|
type commandCfg struct {
|
|
Name string `yaml:"c_name"`
|
|
Keyrings []string `yaml:"c_keyrings"`
|
|
Actions []action `yaml:"c_actions"`
|
|
}
|
|
|
|
type commandState struct {
|
|
Env []string
|
|
Args []string
|
|
}
|
|
|
|
type command struct {
|
|
commandCfg
|
|
commandState
|
|
}
|
|
|
|
// workerCfg represents the static configuration of a worker
|
|
type workerCfg struct {
|
|
Name string `yaml:"w_name"`
|
|
Repos []string `yaml:"w_repos"`
|
|
Folder string `yaml:"w_folder"`
|
|
LogFile string `yaml:"w_logfile"`
|
|
CfgFile string `yaml:"w_cfgfile"`
|
|
// Keyrings []string `yaml:"w_keyrings"`
|
|
Commands []commandCfg `yaml:"w_commands"`
|
|
CommandKeys map[string]map[string]bool
|
|
}
|
|
|
|
// workerState represents the runtime state of a worker
|
|
type workerState struct {
|
|
Keys map[string]openpgp.KeyRing
|
|
MsgChan chan spoolMsg
|
|
StatusChan chan spoolMsg
|
|
}
|
|
|
|
// worker represents the configuration and state of a worker
|
|
type worker struct {
|
|
workerCfg `yaml:",inline"`
|
|
workerState
|
|
}
|
|
|
|
// masterCfg represents the static configuration of the master
|
|
type masterCfg struct {
|
|
Spooldir string `yaml:"s_spooldir"`
|
|
LogFile string `yaml:"s_logfile"`
|
|
LogPrefix string `yaml:"s_logprefix"`
|
|
Workers []worker `yaml:"s_workers"`
|
|
}
|
|
|
|
// masterState represents the runtime state of the master
|
|
type masterState struct {
|
|
Spooler chan spoolMsg
|
|
StatusChan chan spoolMsg
|
|
Repos map[string][]*worker
|
|
WorkingMsg map[string]int
|
|
}
|
|
|
|
// master represents the configuration and state of the master
|
|
type master struct {
|
|
masterCfg `yaml:",inline"`
|
|
masterState
|
|
}
|
|
|
|
// clientCmd is the type of commands sent by clients
|
|
type clientCmd struct {
|
|
Cmd string `yaml:"s_cmd"`
|
|
Args []string `yaml:"s_args"`
|
|
}
|
|
|
|
// clientMsg is the list of commands sent by a client
|
|
type clientMsg struct {
|
|
Commands []clientCmd `yaml:"scorsh"`
|
|
}
|
|
|
|
////////////////////////
|
|
|
|
func (cfg *master) String() string {
|
|
|
|
var buff bytes.Buffer
|
|
|
|
fmt.Fprintf(&buff, "spooldir: %s\n", cfg.Spooldir)
|
|
fmt.Fprintf(&buff, "logfile: %s\n", cfg.LogFile)
|
|
fmt.Fprintf(&buff, "logprefix: %s\n", cfg.LogPrefix)
|
|
fmt.Fprintf(&buff, "Workers: \n")
|
|
|
|
for _, w := range cfg.Workers {
|
|
fmt.Fprintf(&buff, "%s", &w)
|
|
}
|
|
|
|
return buff.String()
|
|
}
|
|
|
|
func (msg *spoolMsg) String() string {
|
|
|
|
var buff bytes.Buffer
|
|
fmt.Fprintf(&buff, "Id: %s\n", msg.ID)
|
|
fmt.Fprintf(&buff, "Repo: %s\n", msg.Repo)
|
|
fmt.Fprintf(&buff, "Branch: %s\n", msg.Branch)
|
|
fmt.Fprintf(&buff, "OldRev: %s\n", msg.OldRev)
|
|
fmt.Fprintf(&buff, "Newrev: %s\n", msg.NewRev)
|
|
fmt.Fprintf(&buff, "Path: %s\n", msg.Path)
|
|
|
|
return buff.String()
|
|
|
|
}
|
|
|
|
func (w *worker) String() string {
|
|
|
|
var buff bytes.Buffer
|
|
fmt.Fprintf(&buff, "Name: %s\n", w.Name)
|
|
fmt.Fprintf(&buff, "Repos: %s\n", w.Repos)
|
|
fmt.Fprintf(&buff, "Folder: %s\n", w.Folder)
|
|
fmt.Fprintf(&buff, "LogFile: %s\n", w.LogFile)
|
|
fmt.Fprintf(&buff, "CfgFile: %s\n", w.CfgFile)
|
|
// fmt.Fprintf(&buff, "Keyrings: %s\n", w.Keyrings)
|
|
|
|
return buff.String()
|
|
}
|
|
|
|
func (msg *clientMsg) String() string {
|
|
|
|
var buff bytes.Buffer
|
|
|
|
for _, c := range msg.Commands {
|
|
|
|
fmt.Fprintf(&buff, "s_cmd: %s\n", c.Cmd)
|
|
for _, a := range c.Args {
|
|
fmt.Fprintf(&buff, " s_args: %s\n", a)
|
|
}
|
|
}
|
|
|
|
return buff.String()
|
|
|
|
}
|
|
|
|
func (cmd *command) setEnvironment(msg *spoolMsg, commitID, author, committer string) {
|
|
|
|
env := os.Environ()
|
|
env = append(env, fmt.Sprintf("SCORSH_REPO=%s", msg.Repo))
|
|
env = append(env, fmt.Sprintf("SCORSH_BRANCH=%s", msg.Branch))
|
|
env = append(env, fmt.Sprintf("SCORSH_OLDREV=%s", msg.OldRev))
|
|
env = append(env, fmt.Sprintf("SCORSH_NEWREV=%s", msg.NewRev))
|
|
env = append(env, fmt.Sprintf("SCORSH_ID=%s", msg.ID))
|
|
env = append(env, fmt.Sprintf("SCORSH_COMMIT=%s", commitID))
|
|
env = append(env, fmt.Sprintf("SCORSH_COMMAND=%s", cmd.Name))
|
|
env = append(env, fmt.Sprintf("SCORSH_AUTHOR=%s", author))
|
|
env = append(env, fmt.Sprintf("SCORSH_COMMITTER=%s", committer))
|
|
cmd.Env = env
|
|
}
|
|
|
|
func (cmd *command) String() string {
|
|
|
|
var buff bytes.Buffer
|
|
|
|
fmt.Fprintf(&buff, "Name: %s\n", cmd.Name)
|
|
fmt.Fprintf(&buff, "Keyrings: %s\n", cmd.Keyrings)
|
|
fmt.Fprintf(&buff, "Actions: %s\n", cmd.Actions)
|
|
fmt.Fprintf(&buff, "Env: %s\n", cmd.Env)
|
|
fmt.Fprintf(&buff, "Args: %s\n", cmd.Args)
|
|
return buff.String()
|
|
}
|
|
|