component/dis: Check if instance alive
This commit is contained in:
parent
37cdd201f0
commit
492a0c0404
8 changed files with 145 additions and 22 deletions
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
22
internal/component/instances/wisski_status.go
Normal file
22
internal/component/instances/wisski_status.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue