mirror of
https://github.com/photoprism/photoprism.git
synced 2025-10-05 08:47:12 +08:00
CLI: Add cluster operations and management commands #98
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
103
internal/commands/cluster_nodes_mod.go
Normal file
103
internal/commands/cluster_nodes_mod.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
|
||||
// flags for nodes mod
|
||||
var (
|
||||
nodesModTypeFlag = &cli.StringFlag{Name: "type", Aliases: []string{"t"}, Usage: "node `TYPE` (portal, instance, service)"}
|
||||
nodesModInternal = &cli.StringFlag{Name: "internal-url", Aliases: []string{"i"}, Usage: "internal service `URL`"}
|
||||
nodesModLabel = &cli.StringSliceFlag{Name: "label", Aliases: []string{"l"}, Usage: "`k=v` label (repeatable)"}
|
||||
)
|
||||
|
||||
// ClusterNodesModCommand updates node fields.
|
||||
var ClusterNodesModCommand = &cli.Command{
|
||||
Name: "mod",
|
||||
Usage: "Updates node properties (Portal-only)",
|
||||
ArgsUsage: "<id|name>",
|
||||
Flags: []cli.Flag{nodesModTypeFlag, nodesModInternal, nodesModLabel, &cli.BoolFlag{Name: "yes", Aliases: []string{"y"}, Usage: "runs the command non-interactively"}},
|
||||
Action: clusterNodesModAction,
|
||||
}
|
||||
|
||||
func clusterNodesModAction(ctx *cli.Context) error {
|
||||
return CallWithDependencies(ctx, func(conf *config.Config) error {
|
||||
if !conf.IsPortal() {
|
||||
return fmt.Errorf("node update is only available on a Portal node")
|
||||
}
|
||||
|
||||
key := ctx.Args().First()
|
||||
if key == "" {
|
||||
return cli.ShowSubcommandHelp(ctx)
|
||||
}
|
||||
|
||||
r, err := reg.NewFileRegistry(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n, getErr := r.Get(key)
|
||||
if getErr != nil {
|
||||
name := clean.TypeLowerDash(key)
|
||||
if name == "" {
|
||||
return fmt.Errorf("invalid node identifier")
|
||||
}
|
||||
n, getErr = r.FindByName(name)
|
||||
}
|
||||
if getErr != nil || n == nil {
|
||||
return fmt.Errorf("node not found")
|
||||
}
|
||||
|
||||
if v := ctx.String("type"); v != "" {
|
||||
n.Type = clean.TypeLowerDash(v)
|
||||
}
|
||||
if v := ctx.String("internal-url"); v != "" {
|
||||
n.Internal = v
|
||||
}
|
||||
if labels := ctx.StringSlice("label"); len(labels) > 0 {
|
||||
if n.Labels == nil {
|
||||
n.Labels = map[string]string{}
|
||||
}
|
||||
for _, kv := range labels {
|
||||
if k, v, ok := splitKV(kv); ok {
|
||||
n.Labels[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
confirmed := RunNonInteractively(ctx.Bool("yes"))
|
||||
if !confirmed {
|
||||
prompt := promptui.Prompt{Label: fmt.Sprintf("Update node %s?", clean.LogQuote(n.Name)), IsConfirm: true}
|
||||
if _, err := prompt.Run(); err != nil {
|
||||
log.Infof("update cancelled for %s", clean.LogQuote(n.Name))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.Put(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("node %s has been updated", clean.LogQuote(n.Name))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func splitKV(s string) (string, string, bool) {
|
||||
if s == "" {
|
||||
return "", "", false
|
||||
}
|
||||
i := strings.IndexByte(s, '=')
|
||||
if i <= 0 || i >= len(s)-1 {
|
||||
return "", "", false
|
||||
}
|
||||
return s[:i], s[i+1:], true
|
||||
}
|
Reference in New Issue
Block a user