Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Improve backup ls command #86

Merged
merged 5 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ Eigenlayer is a setup wizard for EigenLayer Node Software. The tool installs, ma
- [Running a Plugin](#running-a-plugin)
- [Passing arguments to the plugin](#passing-arguments-to-the-plugin)


## Dependencies

This tool depends on [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) in order to manage the installation and running of EigenLayer nodes. Please make sure that you have both Docker and Docker Compose installed and configured properly before using this tool.
Expand All @@ -43,7 +42,8 @@ First, install the Go programming language following the [official instructions]
This command will install the `eigenlayer` executable along with the library and its dependencies in your system:

> As the repository is private, you need to set the `GOPRIVATE` variable properly by running the following command: `export GOPRIVATE=github.com/NethermindEth/eigenlayer,$GOPRIVATE`. Git will automatically resolve the private access if your Git user has all the required permissions over the repository.
```

```bash
go install github.com/NethermindEth/eigenlayer/cmd/eigenlayer@latest
```

Expand Down Expand Up @@ -71,7 +71,7 @@ go build -o build/eigenlayer cmd/eigenlayer/main.go

or if you have `make` installed:

```
```bash
git clone https://github.com/NethermindEth/eigenlayer.git
cd eigenlayer
make build
Expand All @@ -80,9 +80,9 @@ make build
The executable will be in the `build` folder.

---
In case you want the binary in your PATH (or if you used the [Using Go](#using-go) method and you don't have `$GOBIN` in your PATH), please copy the binary to `/usr/local/bin`:
In case you want the binary in your PATH (or if you used the [Using Go](#install-eigenlayer-cli-using-go) method and you don't have `$GOBIN` in your PATH), please copy the binary to `/usr/local/bin`:

```
```bash
# Using Go
sudo cp $GOPATH/bin/eigenlayer /usr/local/bin/

Expand Down Expand Up @@ -169,8 +169,8 @@ INFO[0002] The installed node software has a plugin.

Notice the usage of:

* `--profile` to select the `option-returner` profile without prompt.
* `--no-prompt` to skip options prompts.
- `--profile` to select the `option-returner` profile without prompt.
- `--no-prompt` to skip options prompts.

In this case, the `option-returner` profile uses all the default values. To set option values, use the `--option.<option-name>` dynamic flags. For instance:

Expand Down Expand Up @@ -230,7 +230,7 @@ eigenlayer update mock-avs-default

Output:

```
```log
INFO[0000] Pulling package...
INFO[0000] Package pulled successfully
INFO[0000] Package version changed: v5.4.0 -> v5.5.0
Expand Down Expand Up @@ -275,7 +275,7 @@ eigenlayer backup mock-avs-default

Output:

```bash
```log
INFO[0000] Backing up instance mock-avs-default
INFO[0000] Backing up instance data...
INFO[0000] Backup created with id: mock-avs-default-1696337650
Expand All @@ -292,8 +292,8 @@ eigenlayer backup ls
Output:

```bash
AVS Instance ID TIMESTAMP SIZE (GB)
mock-avs-default 2023-10-01 08:00:00 0.000009
ID AVS Instance ID VERSION COMMIT TIMESTAMP SIZE URL
6ee67470 mock-avs-default v5.5.1 d5af645fffb93e8263b099082a4f512e1917d0af 2023-10-04 13:41:06 10KiB https://github.com/NethermindEth/mock-avs-pkg
```

## Uninstalling AVS Node Software
Expand Down Expand Up @@ -347,7 +347,7 @@ AVS instance logs could be retrieved using the `eigenlayer logs` command. Logs a
eigenlayer logs mock-avs-default
```

```bash
```log
option-returner: INFO: Started server process [1]
option-returner: INFO: Waiting for application startup.
option-returner: INFO: Application startup complete.
Expand Down Expand Up @@ -389,7 +389,7 @@ eigenlayer plugin mock-avs-default

Output:

```bash
```log
INFO[0001] Running plugin with image eigen-plugin-mock-avs-default on network eigenlayer
INFO[0002]
AVS is up
Expand All @@ -415,4 +415,4 @@ INFO[0004]
AVS is up
```

In this case, the plugin container receives the `--port 8080` arguments. Note that this is not a flag of the `eigenlayer plugin` command.
In this case, the plugin container receives the `--port 8080` arguments. Note that this is not a flag of the `eigenlayer plugin` command.
19 changes: 17 additions & 2 deletions cli/backup_ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,38 @@ func BackupLsCmd(d daemon.Daemon) *cobra.Command {

func printBackupTable(backups []daemon.BackupInfo, out io.Writer) {
w := tabwriter.NewWriter(out, 0, 0, 4, ' ', 0)
fmt.Fprintln(w, "AVS Instance ID\tTIMESTAMP\tSIZE\t")
fmt.Fprintln(w, "ID\tAVS Instance ID\tVERSION\tCOMMIT\tTIMESTAMP\tSIZE\tURL\t")
for _, b := range backups {
fmt.Fprintln(w, backupTableItem{
id: b.Id,
instance: b.Instance,
timestamp: b.Timestamp.Format(time.DateTime),
size: datasize.Size(b.SizeBytes).String(),
version: b.Version,
commit: b.Commit,
url: b.Url,
})
}
w.Flush()
}

type backupTableItem struct {
id string
instance string
timestamp string
size string
version string
commit string
url string
}

func minifiedId(id string) string {
if len(id) > 8 {
return id[:8]
}
return id
}

func (b backupTableItem) String() string {
return fmt.Sprintf("%s\t%s\t%s\t", b.instance, b.timestamp, b.size)
return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t", minifiedId(b.id), b.instance, b.version, b.commit, b.timestamp, b.size, b.url)
}
104 changes: 104 additions & 0 deletions cli/backup_ls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cli

import (
"bytes"
"testing"
"time"

"github.com/NethermindEth/eigenlayer/cli/mocks"
"github.com/NethermindEth/eigenlayer/pkg/daemon"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestBackupLs(t *testing.T) {
tc := []struct {
name string
err error
stdErr []byte
stdOut []byte
mocker func(d *mocks.MockDaemon)
}{
{
name: "no backups",
err: nil,
stdErr: nil,
stdOut: []byte("ID AVS Instance ID VERSION COMMIT TIMESTAMP SIZE URL \n"),
mocker: func(d *mocks.MockDaemon) {
d.EXPECT().BackupList().Return([]daemon.BackupInfo{}, nil)
},
},
{
name: "with backups",
err: nil,
stdErr: nil,
stdOut: []byte(
"ID AVS Instance ID VERSION COMMIT TIMESTAMP SIZE URL \n" +
"33de69fe mock-avs-default v5.5.0 a3406616b848164358fdd24465b8eecda5f5ae34 2023-10-03 21:18:36 10KiB https://github.com/NethermindEth/mock-avs-pkg \n" +
"7ba32f63 mock-avs-second v5.5.1 d5af645fffb93e8263b099082a4f512e1917d0af 2023-10-04 07:12:19 10KiB https://github.com/NethermindEth/mock-avs-pkg \n",
),
mocker: func(d *mocks.MockDaemon) {
d.EXPECT().BackupList().Return([]daemon.BackupInfo{
{
Id: "33de69fe9225b95c8fb909cb418e5102970c8d73",
Instance: "mock-avs-default",
Version: "v5.5.0",
Commit: "a3406616b848164358fdd24465b8eecda5f5ae34",
Timestamp: time.Date(2023, 10, 3, 21, 18, 36, 0, time.UTC),
SizeBytes: 10240,
Url: "https://github.com/NethermindEth/mock-avs-pkg",
},
{
Id: "7ba32f630af2cede1388b5712d6ef3ac63175bae",
Instance: "mock-avs-second",
Version: "v5.5.1",
Commit: "d5af645fffb93e8263b099082a4f512e1917d0af",
Timestamp: time.Date(2023, 10, 4, 7, 12, 19, 0, time.UTC),
SizeBytes: 10240,
Url: "https://github.com/NethermindEth/mock-avs-pkg",
},
}, nil)
},
},
{
name: "error",
err: assert.AnError,
stdErr: []byte("Error: " + assert.AnError.Error() + "\n"),
stdOut: []byte{},
mocker: func(d *mocks.MockDaemon) {
d.EXPECT().BackupList().Return(nil, assert.AnError)
},
},
}

for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

d := mocks.NewMockDaemon(ctrl)

tt.mocker(d)

var (
stdOut bytes.Buffer
stdErr bytes.Buffer
)

backupLsCmd := BackupLsCmd(d)
backupLsCmd.SetOut(&stdOut)
backupLsCmd.SetErr(&stdErr)
err := backupLsCmd.Execute()

if tt.err != nil {
require.Error(t, err)
assert.EqualError(t, err, tt.err.Error())
assert.Equal(t, tt.stdErr, stdErr.Bytes())
} else {
require.NoError(t, err)
assert.Equal(t, tt.stdOut, stdOut.Bytes())
}
})
}
}
20 changes: 12 additions & 8 deletions e2e/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ package e2e
import (
"regexp"
"testing"
"time"

"github.com/NethermindEth/eigenlayer/internal/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestBackupInstance(t *testing.T) {
// Test context
var (
output []byte
backupErr error
start time.Time
)
// Build test case
e2eTest := newE2ETestCase(
t,
// Arrange
func(t *testing.T, egnPath string) error {
start = time.Now()
err := buildOptionReturnerImageLatest(t)
if err != nil {
return err
Expand All @@ -30,12 +29,18 @@ func TestBackupInstance(t *testing.T) {
},
// Act
func(t *testing.T, egnPath string) {
backupErr = runCommand(t, egnPath, "backup", "mock-avs-default")
output, backupErr = runCommandOutput(t, egnPath, "backup", "mock-avs-default")
},
// Assert
func(t *testing.T) {
assert.NoError(t, backupErr, "backup command should succeed")
checkBackupExist(t, "mock-avs-default", start, time.Now())

r := regexp.MustCompile(`.*"Backup created with id: (?P<backupId>[a-f0-9]+)".*`)
match := r.FindSubmatch(output)
require.Len(t, match, 2, "backup command output should match regex")
instanceId := string(match[1])

checkBackupExist(t, instanceId)
},
)
// Run test case
Expand Down Expand Up @@ -72,9 +77,8 @@ func TestBackupList(t *testing.T) {
func(t *testing.T) {
t.Log(string(out))
assert.NoError(t, backupErr, "backup ls command should succeed")
assert.Regexp(t, regexp.MustCompile(
`AVS Instance ID TIMESTAMP SIZE
mock-avs-default .* 9KiB`),
assert.Regexp(t, regexp.MustCompile(`ID\s+AVS Instance ID\s+VERSION\s+COMMIT\s+TIMESTAMP\s+SIZE\s+URL\s+`+
`[a-f0-9]{8}\s+mock-avs-default\s+v5\.5\.1\s+d5af645fffb93e8263b099082a4f512e1917d0af\s+.*\s+10KiB\s+https://github.com/NethermindEth/mock-avs-pkg\s+`),
string(out))
},
)
Expand Down
32 changes: 4 additions & 28 deletions e2e/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import (
"context"
"fmt"
"net/url"
"os"
"path/filepath"
"slices"
"testing"
"time"

"github.com/NethermindEth/eigenlayer/internal/data"
"github.com/NethermindEth/eigenlayer/internal/env"
"github.com/NethermindEth/eigenlayer/pkg/monitoring"
"github.com/cenkalti/backoff"
Expand Down Expand Up @@ -367,32 +365,10 @@ func checkEnvTargets(t *testing.T, instanceId string, targets ...string) {
}
}

// checkBackupExist checks that a backup exists for the given instanceId between
// the given times a and b, inclusive.
func checkBackupExist(t *testing.T, instanceId string, a, b time.Time) {
// TODO add doc
func checkBackupExist(t *testing.T, backupId string) {
dataDir, err := dataDirPath()
require.NoError(t, err)
backupsDir := filepath.Join(dataDir, "backup")

dirFiles, err := os.ReadDir(backupsDir)
require.NoError(t, err)

var found bool
for _, f := range dirFiles {
if f.IsDir() {
continue
}
i, timestamp, err := data.ParseBackupName(filepath.Base(f.Name()))
if err != nil {
continue
}
if i == instanceId &&
((timestamp.After(a)) && timestamp.Before(b) ||
timestamp.Equal(a) ||
timestamp.Equal(b)) {
found = true
break
}
}
assert.True(t, found)
backupPath := filepath.Join(dataDir, "backup", backupId+".tar")
assert.FileExists(t, backupPath)
}
Loading