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 package dis
import ( import (
"encoding/json"
"net/http" "net/http"
"github.com/FAU-CDI/wisski-distillery/internal/component/instances"
"github.com/tkw1536/goprogram/stream" "github.com/tkw1536/goprogram/stream"
"golang.org/x/sync/errgroup"
) )
func (dis Dis) info(io stream.IOStream) (http.Handler, error) { func (dis *Dis) info(io stream.IOStream) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(dis.handleDis), nil
all, err := dis.Instances.All() }
if err != nil {
w.WriteHeader(http.StatusInternalServerError) const disLimit = 2
w.Write([]byte("internal server error"))
io.EPrintln(err) func (dis *Dis) handleDis(w http.ResponseWriter, r *http.Request) {
return // make sure the user is authorized
} if !dis.authDis(r) {
w.Header().Add("WWW-Authenticate", `Basic realm="WissKI Distillery Admin"`)
for _, wk := range all { w.WriteHeader(http.StatusUnauthorized)
w.WriteHeader(http.StatusOK) w.Write([]byte("Unauthorized"))
w.Write([]byte(wk.Slug)) return
w.Write([]byte("\n")) }
}
// create a new error group
}), nil 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 COPY wdcli /wdcli
EXPOSE 8888 EXPOSE 8888

View file

@ -1,7 +1,7 @@
version: "3.7" version: "3.7"
services: services:
wdresolve: dis:
build: . build: .
restart: always restart: always
environment: environment:
@ -16,6 +16,8 @@ services:
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL} LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes: volumes:
# TODO: Mount docker socket properly!
- "/var/run/docker.sock:/var/run/docker.sock"
- "${CONFIG_PATH}:${CONFIG_PATH}:ro" - "${CONFIG_PATH}:${CONFIG_PATH}:ro"
- "${DEPLOY_ROOT}:${DEPLOY_ROOT}:ro" - "${DEPLOY_ROOT}:${DEPLOY_ROOT}:ro"
- "${GLOBAL_AUTHORIZED_KEYS_FILE}:${GLOBAL_AUTHORIZED_KEYS_FILE}: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 package component
import ( import (
"bufio"
"bytes"
"errors" "errors"
"github.com/FAU-CDI/wisski-distillery/pkg/execx" "github.com/FAU-CDI/wisski-distillery/pkg/execx"
@ -119,6 +121,38 @@ func (ds Stack) Restart(io stream.IOStream) error {
return nil 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") var errStackDown = errors.New("Stack.Down: Down returned non-zero exit code")
// Down stops and removes all containers in this Stack. // Down stops and removes all containers in this Stack.

View file

@ -74,7 +74,11 @@ type Config struct {
// admin credentials for the Mysql database // admin credentials for the Mysql database
MysqlAdminUser string `env:"MYSQL_ADMIN_USER" default:"admin" parser:"nonempty"` 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 is the path this configuration was loaded from (if any)
ConfigPath string ConfigPath string

View file

@ -58,6 +58,10 @@ GLOBAL_AUTHORIZED_KEYS_FILE=${AUTHORIZED_KEYS_FILE}
GRAPHDB_ADMIN_USER=${GRAPHDB_ADMIN_USER} GRAPHDB_ADMIN_USER=${GRAPHDB_ADMIN_USER}
GRAPHDB_ADMIN_PASSWORD=${GRAPHDB_ADMIN_PASSWORD} 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_USER=${MYSQL_ADMIN_USER}
MYSQL_ADMIN_PASSWORD=${MYSQL_ADMIN_PASSWORD} 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" _ "embed"
) )
// Template is a template for the cofiguration file // Template is a template for the configuration file
type Template struct { type Template struct {
DeployRoot string `env:"DEPLOY_ROOT"` DeployRoot string `env:"DEPLOY_ROOT"`
DefaultDomain string `env:"DEFAULT_DOMAIN"` DefaultDomain string `env:"DEFAULT_DOMAIN"`
@ -24,6 +24,8 @@ type Template struct {
TriplestoreAdminPassword string `env:"GRAPHDB_ADMIN_PASSWORD"` TriplestoreAdminPassword string `env:"GRAPHDB_ADMIN_PASSWORD"`
MysqlAdminUsername string `env:"MYSQL_ADMIN_USER"` MysqlAdminUsername string `env:"MYSQL_ADMIN_USER"`
MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD"` MysqlAdminPassword string `env:"MYSQL_ADMIN_PASSWORD"`
DisAdminUsername string `env:"DIS_ADMIN_USER"`
DisAdminPassword string `env:"DIS_ADMIN_PASSWORD"`
} }
// SetDefaults sets defaults on the template // 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 return nil
} }