From e7623eeae66152f56af4ce2e7bef564d0620bb3a Mon Sep 17 00:00:00 2001 From: tobigiwa Date: Tue, 26 Sep 2023 10:48:57 -0400 Subject: [PATCH] This commit resolves #221, to specify stacks that are currently on your local machine running. Signed-off-by: tobigiwa --- cmd/ps.go | 74 ++++++++++++++++++++++++++++++++ internal/docker/docker.go | 6 +++ internal/stacks/stack_manager.go | 38 +++++++++++++--- 3 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 cmd/ps.go diff --git a/cmd/ps.go b/cmd/ps.go new file mode 100644 index 00000000..0f5f6323 --- /dev/null +++ b/cmd/ps.go @@ -0,0 +1,74 @@ +/* +Copyright © 2023 Giwa Oluwatobi +*/ +package cmd + +import ( + "context" + "fmt" + "strings" + + "github.com/hyperledger/firefly-cli/internal/log" + "github.com/hyperledger/firefly-cli/internal/stacks" + "github.com/spf13/cobra" +) + +// psCmd represents the ps command +var psCmd = &cobra.Command{ + Use: "ps [a stack name]...", + Short: "Returns information on running stacks", + Long: `ps returns currently running stacks on your local machine. + + It also takes a continuous list of whitespace optional arguement - stack name.`, + Aliases: []string{"process"}, + RunE: func(cmd *cobra.Command, args []string) error { + + ctx := log.WithVerbosity(context.Background(), verbose) + ctx = log.WithLogger(ctx, logger) + + allStacks, err := stacks.ListStacks() + if err != nil { + return err + } + + if len(args) > 0 { + namedStacks := make([]string, 0, len(args)) + for _, stackName := range args { + if contains(allStacks, strings.TrimSpace(stackName)) { + namedStacks = append(namedStacks, stackName) + } else { + fmt.Printf("stack name - %s, is not present on your local machine. Run `ff ls` to see all available stacks.\n", stackName) + } + } + + allStacks = namedStacks // replace only the user specified stacks in the slice instead. + } + + stackManager := stacks.NewStackManager(ctx) + for _, stackName := range allStacks { + if err := stackManager.LoadStack(stackName); err != nil { + return err + } + + if err := stackManager.IsRunning(); err != nil { + return err + } + } + return nil + }, +} + +func init() { + rootCmd.AddCommand(psCmd) +} + +// contains can be removed if the go mod version is bumped to version Go 1.18 +// and replaced with slices.Contains(). +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} diff --git a/internal/docker/docker.go b/internal/docker/docker.go index d95a17ef..d26b99ac 100644 --- a/internal/docker/docker.go +++ b/internal/docker/docker.go @@ -111,6 +111,12 @@ func RunDockerCommandBuffered(ctx context.Context, workingDir string, command .. return runCommand(ctx, dockerCmd) } +func RunDockerComposeCommandReturnsStdout(workingDir string, command ...string) ([]byte, error) { + dockerCmd := exec.Command("docker", append([]string{"compose"}, command...)...) + dockerCmd.Dir = workingDir + return dockerCmd.Output() +} + func runCommand(ctx context.Context, cmd *exec.Cmd) (string, error) { verbose := log.VerbosityFromContext(ctx) isLogCmd, _ := ctx.Value(CtxIsLogCmdKey{}).(bool) diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index d80e27e8..400f8db4 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -27,7 +27,9 @@ import ( "path" "path/filepath" "strings" + "sync" "syscall" + "text/tabwriter" "time" "github.com/hyperledger/firefly-cli/internal/blockchain" @@ -58,21 +60,20 @@ type StackManager struct { blockchainProvider blockchain.IBlockchainProvider tokenProviders []tokens.ITokensProvider IsOldFileStructure bool + once sync.Once } func ListStacks() ([]string, error) { - files, err := ioutil.ReadDir(constants.StacksDir) + files, err := os.ReadDir(constants.StacksDir) if err != nil { return nil, err } - stacks := make([]string, 0) - i := 0 + stacks := make([]string, 0, len(files)) for _, f := range files { if f.IsDir() { if exists, err := CheckExists(f.Name()); err == nil && exists { stacks = append(stacks, f.Name()) - i++ } } } @@ -250,7 +251,7 @@ func (s *StackManager) LoadStack(stackName string) error { if !exists { return fmt.Errorf("stack '%s' does not exist", stackName) } - d, err := ioutil.ReadFile(filepath.Join(stackDir, "stack.json")) + d, err := os.ReadFile(filepath.Join(stackDir, "stack.json")) if err != nil { return err } @@ -1103,6 +1104,33 @@ func (s *StackManager) PrintStackInfo() error { return nil } +// IsRunning prints to the stdout, the stack name and it status as "running" or "not_running". +func (s *StackManager) IsRunning() error { + output, err := docker.RunDockerComposeCommandReturnsStdout(s.Stack.StackDir, "ps") + if err != nil { + return err + } + + formatHeader := "\n %s\t%s\t " + formatBody := "\n %s\t%s\t" + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 8, '\t', 0) + + s.once.Do(func() { + fmt.Fprintf(w, formatHeader, "STACK", "STATUS") + }) + + if strings.Contains(string(output), s.Stack.Name) { // if the output contains the stack name, it means the container is running. + fmt.Fprintf(w, formatBody, s.Stack.Name, "running") + } else { + fmt.Fprintf(w, formatBody, s.Stack.Name, "not_running") + } + fmt.Fprintln(w) + w.Flush() + return nil +} + func (s *StackManager) disableFireflyCoreContainers() error { compose := s.buildDockerCompose() for _, member := range s.Stack.Members {