Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWTsecret to relative path #484

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Override `--latest` flag to not use the latest version of the image in the clients if image is specified
- JWT secret path set as relative path when not provided.

## [v1.7.2] - 2024-11-12

Expand Down
20 changes: 15 additions & 5 deletions cli/actions/jwt_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ func (s *sedgeActions) CreateJWTSecrets(options CreateJWTSecretOptions) (string,
jwtPath := options.JWTPath
if jwtPath == "" && !configs.NetworksConfigs()[options.Network].NoJWT {
return handleJWTSecret(options.GenerationPath)
} else if filepath.IsAbs(jwtPath) { // Ensure jwtPath is absolute
if jwtPath, err = filepath.Abs(jwtPath); err != nil {
return jwtPath, err
}
}
// Handle relative paths by joining with generation path
if !filepath.IsAbs(jwtPath) {
jwtPath = filepath.Join(options.GenerationPath, jwtPath)
}
// Create parent directories if they don't exist
if err = os.MkdirAll(filepath.Dir(jwtPath), 0o755); err != nil {
return "", fmt.Errorf(configs.GenerateJWTSecretError, err)
}
return jwtPath, nil
}
Expand Down Expand Up @@ -71,5 +75,11 @@ func handleJWTSecret(generationPath string) (string, error) {
}

log.Info(configs.JWTSecretGenerated)
return jwtPath, nil

