Files
remark42/backend/app/cmd/cmd.go
T
Dmitry VerkhoturovandUmputun f104a6e1b6 move common options to cmd.go
Timeout, admin password and site id are set in many commands,
and we need to take care of synchronising the descriptions
and flags between them.

This change moves these standard options to cmd.go importing them
in the same manner CommonOpts imported by all commands already.
2022-07-25 12:42:13 -05:00

130 lines
3.7 KiB
Go

// Package cmd has all top-level commands dispatched by main's flag.Parse
// The entry point of each command is Execute function
package cmd
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"text/template"
"time"
log "github.com/go-pkgz/lgr"
)
// CommonOptionsCommander extends flags.Commander with SetCommon
// All commands should implement this interfaces
type CommonOptionsCommander interface {
SetCommon(commonOpts CommonOpts)
Execute(args []string) error
HandleDeprecatedFlags() []DeprecatedFlag
}
// CommonOpts sets externally from main, shared across all commands
type CommonOpts struct {
RemarkURL string
SharedSecret string
Revision string
}
// SupportCmdOpts is set of commands shared among similar commands like backup/restore and such.
// Order of fields defines the help command output order.
type SupportCmdOpts struct {
Site string `short:"s" long:"site" env:"SITE" default:"remark" description:"site name"`
AdminPasswd string `long:"admin-passwd" env:"ADMIN_PASSWD" default:"" description:"admin basic auth password"`
Timeout time.Duration `long:"timeout" default:"60m" description:"timeout for the command run"`
}
// DeprecatedFlag contains information about deprecated option
type DeprecatedFlag struct {
Old string
New string
Version string
Collision bool
}
// SetCommon satisfies CommonOptionsCommander interface and sets common option fields
// The method called by main for each command
func (c *CommonOpts) SetCommon(commonOpts CommonOpts) {
c.RemarkURL = strings.TrimSuffix(commonOpts.RemarkURL, "/") // allow RemarkURL with trailing /
c.SharedSecret = commonOpts.SharedSecret
c.Revision = commonOpts.Revision
}
// HandleDeprecatedFlags sets new flags from deprecated and returns their list
func (c *CommonOpts) HandleDeprecatedFlags() []DeprecatedFlag { return nil }
// fileParser used to convert template strings like blah-{{.SITE}}-{{.YYYYMMDD}} the final format
type fileParser struct {
site string
file string
path string
}
// parse apply template and also concat path and file. In case if file contains path separator path will be ignored
func (p *fileParser) parse(now time.Time) (string, error) {
// file/location parameters my have template masks
fileTemplate := struct {
YYYYMMDD string
YYYY string
YYYYMM string
MM string
DD string
TS string
UNIX int64
SITE string
}{
YYYYMMDD: now.Format("20060102"),
YYYY: now.Format("2006"),
YYYYMM: now.Format("200601"),
MM: now.Format("01"),
DD: now.Format("02"),
UNIX: now.Unix(),
SITE: p.site,
TS: now.Format("20060102T150405"),
}
bb := bytes.Buffer{}
fname := p.file
if !strings.Contains(p.file, string(filepath.Separator)) {
fname = filepath.Join(p.path, p.file)
}
if err := template.Must(template.New("bb").Parse(fname)).Execute(&bb, fileTemplate); err != nil {
return "", fmt.Errorf("failed to parse %q: %w", fname, err)
}
return bb.String(), nil
}
// resetEnv clears sensitive env vars
func resetEnv(envs ...string) {
for _, env := range envs {
if err := os.Unsetenv(env); err != nil {
log.Printf("[WARN] can't unset env %s, %s", env, err)
}
}
}
// responseError returns error with status and response body
func responseError(resp *http.Response) error {
body, e := io.ReadAll(resp.Body)
if e != nil {
body = []byte("")
}
return fmt.Errorf("error response %q, %s", resp.Status, body)
}
// mkdir -p for all dirs
func makeDirs(dirs ...string) error {
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0o700); err != nil { // If path is already a directory, MkdirAll does nothing
return fmt.Errorf("can't make directory %s: %w", dir, err)
}
}
return nil
}