Do a large chunk of the move to go
This commit moves a huge chunk of the code to go. The TODO.md document indicates what is left to be done.
This commit is contained in:
parent
db2ad9b4bd
commit
7b38fdd801
93 changed files with 4689 additions and 645 deletions
54
cmd/blind_update.go
Normal file
54
cmd/blind_update.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// BlindUpdate is the 'blind-update' command
|
||||
var BlindUpdate wisski_distillery.Command = blindUpdate{}
|
||||
|
||||
type blindUpdate struct {
|
||||
Force bool `short:"f" long:"force" description:"force running blind-update even if AutoBlindUpdate is set to false"`
|
||||
Positionals struct {
|
||||
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug of instance(s) to run blind-update in"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (blindUpdate) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "blind_update",
|
||||
Description: "Runs the blind update in the provided instances",
|
||||
}
|
||||
}
|
||||
|
||||
var errBlindUpdateFailed = exit.Error{
|
||||
Message: "Failed to run blind update script for instance %q: exited with code %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (bu blindUpdate) Run(context wisski_distillery.Context) error {
|
||||
instances, err := context.Environment.Instances(bu.Positionals.Slug...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, instance := range instances {
|
||||
if !(instance.IsBlindUpdateEnabled() || bu.Force) {
|
||||
context.EPrintf("skipping instance %q\n", instance.Slug)
|
||||
continue
|
||||
}
|
||||
context.EPrintf("Updating instance %s\n", instance.Slug)
|
||||
|
||||
code := instance.Shell(context.IOStream, "/utils/blind_update.sh")
|
||||
if code != 0 {
|
||||
return errBlindUpdateFailed.WithMessageF(instance.Slug, code)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
187
cmd/bootstrap.go
Normal file
187
cmd/bootstrap.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
cfg "github.com/FAU-CDI/wisski-distillery/internal/config"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/hostname"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/password"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Bootstrap is the 'bootstrap' command
|
||||
var Bootstrap wisski_distillery.Command = bootstrap{}
|
||||
|
||||
type bootstrap struct {
|
||||
Directory string `short:"r" long:"root-directory" description:"path to the root deployment directory" default:"/var/www/deploy"`
|
||||
Hostname string `short:"h" long:"hostname" description:"default hostname of the distillery (default: system hostname)"`
|
||||
}
|
||||
|
||||
func (bootstrap) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: false,
|
||||
},
|
||||
Command: "bootstrap",
|
||||
Description: "Bootstraps the installation of a Distillery System",
|
||||
}
|
||||
}
|
||||
|
||||
var errBootstrapDifferent = exit.Error{
|
||||
Message: "refusing to bootstrap: base directory is already set to %s.",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBootstrapFailedToCreateDirectory = exit.Error{
|
||||
Message: "failed to create directory %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBootstrapFailedToSaveDirectory = exit.Error{
|
||||
Message: "failed to register base directory: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBoostrapFailedToCopyExe = exit.Error{
|
||||
Message: "failed to copy wdcli executable: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBootstrapWriteConfig = exit.Error{
|
||||
Message: "failed to write configuration file: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBootstrapOpenConfig = exit.Error{
|
||||
Message: "failed to open configuration file: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errBootstrapCreateFile = exit.Error{
|
||||
Message: "failed to touch configuration file: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (bs bootstrap) Run(context wisski_distillery.Context) error {
|
||||
root := bs.Directory
|
||||
|
||||
// check that we didn't get a different base directory
|
||||
{
|
||||
got, err := env.ReadBaseDirectory()
|
||||
if err == nil && got != "" && got != root {
|
||||
return errBootstrapDifferent.WithMessageF(got)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
logging.LogMessage(context.IOStream, "Creating root deployment directory")
|
||||
if err := os.MkdirAll(root, fs.ModeDir); err != nil {
|
||||
return errBootstrapFailedToCreateDirectory.WithMessageF(root)
|
||||
}
|
||||
if err := env.WriteBaseDirectory(root); err != nil {
|
||||
return errBootstrapFailedToSaveDirectory.WithMessageF(root)
|
||||
}
|
||||
context.Println(root)
|
||||
}
|
||||
|
||||
// TODO: Read these from the command line?
|
||||
wdcliPath := filepath.Join(root, "wdcli")
|
||||
envPath := filepath.Join(root, ".env")
|
||||
domain := bs.Hostname
|
||||
if domain == "" {
|
||||
domain = hostname.FQDN()
|
||||
}
|
||||
overridesPath := filepath.Join(root, "overrides.json")
|
||||
authorizedKeysFile := filepath.Join(root, "authorized_keys")
|
||||
|
||||
{
|
||||
logging.LogMessage(context.IOStream, "Copying over wdcli executable")
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return errBoostrapFailedToCopyExe.WithMessageF(err)
|
||||
}
|
||||
|
||||
err = fsx.CopyFile(wdcliPath, exe)
|
||||
if err != nil && err != fsx.ErrCopySameFile {
|
||||
return errBoostrapFailedToCopyExe.WithMessageF(err)
|
||||
}
|
||||
context.Println(wdcliPath)
|
||||
}
|
||||
|
||||
{
|
||||
if !fsx.IsFile(envPath) {
|
||||
if err := logging.LogOperation(func() error {
|
||||
password, err := password.Password(128)
|
||||
if err != nil {
|
||||
return errBootstrapWriteConfig.WithMessageF(err)
|
||||
}
|
||||
|
||||
if err := distillery.InstallTemplate(envPath, filepath.Join("resources", "templates", "bootstrap", "env"), map[string]string{
|
||||
"DEPLOY_ROOT": root,
|
||||
"DEFAULT_DOMAIN": domain,
|
||||
"SELF_OVERRIDES_FILE": overridesPath,
|
||||
"AUTHORIZED_KEYS_FILE": authorizedKeysFile,
|
||||
|
||||
"GRAPHDB_ADMIN_USER": "admin",
|
||||
"GRAPHDB_ADMIN_PASSWORD": password[:64],
|
||||
|
||||
"MYSQL_ADMIN_USER": "admin",
|
||||
"MYSQL_ADMIN_PASSWORD": password[64:],
|
||||
}); err != nil {
|
||||
return errBootstrapWriteConfig.WithMessageF(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Installing configuration file"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
|
||||
context.Println(overridesPath)
|
||||
if err := distillery.InstallTemplate(overridesPath, filepath.Join("resources", "templates", "bootstrap", "overrides.json"), map[string]string{}); err != nil {
|
||||
return errBootstrapCreateFile.WithMessageF(err)
|
||||
}
|
||||
|
||||
context.Println(authorizedKeysFile)
|
||||
if err := distillery.InstallTemplate(authorizedKeysFile, filepath.Join("resources", "templates", "bootstrap", "global_authorized_keys"), map[string]string{}); err != nil {
|
||||
return errBootstrapCreateFile.WithMessageF(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Creating additional config files"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// re-read the configuration and print it!
|
||||
logging.LogMessage(context.IOStream, "Configuration is now complete")
|
||||
f, err := os.Open(envPath)
|
||||
if err != nil {
|
||||
return errBootstrapOpenConfig.WithMessageF(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var config cfg.Config
|
||||
if err := config.Unmarshal(f); err != nil {
|
||||
return errBootstrapOpenConfig.WithMessageF(err)
|
||||
}
|
||||
context.Println(config)
|
||||
|
||||
// Tell the user how to proceed
|
||||
logging.LogMessage(context.IOStream, "Bootstrap is complete")
|
||||
context.Printf("Adjust the configuration file at %s\n", envPath)
|
||||
context.Printf("Then grab a GraphDB zipped source file and run:\n")
|
||||
context.Printf("%s system_update /path/to/graphdb.zip\n", wdcliPath)
|
||||
|
||||
return nil
|
||||
}
|
||||
27
cmd/config.go
Normal file
27
cmd/config.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
)
|
||||
|
||||
// Config is the configuration command
|
||||
var Config wisski_distillery.Command = config{}
|
||||
|
||||
type config struct {
|
||||
}
|
||||
|
||||
func (s config) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "config",
|
||||
Description: "Prints information about configuration",
|
||||
}
|
||||
}
|
||||
|
||||
func (s config) Run(context wisski_distillery.Context) error {
|
||||
context.Printf("%#v", context.Environment.Config)
|
||||
return nil
|
||||
}
|
||||
55
cmd/cron.go
Normal file
55
cmd/cron.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Cron is the 'cron' command
|
||||
var Cron wisski_distillery.Command = cron{}
|
||||
|
||||
type cron struct {
|
||||
Positionals struct {
|
||||
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug of instance(s) to run cron in"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (cron) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "cron",
|
||||
Description: "Runs the cron script for several instances",
|
||||
}
|
||||
}
|
||||
|
||||
var errCronFailed = exit.Error{
|
||||
Message: "Failed to run cron script for instance %q: exited with code %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (cr cron) Run(context wisski_distillery.Context) error {
|
||||
instances, err := context.Environment.Instances(cr.Positionals.Slug...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// iterate over the instances and store the last value of error
|
||||
for _, instance := range instances {
|
||||
logging.LogOperation(func() error {
|
||||
code := instance.Shell(context.IOStream, "/utils/cron.sh")
|
||||
if code != 0 {
|
||||
// keep going, because we want to run as many crons as possible
|
||||
err = errBlindUpdateFailed.WithMessageF(instance.Slug, code)
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "running cron for instance %s", instance.Slug)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
45
cmd/info.go
Normal file
45
cmd/info.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
)
|
||||
|
||||
// Info is then 'info' command
|
||||
var Info wisski_distillery.Command = info{}
|
||||
|
||||
type info struct {
|
||||
Positionals struct {
|
||||
Slug string `positional-arg-name:"SLUG" required:"1-1" description:"slug of instance to show info about"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (info) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "info",
|
||||
Description: "Provide information about a single repository",
|
||||
}
|
||||
}
|
||||
|
||||
func (i info) Run(context wisski_distillery.Context) error {
|
||||
instance, err := context.Environment.Instance(i.Positionals.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.Printf("URL: %s\n", instance.URL())
|
||||
context.Printf("Base directory: %s\n", instance.FilesystemBase)
|
||||
|
||||
context.Printf("SQL Database: %s\n", instance.SqlDatabase)
|
||||
context.Printf("SQL Username: %s\n", instance.SqlUser)
|
||||
context.Printf("SQL Password: %s\n", instance.SqlPassword)
|
||||
|
||||
context.Printf("GraphDB Repository: %s\n", instance.GraphDBRepository)
|
||||
context.Printf("GraphDB Username: %s\n", instance.GraphDBUser)
|
||||
context.Printf("GraphDB Password: %s\n", instance.GraphDBPassword)
|
||||
|
||||
return nil
|
||||
}
|
||||
47
cmd/license.go
Normal file
47
cmd/license.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/legal"
|
||||
)
|
||||
|
||||
// License is the 'wdcli license' command.
|
||||
//
|
||||
// The license command prints to standard output legal notices about the wdcli program.
|
||||
var License wisski_distillery.Command = license{}
|
||||
|
||||
type license struct{}
|
||||
|
||||
func (license) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: false,
|
||||
},
|
||||
Command: "license",
|
||||
Description: "Print licensing information about wdcli and exit. ",
|
||||
}
|
||||
}
|
||||
|
||||
func (license) AfterParse() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (license) Run(context wisski_distillery.Context) error {
|
||||
context.Printf(stringLicenseInfo, wisski_distillery.License, legal.Notices)
|
||||
return nil
|
||||
}
|
||||
|
||||
const stringLicenseInfo = `
|
||||
wdcli -- WissKI Distillery Command Line Utility
|
||||
https://github.com/FAU-CDI/wisski-distillery
|
||||
|
||||
================================================================================
|
||||
wdcli is licensed under the terms of the AGPL Version 3.0 License:
|
||||
|
||||
%s
|
||||
================================================================================
|
||||
|
||||
Furthermore, this executable may include code from the following projects:
|
||||
%s
|
||||
`
|
||||
38
cmd/ls.go
Normal file
38
cmd/ls.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
)
|
||||
|
||||
// Ls is the 'ls' command
|
||||
var Ls wisski_distillery.Command = ls{}
|
||||
|
||||
type ls struct {
|
||||
Positionals struct {
|
||||
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug(s) of instance(s) to list"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (ls) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "ls",
|
||||
Description: "Lists WissKI instances",
|
||||
}
|
||||
}
|
||||
|
||||
func (l ls) Run(context wisski_distillery.Context) error {
|
||||
instances, err := context.Environment.Instances(l.Positionals.Slug...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, instance := range instances {
|
||||
context.Println(instance.Slug)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
67
cmd/make_mysql_account.go
Normal file
67
cmd/make_mysql_account.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/sqle"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/parser"
|
||||
)
|
||||
|
||||
// Shell is the 'shell' command
|
||||
var MakeMysqlAccount wisski_distillery.Command = makeMysqlAccount{}
|
||||
|
||||
type makeMysqlAccount struct{}
|
||||
|
||||
func (makeMysqlAccount) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
},
|
||||
Command: "make_mysql_account",
|
||||
Description: "Open a shell in the provided instance",
|
||||
}
|
||||
}
|
||||
|
||||
var errUnableToReadUsername = exit.Error{
|
||||
ExitCode: exit.ExitGeneric,
|
||||
Message: "unable to read username: %s",
|
||||
}
|
||||
|
||||
var errUnableToReadPassword = exit.Error{
|
||||
ExitCode: exit.ExitGeneric,
|
||||
Message: "unable to read password: %s",
|
||||
}
|
||||
|
||||
func (mma makeMysqlAccount) Run(context wisski_distillery.Context) error {
|
||||
context.Printf("Username>")
|
||||
username, err := context.ReadLine()
|
||||
if err != nil {
|
||||
return errUnableToReadUsername.WithMessageF(err)
|
||||
}
|
||||
|
||||
context.Printf("Password>")
|
||||
password, err := context.ReadPassword()
|
||||
if err != nil {
|
||||
return errUnableToReadPassword.WithMessageF(err)
|
||||
}
|
||||
|
||||
query := sqle.Format("CREATE USER ?@'%' IDENTIFIED BY ?; GRANT ALL PRIVILEGES ON *.* TO ?@`%` WITH GRANT OPTION; FLUSH PRIVILEGES;", username, password, username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
code := context.Environment.SQLShell(context.IOStream, "-e", query)
|
||||
|
||||
if code != 0 {
|
||||
return exit.Error{
|
||||
ExitCode: exit.ExitCode(uint8(code)),
|
||||
Message: fmt.Sprintf("Exit code %d", code),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
43
cmd/mysql.go
Normal file
43
cmd/mysql.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/parser"
|
||||
)
|
||||
|
||||
// Mysql is the 'mysql' command
|
||||
var Mysql wisski_distillery.Command = mysql{}
|
||||
|
||||
type mysql struct {
|
||||
Positionals struct {
|
||||
Args []string `positional-arg-name:"ARGS" description:"arguments to pass to the mysql command"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (mysql) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
},
|
||||
Command: "mysql",
|
||||
Description: "Opens a mysql shell",
|
||||
}
|
||||
}
|
||||
|
||||
func (ms mysql) Run(context wisski_distillery.Context) error {
|
||||
code := context.Environment.SQLShell(context.IOStream, ms.Positionals.Args...)
|
||||
if code != 0 {
|
||||
return exit.Error{
|
||||
ExitCode: exit.ExitCode(uint8(code)),
|
||||
Message: fmt.Sprintf("Exit code %d", code),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
77
cmd/prefix.go
Normal file
77
cmd/prefix.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Cron is the 'cron' command
|
||||
var UpdatePrefixConfig wisski_distillery.Command = updateprefixconfig{}
|
||||
|
||||
type updateprefixconfig struct{}
|
||||
|
||||
func (updateprefixconfig) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "update_prefix_config",
|
||||
Description: "Updates the prefix configuration",
|
||||
}
|
||||
}
|
||||
|
||||
var errPrefixUpdateFailed = exit.Error{
|
||||
Message: "Failed to update the prefix configuration: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (upc updateprefixconfig) Run(context wisski_distillery.Context) error {
|
||||
dis := context.Environment
|
||||
|
||||
instances, err := dis.AllInstances()
|
||||
if err != nil {
|
||||
return errPrefixUpdateFailed.WithMessageF(err)
|
||||
}
|
||||
|
||||
target := dis.ResolverPrefixConfig()
|
||||
|
||||
// print the configuration
|
||||
config, err := os.OpenFile(target, os.O_WRONLY, fs.ModePerm)
|
||||
if err != nil {
|
||||
return errPrefixUpdateFailed.WithMessageF(err)
|
||||
}
|
||||
|
||||
// iterate over the instances and store the last value of error
|
||||
for _, instance := range instances {
|
||||
if err := logging.LogOperation(func() error {
|
||||
// read the prefix config
|
||||
data, err := instance.PrefixConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
context.IOStream.Printf("%s", data)
|
||||
|
||||
// and write it out!
|
||||
if _, err := config.WriteString(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "reading prefix config %s", instance.Slug); err != nil {
|
||||
return errPrefixUpdateFailed.WithMessageF(err)
|
||||
}
|
||||
}
|
||||
|
||||
// and restart the resolver to apply the config!
|
||||
logging.LogMessage(context.IOStream, "restarting resolver stack")
|
||||
if err := dis.ResolverStack().Restart(context.IOStream); err != nil {
|
||||
return errPrefixUpdateFailed.WithMessageF(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
121
cmd/provision.go
Normal file
121
cmd/provision.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Provision is the 'provision' command
|
||||
var Provision wisski_distillery.Command = provision{}
|
||||
|
||||
type provision struct {
|
||||
Positionals struct {
|
||||
Slug string `positional-arg-name:"slug" required:"1-1" description:"name of WissKI Instance to create"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (provision) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "provision",
|
||||
Description: "Creates a new WissKI Instance",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: AfterParse to check instance!
|
||||
|
||||
var errProvisionAlreadyExists = exit.Error{
|
||||
Message: "Instance %q already exists",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errProvisionGeneric = exit.Error{
|
||||
Message: "Unable to provision instance %s: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (p provision) Run(context wisski_distillery.Context) error {
|
||||
dis := context.Environment
|
||||
slug := p.Positionals.Slug
|
||||
|
||||
// check that it doesn't already exist
|
||||
logging.LogMessage(context.IOStream, "Provisioning new WissKI instance %s", slug)
|
||||
if exists, err := dis.HasInstance(slug); err != nil || exists {
|
||||
return errProvisionAlreadyExists.WithMessageF(slug)
|
||||
}
|
||||
|
||||
// make it in-memory
|
||||
instance, err := dis.NewInstance(slug)
|
||||
if err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
// check that the base directory does not exist
|
||||
logging.LogMessage(context.IOStream, "Checking that base directory %s does not exist", instance.FilesystemBase)
|
||||
if fsx.IsDirectory(instance.FilesystemBase) {
|
||||
return errProvisionAlreadyExists.WithMessageF(slug)
|
||||
}
|
||||
|
||||
// Store in bookkeeping
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := instance.Update(); err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Updating bookkeeping database"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the sql
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := dis.SQLProvision(instance.SqlDatabase, instance.SqlUser, instance.SqlPassword); err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Provisioning SQL Database"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the triplestore
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := dis.TriplestoreProvision(instance.GraphDBRepository, instance.Domain(), instance.GraphDBUser, instance.GraphDBPassword); err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Provisioning Triplestore"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// run the provision script
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := instance.Provision(context.IOStream); err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Running setup scripts"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start the container!
|
||||
logging.LogMessage(context.IOStream, "Starting Container")
|
||||
if err := instance.Stack().Up(context.IOStream); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// and we're done!
|
||||
logging.LogMessage(context.IOStream, "Instance has been provisioned")
|
||||
context.Printf("URL: %s\n", instance.URL().String())
|
||||
context.Printf("Username: %s\n", instance.DrupalUsername)
|
||||
context.Printf("Password: %s\n", instance.DrupalPassword)
|
||||
|
||||
return nil
|
||||
}
|
||||
117
cmd/purge.go
Normal file
117
cmd/purge.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Provision is the 'provision' command
|
||||
var Purge wisski_distillery.Command = purge{}
|
||||
|
||||
type purge struct {
|
||||
Yes bool `short:"y" long:"yes" description:"Skip asking for confirmation"`
|
||||
Positionals struct {
|
||||
Slug string `positional-arg-name:"slug" required:"1-1" description:"name of WissKI Instance to purge"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (purge) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "purge",
|
||||
Description: "Purges a WissKI Instance",
|
||||
}
|
||||
}
|
||||
|
||||
var errPurgeNoDetails = exit.Error{
|
||||
Message: "Unable to find instance details for purge: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errPurgeNoConfirmation = exit.Error{
|
||||
Message: "Aborting after request was not confirmed. Either type `yes` or pass `--yes` on the command line",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (p purge) Run(context wisski_distillery.Context) error {
|
||||
dis := context.Environment
|
||||
slug := p.Positionals.Slug
|
||||
|
||||
// check the confirmation from the user
|
||||
if !p.Yes {
|
||||
context.Printf("About to remove repository %s. This cannot be undone.\n", slug)
|
||||
context.Printf("Type 'yes' to continue: ")
|
||||
line, err := context.ReadLine()
|
||||
if err != nil || line != "yes" {
|
||||
return errPurgeNoConfirmation
|
||||
}
|
||||
}
|
||||
|
||||
// load the instance (first via bookkeeping, then via defaults)
|
||||
logging.LogMessage(context.IOStream, "Checking bookkeeping table")
|
||||
instance, err := dis.Instance(slug)
|
||||
if err == env.ErrInstanceNotFound {
|
||||
context.Println("Not found in bookkeeping table, assuming defaults")
|
||||
instance, err = dis.NewInstance(slug)
|
||||
}
|
||||
if err != nil {
|
||||
return errPurgeNoDetails.WithMessageF(err)
|
||||
}
|
||||
|
||||
// remove docker stack
|
||||
logging.LogMessage(context.IOStream, "Stopping and removing docker container")
|
||||
if err := instance.Stack().Down(context.IOStream); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
// remove the filesystem
|
||||
logging.LogMessage(context.IOStream, "Removing from filesystem %s", instance.FilesystemBase)
|
||||
if err := os.RemoveAll(instance.FilesystemBase); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
// remove the triplestore
|
||||
logging.LogOperation(func() error {
|
||||
logging.LogMessage(context.IOStream, "Removing user %s", instance.GraphDBUser)
|
||||
if err := dis.TriplestorePurgeUser(instance.GraphDBUser); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
logging.LogMessage(context.IOStream, "Removing repository %s", instance.GraphDBRepository)
|
||||
if err := dis.TriplestorePurgeRepo(instance.GraphDBRepository); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Removing from Triplestore")
|
||||
|
||||
// remove the sql
|
||||
logging.LogOperation(func() error {
|
||||
logging.LogMessage(context.IOStream, "Removing user %s", instance.SqlUser)
|
||||
if err := dis.SQLPurgeUser(instance.SqlUser); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
logging.LogMessage(context.IOStream, "Removing database %s", instance.SqlDatabase)
|
||||
if err := dis.SQLPurgeDatabase(instance.SqlDatabase); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Removing from SQL")
|
||||
|
||||
// remove from bookkeeping
|
||||
logging.LogMessage(context.IOStream, "Removing instance from bookkeeping")
|
||||
if err := instance.Delete(); err != nil {
|
||||
context.EPrintln(err)
|
||||
}
|
||||
|
||||
logging.LogMessage(context.IOStream, "Instance %s has been purged", slug)
|
||||
return nil
|
||||
}
|
||||
65
cmd/rebuild.go
Normal file
65
cmd/rebuild.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/stack"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Cron is the 'cron' command
|
||||
var Rebuild wisski_distillery.Command = rebuild{}
|
||||
|
||||
type rebuild struct {
|
||||
Positionals struct {
|
||||
Slug []string `positional-arg-name:"SLUG" required:"0" description:"slug of instance(s) to run rebuild"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (rebuild) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "rebuild",
|
||||
Description: "Runs the rebuild script for several instances",
|
||||
}
|
||||
}
|
||||
|
||||
var errRebuildFailed = exit.Error{
|
||||
Message: "Failed to run rebuild script for instance %q: exited with code %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (rb rebuild) Run(context wisski_distillery.Context) error {
|
||||
instances, err := context.Environment.Instances(rb.Positionals.Slug...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// iterate over the instances and store the last value of error
|
||||
var globalErr error
|
||||
for _, instance := range instances {
|
||||
logging.LogOperation(func() error {
|
||||
s := instance.Stack()
|
||||
if err := logging.LogOperation(func() error {
|
||||
return s.Install(context.IOStream, stack.InstallationContext{})
|
||||
}, context.IOStream, "Installing docker stack"); err != nil {
|
||||
globalErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
return s.Update(context.IOStream, true)
|
||||
}, context.IOStream, "Updating docker stack"); err != nil {
|
||||
globalErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, context.IOStream, "Rebuilding instance %s", instance.Slug)
|
||||
}
|
||||
|
||||
return globalErr
|
||||
}
|
||||
86
cmd/reserve.go
Normal file
86
cmd/reserve.go
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/stack"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
)
|
||||
|
||||
// Reserve is the 'reserve' command
|
||||
var Reserve wisski_distillery.Command = reserve{}
|
||||
|
||||
type reserve struct {
|
||||
Positionals struct {
|
||||
Slug string `positional-arg-name:"slug" required:"1-1" description:"name of WissKI Instance to reserve"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (reserve) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
Command: "reserve",
|
||||
Description: "Reserves a new WissKI Instance",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: AfterParse to check instance!
|
||||
|
||||
var errReserveAlreadyExists = exit.Error{
|
||||
Message: "Instance %q already exists",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errReserveGeneric = exit.Error{
|
||||
Message: "Unable to provision instance %s: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (r reserve) Run(context wisski_distillery.Context) error {
|
||||
dis := context.Environment
|
||||
slug := r.Positionals.Slug
|
||||
|
||||
// check that it doesn't already exist
|
||||
logging.LogMessage(context.IOStream, "Reserving new WissKI instance %s", slug)
|
||||
if exists, err := dis.HasInstance(slug); err != nil || exists {
|
||||
return errProvisionAlreadyExists.WithMessageF(slug)
|
||||
}
|
||||
|
||||
// make it in-memory
|
||||
instance, err := dis.NewInstance(slug)
|
||||
if err != nil {
|
||||
return errProvisionGeneric.WithMessageF(slug, err)
|
||||
}
|
||||
|
||||
// check that the base directory does not exist
|
||||
logging.LogMessage(context.IOStream, "Checking that base directory %s does not exist", instance.FilesystemBase)
|
||||
if fsx.IsDirectory(instance.FilesystemBase) {
|
||||
return errProvisionAlreadyExists.WithMessageF(slug)
|
||||
}
|
||||
|
||||
// setup docker stack
|
||||
s := instance.ReserveStack()
|
||||
{
|
||||
if err := logging.LogOperation(func() error {
|
||||
return s.Install(context.IOStream, stack.InstallationContext{})
|
||||
}, context.IOStream, "Installing docker stack"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
return s.Update(context.IOStream, true)
|
||||
}, context.IOStream, "Updating docker stack"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// and we're done!
|
||||
logging.LogMessage(context.IOStream, "Instance has been reserved")
|
||||
context.Printf("URL: %s\n", instance.URL().String())
|
||||
|
||||
return nil
|
||||
}
|
||||
49
cmd/shell.go
Normal file
49
cmd/shell.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/parser"
|
||||
)
|
||||
|
||||
// Shell is the 'shell' command
|
||||
var Shell wisski_distillery.Command = shell{}
|
||||
|
||||
type shell struct {
|
||||
Positionals struct {
|
||||
Slug string `positional-arg-name:"SLUG" required:"1-1" description:"slug of instance to show run shell in"`
|
||||
Args []string `positional-arg-name:"ARGS" description:"arguments to pass to the shell"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (shell) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
},
|
||||
Command: "shell",
|
||||
Description: "Open a shell in the provided instance",
|
||||
}
|
||||
}
|
||||
|
||||
func (sh shell) Run(context wisski_distillery.Context) error {
|
||||
instance, err := context.Environment.Instance(sh.Positionals.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
code := instance.Shell(context.IOStream, sh.Positionals.Args...)
|
||||
if code != 0 {
|
||||
return exit.Error{
|
||||
ExitCode: exit.ExitCode(uint8(code)),
|
||||
Message: fmt.Sprintf("Exit code %d", code),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
182
cmd/system_update.go
Normal file
182
cmd/system_update.go
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/execx"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/logging"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/stack"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/parser"
|
||||
)
|
||||
|
||||
// SystemUpdate is the 'system_update' command
|
||||
var SystemUpdate wisski_distillery.Command = systemupdate{}
|
||||
|
||||
type systemupdate struct {
|
||||
SkipCoreUpdates bool `short:"s" long:"skip-core-updates" description:"Skip applying operating system and other core system updates"`
|
||||
Positionals struct {
|
||||
GraphdbZip string `positional-arg-name:"PATH_TO_GRAPHDB_ZIP" required:"1-1" description:"path to the graphdb.zip file"`
|
||||
} `positional-args:"true"`
|
||||
}
|
||||
|
||||
func (systemupdate) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
},
|
||||
Command: "system_update",
|
||||
Description: "Installs and Update Components of the WissKI Distillery System",
|
||||
}
|
||||
}
|
||||
|
||||
var errNoGraphDBZip = exit.Error{
|
||||
Message: "%s does not exist",
|
||||
ExitCode: exit.ExitCommandArguments,
|
||||
}
|
||||
|
||||
func (s systemupdate) AfterParse() error {
|
||||
_, err := os.Stat(s.Positionals.GraphdbZip)
|
||||
if os.IsNotExist(err) {
|
||||
return errNoGraphDBZip.WithMessageF(s.Positionals.GraphdbZip)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var errFailedToCreateDirectory = exit.Error{
|
||||
Message: "failed to create directory %s: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
var errFailedRuntime = exit.Error{
|
||||
Message: "failed to update runtime: %s",
|
||||
ExitCode: exit.ExitGeneric,
|
||||
}
|
||||
|
||||
func (si systemupdate) Run(context wisski_distillery.Context) error {
|
||||
dis := context.Environment
|
||||
|
||||
// create all the other directories
|
||||
logging.LogMessage(context.IOStream, "Ensuring distillery installation directories exist")
|
||||
for _, d := range []string{
|
||||
dis.Config.DeployRoot,
|
||||
dis.InstancesDir(),
|
||||
dis.InprogressBackupPath(),
|
||||
dis.FinalBackupPath(),
|
||||
} {
|
||||
context.Println(d)
|
||||
if err := os.MkdirAll(d, os.ModeDir); err != nil {
|
||||
return errFailedToCreateDirectory.WithMessageF(d, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !si.SkipCoreUpdates {
|
||||
// install system updates
|
||||
logging.LogMessage(context.IOStream, "Updating Operating System Packages")
|
||||
if err := si.mustExec(context, "", "apt-get", "update"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := si.mustExec(context, "", "apt-get", "upgrade", "-y"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// install docker
|
||||
logging.LogMessage(context.IOStream, "Installing / Updating Docker")
|
||||
if err := si.mustExec(context, "", "apt-get", "install", "curl"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := si.mustExec(context, "", "/bin/sh", "-c", "curl -fsSL https://get.docker.com -o - | /bin/sh"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// create the docker network
|
||||
// TODO: Use docker API for this
|
||||
logging.LogMessage(context.IOStream, "Updating Docker Configuration")
|
||||
si.mustExec(context, "", "docker", "network", "create", "distillery")
|
||||
|
||||
// install and update the various stacks!
|
||||
ctx := stack.InstallationContext{
|
||||
"graphdb.zip": si.Positionals.GraphdbZip,
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
for _, stack := range dis.Stacks() {
|
||||
if err := logging.LogOperation(func() error {
|
||||
return stack.Install(context.IOStream, ctx)
|
||||
}, context.IOStream, "Installing docker stack %q", stack.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
return stack.Update(context.IOStream, true)
|
||||
}, context.IOStream, "Updating docker stack %q", stack.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, context.IOStream, "Updating Components"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := distillery.InstallResource(dis.RuntimeDir(), filepath.Join("resources", "runtime"), func(dst, src string) {
|
||||
context.Printf("[copy] %s\n", dst)
|
||||
}); err != nil {
|
||||
return errFailedRuntime.WithMessageF(err)
|
||||
}
|
||||
return nil
|
||||
}, context.IOStream, "Unpacking Runtime Components"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := dis.SQLBootstrap(context.IOStream); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, context.IOStream, "Bootstraping SQL database"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := logging.LogOperation(func() error {
|
||||
if err := dis.TriplestoreBootstrap(context.IOStream); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, context.IOStream, "Bootstraping Triplestore"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logging.LogMessage(context.IOStream, "System has been updated")
|
||||
return nil
|
||||
}
|
||||
|
||||
var errMustExecFailed = exit.Error{
|
||||
Message: "process exited with code %d",
|
||||
}
|
||||
|
||||
// mustExec indicates that the given executable process must complete successfully.
|
||||
// If it does not, returns errMustExecFailed
|
||||
func (si systemupdate) mustExec(context wisski_distillery.Context, workdir string, exe string, argv ...string) error {
|
||||
if workdir == "" {
|
||||
workdir = context.Environment.Config.DeployRoot
|
||||
}
|
||||
code := execx.Exec(context.IOStream, workdir, exe, argv...)
|
||||
if code != 0 {
|
||||
err := errMustExecFailed.WithMessageF(code)
|
||||
err.ExitCode = exit.ExitCode(code)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
110
cmd/wdcli/main.go
Normal file
110
cmd/wdcli/main.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Command wdcli implement the entry point for the wisski-distillery
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
wisski_distillery "github.com/FAU-CDI/wisski-distillery"
|
||||
"github.com/FAU-CDI/wisski-distillery/cmd"
|
||||
"github.com/FAU-CDI/wisski-distillery/env"
|
||||
"github.com/tkw1536/goprogram/exit"
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
)
|
||||
|
||||
var wdcli = wisski_distillery.NewProgram()
|
||||
|
||||
func init() {
|
||||
// self commands
|
||||
wdcli.Register(cmd.Config)
|
||||
wdcli.Register(cmd.License)
|
||||
|
||||
// setup commands
|
||||
wdcli.Register(cmd.Bootstrap)
|
||||
wdcli.Register(cmd.SystemUpdate)
|
||||
|
||||
// sql commands
|
||||
wdcli.Register(cmd.Mysql)
|
||||
wdcli.Register(cmd.MakeMysqlAccount)
|
||||
|
||||
// instance setup and teardown
|
||||
wdcli.Register(cmd.Provision)
|
||||
wdcli.Register(cmd.Purge)
|
||||
wdcli.Register(cmd.Reserve)
|
||||
wdcli.Register(cmd.Rebuild)
|
||||
|
||||
// instance management
|
||||
wdcli.Register(cmd.Ls)
|
||||
wdcli.Register(cmd.Info)
|
||||
|
||||
// instance tasks
|
||||
wdcli.Register(cmd.Shell)
|
||||
wdcli.Register(cmd.BlindUpdate)
|
||||
wdcli.Register(cmd.Cron)
|
||||
wdcli.Register(cmd.UpdatePrefixConfig) // TODO: Move into post-instance configuration
|
||||
|
||||
// backup & cron
|
||||
// wdcli.Register(cmd.BackupInstance)
|
||||
// wdcli.Register(cmd.BackupAll)
|
||||
}
|
||||
|
||||
// an error when no arguments are provided.
|
||||
var errNoArgumentsProvided = exit.Error{
|
||||
ExitCode: exit.ExitGeneralArguments,
|
||||
Message: "Need at least one argument. Use `wdcli license` to view licensing information. ",
|
||||
}
|
||||
|
||||
func main() {
|
||||
// recover from calls to panic(), and exit the program appropriatly.
|
||||
// This has to be in the main() function because any of the library functions might be broken.
|
||||
// For this reason, as few ggman functions as possible are used here; just stuff from the top-level ggman package.
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, fatalPanicMessage, err)
|
||||
debug.PrintStack()
|
||||
exit.ExitPanic.Return()
|
||||
}
|
||||
}()
|
||||
|
||||
streams := stream.FromEnv()
|
||||
|
||||
// when there are no arguments then parsing argument *will* fail
|
||||
//
|
||||
// we don't need to even bother with the rest of the program
|
||||
// just immediatly return a custom error message.
|
||||
if len(os.Args) == 1 {
|
||||
streams.Die(errNoArgumentsProvided)
|
||||
errNoArgumentsProvided.Return()
|
||||
return
|
||||
}
|
||||
|
||||
// creat a new set of parameters
|
||||
// and then use them to execute the main command
|
||||
err := func() error {
|
||||
params, err := env.ParamsFromEnv()
|
||||
if err != nil {
|
||||
return streams.Die(err)
|
||||
}
|
||||
|
||||
return wdcli.Main(streams, params, os.Args[1:])
|
||||
}()
|
||||
|
||||
// return the error to the user
|
||||
|
||||
exit.AsError(err).Return()
|
||||
}
|
||||
|
||||
const fatalPanicMessage = `Fatal Error: Panic
|
||||
|
||||
The wdcli program panicked and had to abort execution. This is usually
|
||||
indicative of a bug. If this occurs repeatedly you might want to consider
|
||||
filing an issue in the issue tracker at:
|
||||
|
||||
https://github.com/FAU-CDI/wisski-distillery/issues
|
||||
|
||||
Below is debug information that might help the developers track down what
|
||||
happened.
|
||||
|
||||
panic: %v
|
||||
`
|
||||
Loading…
Add table
Add a link
Reference in a new issue