component/dis: Check if instance alive

This commit is contained in:
Tom Wiesing 2022-09-15 18:11:19 +02:00
parent 37cdd201f0
commit 492a0c0404
No known key found for this signature in database
8 changed files with 145 additions and 22 deletions

View file

@ -1,26 +1,70 @@
package dis
import (
"encoding/json"
"net/http"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/tkw1536/goprogram/stream"
"golang.org/x/sync/errgroup"
)
func (dis Dis) info(io stream.IOStream) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
all, err := dis.Instances.All()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
io.EPrintln(err)
return
}
for _, wk := range all {
w.WriteHeader(http.StatusOK)
w.Write([]byte(wk.Slug))
w.Write([]byte("\n"))
}
}), nil
func (dis *Dis) info(io stream.IOStream) (http.Handler, error) {
return http.HandlerFunc(dis.handleDis), nil
}
const disLimit = 2
func (dis *Dis) handleDis(w http.ResponseWriter, r *http.Request) {
// make sure the user is authorized
if !dis.authDis(r) {
w.Header().Add("WWW-Authenticate", `Basic realm="WissKI Distillery Admin"`)
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Unauthorized"))
return
}
// create a new error group
var errgroup errgroup.Group
errgroup.SetLimit(disLimit)
// list all the instances
all, err := dis.Instances.All()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
return
}
// get all of their info!
infos := make([]instances.Info, len(all))
for i, instance := range all {
{
i := i
instance := instance
errgroup.Go(func() (err error) {
infos[i], err = instance.Info()
return err
})
}
}
// if some info call failed
if err := errgroup.Wait(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("internal server error"))
w.Write([]byte("\n"))
return
}
// and return the json
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(infos)
}
func (dis *Dis) authDis(r *http.Request) bool {
user, pass, ok := r.BasicAuth()
return ok && user == dis.Config.DisAdminUser && pass == dis.Config.DisAdminPassword
}

View file

@ -1,4 +1,4 @@
FROM docker.io/library/alpine
FROM docker.io/library/docker:20.10-cli
COPY wdcli /wdcli
EXPOSE 8888

View file

@ -1,7 +1,7 @@
version: "3.7"
services:
wdresolve:
dis:
build: .
restart: always
environment:
@ -16,6 +16,8 @@ services:
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes:
# TODO: Mount docker socket properly!
- "/var/run/docker.sock:/var/run/docker.sock"
- "${CONFIG_PATH}:${CONFIG_PATH}:ro"
- "${DEPLOY_ROOT}:${DEPLOY_ROOT}:ro"
- "${GLOBAL_AUTHORIZED_KEYS_FILE}:${GLOBAL_AUTHORIZED_KEYS_FILE}:ro"

View file

@ -0,0 +1,22 @@
package instances
import "github.com/tkw1536/goprogram/stream"
// Info represents some info about this WissKI
type Info struct {
Slug string // The slug of the instance
Running bool // is the instance running?
}
// Info returns info about this instance
func (wisski *WissKI) Info() (info Info, err error) {
info.Slug = wisski.Slug
ps, err := wisski.Barrel().Ps(stream.FromNil())
if err != nil {
return
}
info.Running = len(ps) > 0
return
}

View file

@ -2,6 +2,8 @@
package component
import (
"bufio"
"bytes"
"errors"
"github.com/FAU-CDI/wisski-distillery/pkg/execx"
@ -119,6 +121,38 @@ func (ds Stack) Restart(io stream.IOStream) error {
return nil
}
var errStackPs = errors.New("Stack.Ps: Down returned non-zero exit code")
// Ps returns the ids of the containers currently running
func (ds Stack) Ps(io stream.IOStream) ([]string, error) {
// create a buffer
var buffer bytes.Buffer
// read the ids from the command!
code, err := ds.compose(io.Streams(&buffer, nil, nil, 0), "ps", "-q")
if err != nil {
return nil, err
}
if code != 0 {
return nil, errStackPs
}
// scan each of the lines
var results []string
scanner := bufio.NewScanner(&buffer)
for scanner.Scan() {
if text := scanner.Text(); text != "" {
results = append(results, text)
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
// return them!
return results, nil
}
var errStackDown = errors.New("Stack.Down: Down returned non-zero exit code")
// Down stops and removes all containers in this Stack.

View file

@ -74,7 +74,11 @@ type Config struct {
// admin credentials for the Mysql database
MysqlAdminUser string `env:"MYSQL_ADMIN_USER" default:"admin" parser:"nonempty"`
MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD" default:"admin" parser:"nonempty"`
MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD" default:"" parser:"nonempty"`
// admin credentials for the dis server
DisAdminUser string `env:"DIS_ADMIN_USER" default:"admin" parser:"nonempty"`
DisAdminPassword string `env:"DIS_ADMIN_PASSWORD" default:"" parser:"nonempty"`
// ConfigPath is the path this configuration was loaded from (if any)
ConfigPath string

View file

@ -58,6 +58,10 @@ GLOBAL_AUTHORIZED_KEYS_FILE=${AUTHORIZED_KEYS_FILE}
GRAPHDB_ADMIN_USER=${GRAPHDB_ADMIN_USER}
GRAPHDB_ADMIN_PASSWORD=${GRAPHDB_ADMIN_PASSWORD}
# The admin password to use for access to mysql
# The admin user and password of the MySQL interface, to be used for provisioning
MYSQL_ADMIN_USER=${MYSQL_ADMIN_USER}
MYSQL_ADMIN_PASSWORD=${MYSQL_ADMIN_PASSWORD}
# The admin user and password required to access the /dis/ server and api
DIS_ADMIN_USER=${DIS_ADMIN_USER}
DIS_ADMIN_PASSWORD=${DIS_ADMIN_PASSWORD}

View file

@ -14,7 +14,7 @@ import (
_ "embed"
)
// Template is a template for the cofiguration file
// Template is a template for the configuration file
type Template struct {
DeployRoot string `env:"DEPLOY_ROOT"`
DefaultDomain string `env:"DEFAULT_DOMAIN"`
@ -24,6 +24,8 @@ type Template struct {
TriplestoreAdminPassword string `env:"GRAPHDB_ADMIN_PASSWORD"`
MysqlAdminUsername string `env:"MYSQL_ADMIN_USER"`
MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD"`
DisAdminUsername string `env:"DIS_ADMIN_USER"`
DisAdminPassword string `env:"DIS_ADMIN_PASSWORD"`
}
// SetDefaults sets defaults on the template
@ -66,6 +68,17 @@ func (tpl *Template) SetDefaults() (err error) {
}
}
if tpl.DisAdminUsername == "" {
tpl.DisAdminUsername = "admin"
}
if tpl.DisAdminPassword == "" {
tpl.DisAdminPassword, err = password.Password(64)
if err != nil {
return err
}
}
return nil
}