Show a warning when using wrong executable
This commit updates the 'wdcli' command to show a warning when using the wrong executable.
This commit is contained in:
parent
c4de1f2a06
commit
35bb95c5ca
29 changed files with 176 additions and 30 deletions
|
|
@ -25,7 +25,7 @@ type backup struct {
|
|||
func (backup) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "backup",
|
||||
Description: "Makes a backup of the entire distillery",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type blindUpdate struct {
|
|||
func (blindUpdate) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "blind_update",
|
||||
Description: "Runs the blind update in the provided instances",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ type bootstrap struct {
|
|||
func (bootstrap) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: false,
|
||||
NeedsDistillery: false,
|
||||
},
|
||||
Command: "bootstrap",
|
||||
Description: "Bootstraps the installation of a Distillery System",
|
||||
|
|
@ -92,8 +92,8 @@ func (bs bootstrap) Run(context wisski_distillery.Context) error {
|
|||
}
|
||||
|
||||
// TODO: Read these from the command line?
|
||||
wdcliPath := filepath.Join(root, "wdcli")
|
||||
envPath := filepath.Join(root, ".env")
|
||||
wdcliPath := filepath.Join(root, env.Executable)
|
||||
envPath := filepath.Join(root, env.ConfigFile)
|
||||
domain := bs.Hostname
|
||||
if domain == "" {
|
||||
domain = hostname.FQDN()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type config struct {
|
|||
func (s config) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "config",
|
||||
Description: "Prints information about configuration",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ type cron struct {
|
|||
func (cron) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "cron",
|
||||
Description: "Runs the cron script for several instances",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type info struct {
|
|||
func (info) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "info",
|
||||
Description: "Provide information about a single repository",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ type license struct{}
|
|||
func (license) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: false,
|
||||
NeedsDistillery: false,
|
||||
},
|
||||
Command: "license",
|
||||
Description: "Print licensing information about wdcli and exit. ",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type ls struct {
|
|||
func (ls) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "ls",
|
||||
Description: "Lists WissKI instances",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type makeMysqlAccount struct{}
|
|||
func (makeMysqlAccount) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type monday struct {
|
|||
func (monday) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "monday",
|
||||
Description: "Runs regular monday tasks",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type mysql struct {
|
|||
func (mysql) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type provision struct {
|
|||
func (provision) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "provision",
|
||||
Description: "Creates a new WissKI Instance",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type purge struct {
|
|||
func (purge) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "purge",
|
||||
Description: "Purges a WissKI Instance",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type rebuild struct {
|
|||
func (rebuild) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "rebuild",
|
||||
Description: "Runs the rebuild script for several instances",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type reserve struct {
|
|||
func (reserve) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "reserve",
|
||||
Description: "Reserves a new WissKI Instance",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ type shell struct {
|
|||
func (shell) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ type snapshot struct {
|
|||
func (snapshot) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "snapshot",
|
||||
Description: "Generates a snapshot archive for the provided archive",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ type systemupdate struct {
|
|||
func (systemupdate) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
ParserConfig: parser.Config{
|
||||
IncludeUnknown: true,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type updateprefixconfig struct{}
|
|||
func (updateprefixconfig) Description() wisski_distillery.Description {
|
||||
return wisski_distillery.Description{
|
||||
Requirements: env.Requirements{
|
||||
NeedsConfig: true,
|
||||
NeedsDistillery: true,
|
||||
},
|
||||
Command: "update_prefix_config",
|
||||
Description: "Updates the prefix configuration",
|
||||
|
|
|
|||
36
env/constants.go
vendored
Normal file
36
env/constants.go
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/fsx"
|
||||
)
|
||||
|
||||
// Executable is the name of the 'wdcli' executable.
|
||||
// It should be located inside the deployment directory.
|
||||
const Executable = "wdcli"
|
||||
|
||||
// ExecutablePath returns the path to the executable of this distillery.
|
||||
func (dis *Distillery) ExecutablePath() string {
|
||||
return filepath.Join(dis.Config.DeployRoot, Executable)
|
||||
}
|
||||
|
||||
// UsingDistilleryExecutable checks if the current process
|
||||
func (dis *Distillery) UsingDistilleryExecutable() bool {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fsx.SameFile(exe, dis.ExecutablePath())
|
||||
}
|
||||
|
||||
// Config file is the name of the config file.
|
||||
// It should be located inside the deployment directory.
|
||||
const ConfigFile = ".env"
|
||||
|
||||
// ConfigFilePath returns the path to the configuration file of this distillery.
|
||||
// TODO: This should be moved to the Config struct.
|
||||
func (dis *Distillery) ConfigFilePath() string {
|
||||
return filepath.Join(dis.Config.DeployRoot, ConfigFile)
|
||||
}
|
||||
2
env/distillery.go
vendored
2
env/distillery.go
vendored
|
|
@ -54,7 +54,7 @@ func NewDistillery(params Params, req Requirements) (env *Distillery, err error)
|
|||
env = &Distillery{}
|
||||
|
||||
// if we don't need to load the config, there is nothing to do
|
||||
if !req.NeedsConfig {
|
||||
if !req.NeedsDistillery {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
2
env/params.go
vendored
2
env/params.go
vendored
|
|
@ -20,7 +20,7 @@ func (params Params) ConfigFilePath() string {
|
|||
if params.BaseDirectory == "" {
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(params.BaseDirectory, ".env")
|
||||
return filepath.Join(params.BaseDirectory, ConfigFile)
|
||||
}
|
||||
|
||||
var errUnableToLoadParams = exit.Error{
|
||||
|
|
|
|||
3
env/requirements.go
vendored
3
env/requirements.go
vendored
|
|
@ -6,7 +6,8 @@ import (
|
|||
)
|
||||
|
||||
type Requirements struct {
|
||||
NeedsConfig bool
|
||||
// Do we need an installed distillery?
|
||||
NeedsDistillery bool
|
||||
}
|
||||
|
||||
// AllowsFlag checks if the provided flag may be passed to fullfill this requirement
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ var ErrCopySameFile = errors.New("src and dst must be different files")
|
|||
// CopyFile copies a file from src to dst.
|
||||
// When dst and src are the same file, returns ErrCopySameFile.
|
||||
func CopyFile(dst, src string) error {
|
||||
if src == dst {
|
||||
if SameFile(src, dst) {
|
||||
return ErrCopySameFile
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ func CopyDirectory(dst, src string, onCopy func(dst, src string)) error {
|
|||
// TODO: Allow copying in parallel? Maybe with a mutex?
|
||||
|
||||
// sanity checks
|
||||
if src == dst {
|
||||
if SameFile(src, dst) {
|
||||
return ErrCopySameFile
|
||||
}
|
||||
if !IsDirectory(dst) {
|
||||
|
|
|
|||
87
internal/fsx/same.go
Normal file
87
internal/fsx/same.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package fsx
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// SameFile checks if path1 and path2 refer to the same file.
|
||||
// If both files exist, they are compared using [os.SameFile].
|
||||
// If both files do not exist, the paths are first compared syntactically and then via recursion on [filepath.Dir].
|
||||
func SameFile(path1, path2 string) bool {
|
||||
|
||||
// initial attempt: check if directly
|
||||
same, certain := couldBeSameFile(path1, path2)
|
||||
if certain {
|
||||
return same
|
||||
}
|
||||
|
||||
// second attempt: find the directory names and base paths
|
||||
d1, n1 := filepath.Dir(path1), filepath.Base(path1)
|
||||
d2, n2 := filepath.Dir(path2), filepath.Base(path2)
|
||||
|
||||
// if we have different file names (and they don't exist)
|
||||
// we don't need to continue
|
||||
if n1 != n2 {
|
||||
return false
|
||||
}
|
||||
|
||||
// compare the base names!
|
||||
{
|
||||
same, _ := couldBeSameFile(d1, d2)
|
||||
return same
|
||||
}
|
||||
}
|
||||
|
||||
// couldBeSameFile checks if path1 might be the same as path2.
|
||||
//
|
||||
// If both files exist, compares using [os.SameFile].
|
||||
// Otherwise compares absolute paths using string comparison.
|
||||
//
|
||||
// same indicates if they might be the same file.
|
||||
// authorative indiciates if the result is authorative.
|
||||
func couldBeSameFile(path1, path2 string) (same, authorative bool) {
|
||||
{
|
||||
// stat both files
|
||||
info1, err1 := os.Stat(path1)
|
||||
info2, err2 := os.Stat(path2)
|
||||
|
||||
// both files exist => check using os.SameFile
|
||||
// the result is always authorative
|
||||
if err1 == nil && err2 == nil {
|
||||
same = os.SameFile(info1, info2)
|
||||
authorative = true
|
||||
return
|
||||
}
|
||||
|
||||
// only 1 file errored => they could be different
|
||||
if (err1 == nil) != (err2 == nil) {
|
||||
return
|
||||
}
|
||||
|
||||
// only 1 file does not exist => they could be different
|
||||
if os.IsNotExist(err1) != os.IsNotExist(err2) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// resolve paths absolutely
|
||||
rpath1, err1 := filepath.Abs(path1)
|
||||
rpath2, err2 := filepath.Abs(path2)
|
||||
|
||||
// if either path could not be resolved absolutely
|
||||
// fallback to just using clean!
|
||||
if err1 != nil {
|
||||
rpath1 = filepath.Clean(path1)
|
||||
}
|
||||
if err2 != nil {
|
||||
rpath2 = filepath.Clean(path2)
|
||||
}
|
||||
|
||||
// compare using strings
|
||||
same = rpath1 == rpath2
|
||||
authorative = same // positive result is authorative!
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// Touch touches a file
|
||||
// Touch touches a file.
|
||||
// It is similar to the unix 'touch' command.
|
||||
//
|
||||
// If the file does not exist exists, it is created using [os.Create].
|
||||
// If the file does exist, it's access and modification times are updated to the current time.
|
||||
func Touch(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
switch {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
// Package fsx provides convenient abstractions to work with the filesystem.
|
||||
package fsx
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Exists checks if the given path exists
|
||||
func Exists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsDirectory checks if the provided path exists and is a directory
|
||||
func IsDirectory(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && info.Mode().IsDir()
|
||||
}
|
||||
|
||||
// IsFile checks if the provided path exists and is a regular file
|
||||
func IsFile(path string) bool {
|
||||
info, err := os.Stat(path)
|
||||
return err == nil && info.Mode().IsRegular()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
// Installable represents a Stack that can be automatically installed from a set of resources
|
||||
// See the Install() method.
|
||||
// See the [Install] method.
|
||||
type Installable struct {
|
||||
Stack
|
||||
|
||||
|
|
|
|||
14
program.go
14
program.go
|
|
@ -27,13 +27,25 @@ var errUserIsNotRoot = exit.Error{
|
|||
Message: "This command has to be executed as root. The current user is not root.",
|
||||
}
|
||||
|
||||
var warnNoDeployWdcli = "Warning: Not using %q executable at %q. This might leave the distillery in an inconsistent state. \n"
|
||||
|
||||
func NewProgram() Program {
|
||||
return Program{
|
||||
BeforeCommand: func(context Context, command Command) error {
|
||||
// make sure that we are root!
|
||||
usr, err := user.Current()
|
||||
if err != nil || usr.Uid != "0" || usr.Gid != "0" { // make sure that we are root!
|
||||
if err != nil || usr.Uid != "0" || usr.Gid != "0" {
|
||||
return errUserIsNotRoot
|
||||
}
|
||||
|
||||
// warn when not using the distillery excutable
|
||||
if context.Description.Requirements.NeedsDistillery {
|
||||
dis := context.Environment
|
||||
if !dis.UsingDistilleryExecutable() {
|
||||
context.EPrintf(warnNoDeployWdcli, env.Executable, dis.ExecutablePath())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue