Skip to content

Commit

Permalink
feat: add real time logs for spaceship (#124)
Browse files Browse the repository at this point in the history
* Initialized with Ignite CLI

* spaceship

* use a pkg to deal with ssh connections

* add shh deploy and dev logic

* remove unused binary check

* fix cmds

* remove development mode

* remove unused methods

* initChain flag

* add runner script

* check os and arch and get logs

* fix remote logs

* remove exported var

* fix script

* fix the local tmp folder

* add comments

* add tarball extractor

* add changelog and readme

* skip tests

* add arch map and improve readme

* add upload progress bar

* unknow hosts

* unkown host

* add real time logs

* update go.mod

* improve readme

* improve logs

---------

Co-authored-by: Developer Experience team at Ignite <hello@ignite.com>
  • Loading branch information
Pantani and Developer Experience team at Ignite authored Aug 6, 2024
1 parent ce330d8 commit 33c23cd
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 66 deletions.
32 changes: 19 additions & 13 deletions spaceship/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ deploy blockchain applications via SSH.

## Prerequisites

* Ignite CLI: Version v28.4.0 or higher is required.
* Ignite CLI: Version `v28.4.0` or higher is required.
* Blockchain Scaffold: A blockchain scaffolded using Ignite

## Usage
Expand All @@ -21,31 +21,37 @@ ignite spaceship deploy root@127.0.0.1 --key $HOME/.ssh/id_rsa --key-password ke

Each command initiates a build of the blockchain binary and sets up the chain's home directory based on the
configuration. The app then connects to the specified SSH server, establishes workspaces, transfers the binary, and
executes it using a runner script. The workspaces are organized under $HOME/workspace/<chain-id> and include:
executes it using a runner script. The workspaces are organized under `$HOME/workspace/<chain-id>` and include:

- Binary Directory: $HOME/workspace/<chain-id>/bin - Contains the chain binary.
- Home Directory: $HOME/workspace/<chain-id>/home - Stores chain data.
- Log Directory: $HOME/workspace/<chain-id>/log - Holds logs of the running chain.
- Runner Script: $HOME/workspace/<chain-id>/run.sh - A script to start the binary in the background using nohup.
- PID File: $HOME/workspace/<chain-id>/spaceship.pid - Stores the PID of the currently running chain instance.
- Binary Directory: `$HOME/workspace/<chain-id>/bin` - Contains the chain binary.
- Home Directory: `$HOME/workspace/<chain-id>/home` - Stores chain data.
- Log Directory: `$HOME/workspace/<chain-id>/log` - Holds logs of the running chain.
- Runner Script: `$HOME/workspace/<chain-id>/run.sh` - A script to start the binary in the background using nohup.
- PID File: `$HOME/workspace/<chain-id>/spaceship.pid` - Stores the PID of the currently running chain instance.

### Managing the Chain

To manage your blockchain deployment, use the following commands:

- Check Status:
- Check status:

```sh
ignite spaceship status root@127.0.0.1 --key $HOME/.ssh/id_rsa
```

- View Logs:
- View logs:

```sh
ignite spaceship log root@127.0.0.1 --key $HOME/.ssh/id_rsa
```

- Restart the Chain:
- Watch the logs in real time:

```sh
ignite spaceship log root@127.0.0.1 --key $HOME/.ssh/id_rsa --real-time
```

- Restart the chain:

```sh
ignite spaceship restart root@127.0.0.1 --key $HOME/.ssh/id_rsa
Expand All @@ -57,16 +63,16 @@ ignite spaceship restart root@127.0.0.1 --key $HOME/.ssh/id_rsa
ignite spaceship stop root@127.0.0.1 --key $HOME/.ssh/id_rsa
```

To redeploy the chain on the same server without overwriting the home directory, use the --init-chain flag to
To redeploy the chain on the same server without overwriting the home directory, use the `--init-chain` flag to
reinitialize the chain if necessary.

### Config

You can override the default [chain configuration](https://docs.ignite.com/references/config#validators) by using the
Ignite configuration file. Validators' node configuration files are stored in the data directory. By default, Spaceship
initializes the chain locally in a temporary folder using the Ignite config file and then copies the configuration to
the remote machine at $HOME/workspace/<chain-id>/home.
Configuration resets are performed by Ignite when necessary, especially when using the --init-chain flag or if the chain
the remote machine at `$HOME/workspace/<chain-id>/home`.
Configuration resets are performed by Ignite when necessary, especially when using the `--init-chain` flag or if the chain
was not previously initialized.

Example Ignite config:
Expand Down
15 changes: 14 additions & 1 deletion spaceship/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,20 @@ func GetCommands() []*plugin.Command {
{
Use: "log",
Short: "get chain logs if its running",
Flags: defaultFlags,
Flags: append(defaultFlags,
&plugin.Flag{
Name: flagLines,
Shorthand: "l",
Usage: "number of lines of chain logs",
Type: plugin.FlagTypeInt,
DefaultValue: "100",
},
&plugin.Flag{
Name: flagRealTime,
Usage: "show the logs in the real time",
Type: plugin.FlagTypeBool,
},
),
},
{
Use: "status",
Expand Down
28 changes: 21 additions & 7 deletions spaceship/cmd/debug/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,37 @@ func main() {
Type: plugin.FlagTypeString,
Value: filepath.Join(home, ".ssh/id_rsa"),
},
{
Name: "init-chain",
Shorthand: "i",
Usage: "run init chain and create the home folder",
Type: plugin.FlagTypeBool,
Value: "true",
},
},
}
)
switch args[1] {
case "deploy":
c.Flags = append(c.Flags,
&plugin.Flag{
Name: "init-chain",
Shorthand: "i",
Usage: "run init chain and create the home folder",
Type: plugin.FlagTypeBool,
Value: "true",
},
&plugin.Flag{
Name: "init-chain",
Shorthand: "i",
Usage: "run init chain and create the home folder",
Type: plugin.FlagTypeBool,
Value: "true",
})
if err := cmd.ExecuteSSHDeploy(ctx, c, chainInfo); err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
case "log":
c.Flags = append(c.Flags, &plugin.Flag{
Name: "real-time",
Usage: "show the logs in the real time",
Type: plugin.FlagTypeBool,
Value: "true",
})
if err := cmd.ExecuteSSHLog(ctx, c, chainInfo); err != nil {
fmt.Fprintln(os.Stderr, err)
return
Expand Down
92 changes: 77 additions & 15 deletions spaceship/cmd/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import (
"path/filepath"
"strings"

"github.com/gookit/color"
ignitecmd "github.com/ignite/cli/v28/ignite/cmd"
"github.com/ignite/cli/v28/ignite/pkg/cliui"
"github.com/ignite/cli/v28/ignite/pkg/errors"
"github.com/ignite/cli/v28/ignite/services/plugin"
"github.com/schollz/progressbar/v3"
"golang.org/x/sync/errgroup"

"github.com/ignite/apps/spaceship/pkg/ssh"
"github.com/ignite/apps/spaceship/pkg/tarball"
Expand All @@ -27,6 +30,10 @@ const (
flagRawKey = "raw-key"
flagKeyPassword = "key-password"
flagInitChain = "init-chain"
flagLines = "lines"
flagRealTime = "real-time"

statusConnecting = "Connecting..."
)

func executeSSH(cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) (*ssh.SSH, error) {
Expand Down Expand Up @@ -67,6 +74,9 @@ func executeSSH(cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) (*ssh.SSH,

// ExecuteSSHStatus executes the ssh status subcommand.
func ExecuteSSHStatus(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) error {
session := cliui.New(cliui.StartSpinnerWithText(statusConnecting))
defer session.End()

c, err := executeSSH(cmd, chain)
if err != nil {
return err
Expand All @@ -81,12 +91,15 @@ func ExecuteSSHStatus(ctx context.Context, cmd *plugin.ExecutedCommand, chain *p
if err != nil {
return err
}
fmt.Println(status)
return nil

return session.Println(status)
}

// ExecuteSSHSStop executes the ssh stop subcommand.
func ExecuteSSHSStop(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) error {
session := cliui.New(cliui.StartSpinnerWithText(statusConnecting))
defer session.End()

c, err := executeSSH(cmd, chain)
if err != nil {
return err
Expand All @@ -101,12 +114,15 @@ func ExecuteSSHSStop(ctx context.Context, cmd *plugin.ExecutedCommand, chain *pl
if err != nil {
return err
}
fmt.Println(stop)
return nil

return session.Println(stop)
}

// ExecuteSSHRestart executes the ssh restart subcommand.
func ExecuteSSHRestart(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) error {
session := cliui.New(cliui.StartSpinnerWithText(statusConnecting))
defer session.End()

c, err := executeSSH(cmd, chain)
if err != nil {
return err
Expand All @@ -121,12 +137,21 @@ func ExecuteSSHRestart(ctx context.Context, cmd *plugin.ExecutedCommand, chain *
if err != nil {
return err
}
fmt.Println(restart)
return nil

return session.Println(restart)
}

// ExecuteSSHLog executes the ssh log subcommand.
func ExecuteSSHLog(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) error {
session := cliui.New(cliui.StartSpinnerWithText(statusConnecting))
defer session.End()

var (
flags = plugin.Flags(cmd.Flags)
lines, _ = flags.GetInt(flagLines)
realTime, _ = flags.GetBool(flagRealTime)
)

c, err := executeSSH(cmd, chain)
if err != nil {
return err
Expand All @@ -137,16 +162,48 @@ func ExecuteSSHLog(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plug
return ErrServerNotInitialized
}

log, err := c.LatestLog()
logs, err := c.LatestLog(lines)
if err != nil {
return err
}
fmt.Println(string(log))
_ = session.Println(logs)

if realTime {
// Create a buffered channel to receive log lines.
logChannel := make(chan string, 100)
g, ctx := errgroup.WithContext(ctx)

// Start the FollowLog method in a goroutine using errgroup
g.Go(func() error {
return c.FollowLog(ctx, logChannel)
})

// Start a goroutine to consume log lines
g.Go(func() error {
for {
select {
case line := <-logChannel:
_ = session.Print(line)
case <-ctx.Done():
return ctx.Err()
}
}
})

// Wait for all goroutines to complete
if err := g.Wait(); err != nil && !errors.Is(err, context.Canceled) {
return err
}
}

return nil
}

// ExecuteSSHDeploy executes the ssh deploy subcommand.
func ExecuteSSHDeploy(ctx context.Context, cmd *plugin.ExecutedCommand, chain *plugin.ChainInfo) error {
session := cliui.New(cliui.StartSpinnerWithText(statusConnecting))
defer session.End()

flags := plugin.Flags(cmd.Flags)

localDir, err := os.MkdirTemp(os.TempDir(), "spaceship")
Expand All @@ -170,6 +227,8 @@ func ExecuteSSHDeploy(ctx context.Context, cmd *plugin.ExecutedCommand, chain *p
}
defer c.Close()

_ = session.Println(color.Yellow.Sprintf("Building chain binary using Ignite:"))

target, err := c.Target(ctx)
if err != nil {
return err
Expand Down Expand Up @@ -227,26 +286,29 @@ func ExecuteSSHDeploy(ctx context.Context, cmd *plugin.ExecutedCommand, chain *p
return errors.Errorf("zero files extracted from the tarball %s", localChainTarball)
}

bar.Describe("uploading chain binary")
bar.Describe("Uploading chain binary")
binPath, err := c.UploadBinary(extracted[0], progressCallback)
if err != nil {
return err
}
_ = session.Println(color.Yellow.Sprintf("Chain binary uploaded to '%s'\n", binPath))

home := c.Home()
if initChain || !c.HasGenesis(ctx) {
// Init the chain.
_ = session.Println(color.Yellow.Sprintf("Initializing the chain home folder using Ignite:"))

igniteChainInitCmd := ignitecmd.NewChainInit()
igniteChainInitCmd.SetArgs([]string{"-p", chain.AppPath, "--home", localChainHome}) // TODO add verbose flag after merge and backport this one https://github.com/ignite/cli/pull/4286
if err := igniteChainInitCmd.ExecuteContext(ctx); err != nil {
return err
}

bar.Describe("uploading chain home folder")
home, err = c.UploadHome(ctx, localChainHome, progressCallback)
bar.Describe("Uploading chain home folder")
homeFiles, err := c.UploadHome(ctx, localChainHome, progressCallback)
if err != nil {
return err
}
_ = session.Println(color.Yellow.Sprintf("Uploaded files: \n- %s\n", strings.Join(homeFiles, "\n- ")))
}

// Create the runner script.
Expand All @@ -255,7 +317,7 @@ func ExecuteSSHDeploy(ctx context.Context, cmd *plugin.ExecutedCommand, chain *p
return err
}

bar.Describe("uploading runner script")
bar.Describe("Uploading runner script")
if _, err := c.UploadRunnerScript(localRunScriptPath, progressCallback); err != nil {
return err
}
Expand All @@ -264,7 +326,7 @@ func ExecuteSSHDeploy(ctx context.Context, cmd *plugin.ExecutedCommand, chain *p
if err != nil {
return err
}
fmt.Println(start)
_ = session.Println("")

return nil
return session.Println(color.Blue.Sprintf(start))
}
2 changes: 2 additions & 0 deletions spaceship/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.22.3
require (
github.com/gobuffalo/genny/v2 v2.1.0
github.com/gobuffalo/plush/v4 v4.1.19
github.com/gookit/color v1.5.4
github.com/hashicorp/go-plugin v1.6.0
github.com/ignite/cli/v28 v28.5.1
github.com/manifoldco/promptui v0.9.0
Expand Down Expand Up @@ -262,6 +263,7 @@ require (
github.com/tidwall/btree v1.7.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
github.com/yuin/goldmark-emoji v1.0.1 // indirect
github.com/zondax/hid v0.9.2 // indirect
Expand Down
4 changes: 4 additions & 0 deletions spaceship/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
Expand Down Expand Up @@ -1110,6 +1112,8 @@ github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
Loading

0 comments on commit 33c23cd

Please sign in to comment.