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 ( nodesModRoleFlag = &cli.StringFlag{Name: "role", Aliases: []string{"t"}, Usage: "node `ROLE` (portal, instance, service)"} nodesModInternal = &cli.StringFlag{Name: "advertise-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: "", Flags: []cli.Flag{nodesModRoleFlag, 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 cli.Exit(fmt.Errorf("node update is only available on a Portal node"), 2) } key := ctx.Args().First() if key == "" { return cli.Exit(fmt.Errorf("node id or name is required"), 2) } r, err := reg.NewClientRegistryWithConfig(conf) if err != nil { return cli.Exit(err, 1) } n, getErr := r.Get(key) if getErr != nil { name := clean.TypeLowerDash(key) if name == "" { return cli.Exit(fmt.Errorf("invalid node identifier"), 2) } n, getErr = r.FindByName(name) } if getErr != nil || n == nil { return cli.Exit(fmt.Errorf("node not found"), 3) } if v := ctx.String("role"); v != "" { n.Role = clean.TypeLowerDash(v) } if v := ctx.String("advertise-url"); v != "" { n.AdvertiseUrl = 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 cli.Exit(err, 1) } 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 }