// Convert the absolute path to a relative path
relPath, err := filepath.Rel(generationPath, jwtPath)
if err != nil {
return "", fmt.Errorf(configs.GenerateJWTSecretError, err)
}
return relPath, nil
}
6 changes: 5 additions & 1 deletion cli/actions/jwt_secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ func TestCreateJwtSecrets(t *testing.T) {
if jwtPath == "" {
return
}
assert.FileExists(t, jwtPath)
if tc.options.JWTPath == "" {
assert.FileExists(t, filepath.Join(tempDir, "jwtsecret"))
} else {
assert.FileExists(t, jwtPath)
}
})
}
}
40 changes: 32 additions & 8 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func TestCli(t *testing.T) {
name: "full node with validator mainnet",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"execution", "consensus", "validator", "mev-boost"},
ExecutionClient: &clients.Client{
Expand Down Expand Up @@ -95,7 +98,7 @@ func TestCli(t *testing.T) {
MevImage: "flashbots/mev-boost:latest",
RelayURLs: configs.NetworksConfigs()[NetworkMainnet].RelayURLs,
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}
sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{})
gomock.InOrder(
Expand Down Expand Up @@ -151,6 +154,9 @@ func TestCli(t *testing.T) {
name: "full node without validator mainnet",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"execution", "consensus"},
ExecutionClient: &clients.Client{
Expand All @@ -168,7 +174,7 @@ func TestCli(t *testing.T) {
FeeRecipient: "0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5",
MapAllPorts: true,
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}
gomock.InOrder(
prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil),
Expand Down Expand Up @@ -196,6 +202,9 @@ func TestCli(t *testing.T) {
name: "full node without validator holesky",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"execution", "consensus"},
ExecutionClient: &clients.Client{
Expand All @@ -213,7 +222,7 @@ func TestCli(t *testing.T) {
FeeRecipient: "0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5",
MapAllPorts: true,
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}
gomock.InOrder(
prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil),
Expand Down Expand Up @@ -331,6 +340,9 @@ func TestCli(t *testing.T) {
name: "consensus node",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"consensus"},
ConsensusClient: &clients.Client{
Expand All @@ -346,7 +358,7 @@ func TestCli(t *testing.T) {
ExecutionAuthUrl: "http://execution:5051",
MevBoostEndpoint: "http://mev-boost:3030",
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}

gomock.InOrder(
Expand Down Expand Up @@ -376,6 +388,9 @@ func TestCli(t *testing.T) {
name: "consensus node holesky",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"consensus"},
ConsensusClient: &clients.Client{
Expand All @@ -391,7 +406,7 @@ func TestCli(t *testing.T) {
ExecutionAuthUrl: "http://execution:5051",
MevBoostEndpoint: "http://mev-boost:3030",
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}

gomock.InOrder(
Expand Down Expand Up @@ -485,6 +500,9 @@ func TestCli(t *testing.T) {
name: "consensus node",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"consensus"},
ConsensusClient: &clients.Client{
Expand All @@ -500,7 +518,7 @@ func TestCli(t *testing.T) {
ExecutionAuthUrl: "http://execution:5051",
MevBoostEndpoint: "http://mev-boost:3030",
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}

gomock.InOrder(
Expand Down Expand Up @@ -530,6 +548,9 @@ func TestCli(t *testing.T) {
name: "full node with Lido, mainnet",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"execution", "consensus", "validator", "mev-boost"},
ExecutionClient: &clients.Client{
Expand Down Expand Up @@ -557,7 +578,7 @@ func TestCli(t *testing.T) {
MevImage: "flashbots/mev-boost:latest",
RelayURLs: mainnetMevboostRelayListUris,
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}
sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{})
gomock.InOrder(
Expand Down Expand Up @@ -609,6 +630,9 @@ func TestCli(t *testing.T) {
name: "full node with Lido, holesky",
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
generationPath := t.TempDir()
jwtPathAbs, _ := filepath.Abs(filepath.Join(generationPath, "jwtsecret"))
jwtPath, _ := filepath.Rel(generationPath, jwtPathAbs)
relativeJWTPath := "." + string(filepath.Separator) + jwtPath
genData := generate.GenData{
Services: []string{"execution", "consensus", "validator", "mev-boost"},
ExecutionClient: &clients.Client{
Expand Down Expand Up @@ -636,7 +660,7 @@ func TestCli(t *testing.T) {
MevImage: "flashbots/mev-boost:latest",
RelayURLs: holeskyMevboostRelayListUris,
ContainerTag: "tag",
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
JWTSecretPath: relativeJWTPath,
}
sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{})
gomock.InOrder(
Expand Down
15 changes: 13 additions & 2 deletions cli/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,21 @@ func handleJWTSecret(generationPath, name string) (string, error) {
}

log.Info(configs.JWTSecretGenerated)
return jwtPath, nil

// Convert the absolute path to a relative path
relativeJWTPath, err := filepath.Rel(generationPath, jwtPath)
if err != nil {
return "", fmt.Errorf(configs.GenerateJWTSecretError, err)
}

// Prepend "./" to ensure compatibility with relative paths
if !filepath.IsAbs(relativeJWTPath) && !filepath.HasPrefix(relativeJWTPath, ".") {
relativeJWTPath = "." + string(filepath.Separator) + relativeJWTPath
}

return relativeJWTPath, nil
}

// TODO: Add unit tests
func loadJWTSecret(from string) (absFrom string, err error) {
// Ensure from is absolute
absFrom, err = filepath.Abs(from)
Expand Down
150 changes: 150 additions & 0 deletions e2e/sedge/jwt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
Copyright 2022 Nethermind

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package e2e

import (
"os"
"path/filepath"
"testing"

base "github.com/NethermindEth/sedge/e2e"
"github.com/stretchr/testify/assert"
)

const (
// validJWTSecret is a 32-byte hex string used as a valid JWT secret
validJWTSecret = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
// jwtSecretPath is the default path for JWT secrets
jwtSecretPath = "jwtsecret"
)

func TestE2E_Generate_JWTSecret_RelativePath(t *testing.T) {
// Test context
var (
runErr error
jwtPath = "custom/path/jwtsecret"
)

// Build test case
e2eTest := newE2ESedgeTestCase(
t,
// Arrange
func(t *testing.T, sedgePath string) error {
// Create the JWT secret file in the data directory
dataDir := filepath.Join(filepath.Dir(sedgePath), "sedge-data")
fullJWTPath := filepath.Join(dataDir, jwtPath)
if err := os.MkdirAll(filepath.Dir(fullJWTPath), 0o755); err != nil {
return err
}
if err := os.WriteFile(fullJWTPath, []byte(validJWTSecret), 0o644); err != nil {
return err
}
return nil
},
// Act
func(t *testing.T, binaryPath, dataDirPath string) {
// Use the full path when running the generate command
fullJWTPath := filepath.Join(dataDirPath, jwtPath)
runErr = base.RunSedge(t, binaryPath, "generate", "full-node", "--network", "mainnet", "--jwt-secret-path", fullJWTPath)
},
// Assert
func(t *testing.T, dataDirPath string) {
assert.NoError(t, runErr)
// Check if JWT file exists in the relative path
fullJWTPath := filepath.Join(dataDirPath, jwtPath)
_, err := os.Stat(fullJWTPath)
assert.NoError(t, err, "JWT secret file should exist at relative path")

// Verify the JWT secret content
content, err := os.ReadFile(fullJWTPath)
assert.NoError(t, err)
assert.Equal(t, validJWTSecret, string(content), "JWT secret content should match")
},
)
e2eTest.run()
}

func TestE2E_Generate_JWTSecret_AbsolutePath(t *testing.T) {
// Test context
var (
runErr error
tempJWTDir string
)

// Build test case
e2eTest := newE2ESedgeTestCase(
t,
// Arrange
func(t *testing.T, sedgePath string) error {
// Create a temporary directory for JWT secret
tempJWTDir = t.TempDir()
// Create the JWT secret file
jwtPath := filepath.Join(tempJWTDir, "jwtsecret")
return os.WriteFile(jwtPath, []byte(validJWTSecret), 0o644)
},
// Act
func(t *testing.T, binaryPath, dataDirPath string) {
jwtPath := filepath.Join(tempJWTDir, "jwtsecret")
runErr = base.RunSedge(t, binaryPath, "generate", "full-node", "--network", "mainnet", "--jwt-secret-path", jwtPath)
},
// Assert
func(t *testing.T, dataDirPath string) {
assert.NoError(t, runErr)
// Check if JWT file exists in the absolute path
jwtPath := filepath.Join(tempJWTDir, "jwtsecret")
_, err := os.Stat(jwtPath)
assert.NoError(t, err, "JWT secret file should exist at absolute path")

// Verify the JWT secret content
content, err := os.ReadFile(jwtPath)
assert.NoError(t, err)
assert.Equal(t, validJWTSecret, string(content), "JWT secret content should match")
},
)
e2eTest.run()
}

func TestE2E_Generate_JWTSecret_DefaultPath(t *testing.T) {
// Test context
var (
runErr error
)

// Build test case
e2eTest := newE2ESedgeTestCase(
t,
// Arrange
nil,
// Act
func(t *testing.T, binaryPath, dataDirPath string) {
runErr = base.RunSedge(t, binaryPath, "generate", "full-node", "--network", "mainnet")
},
// Assert
func(t *testing.T, dataDirPath string) {
assert.NoError(t, runErr)
// Check if JWT file exists in the default path
defaultJWTPath := filepath.Join(dataDirPath, "jwtsecret")
_, err := os.Stat(defaultJWTPath)
assert.NoError(t, err, "JWT secret file should exist at default path")

// Read the file to ensure it's not empty
content, err := os.ReadFile(defaultJWTPath)
assert.NoError(t, err)
assert.NotEmpty(t, content, "JWT secret file should not be empty")
},
)
e2eTest.run()
}
Loading