Skip to content

feat(docker): custom network support #49

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

Merged
merged 9 commits into from
Jan 16, 2025
1 change: 1 addition & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ archives:
- install.sh
- docker-compose.yml
- docker-compose.ports.yml
- docker-compose.network.yml

changelog:
sort: asc
Expand Down
68 changes: 66 additions & 2 deletions cmd/cli/commands/config/config_network.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package config

import (
"os/exec"
"strings"

"github.com/ethpandaops/contributoor-installer/internal/tui"
"github.com/ethpandaops/contributoor-installer/internal/validate"
"github.com/ethpandaops/contributoor/pkg/config/v1"
Expand Down Expand Up @@ -85,6 +88,47 @@ func (p *NetworkConfigPage) initPage() {
p.description.SetText(networkDescriptions[option])
})
form.AddInputField("Beacon Node Address", p.display.sidecarCfg.Get().BeaconNodeAddress, 0, nil, nil)

// Add Docker network dropdown if using Docker.
if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
// Get list of existing Docker networks.
networks := []string{"<Please Select>"}
cmd := exec.Command("docker", "network", "ls", "--format", "{{.Name}}")

output, err := cmd.Output()
if err == nil {
for _, network := range strings.Split(strings.TrimSpace(string(output)), "\n") {
if network != "" && !strings.Contains(network, "contributoor") {
networks = append(networks, network)
}
}
}

// Add network dropdown.
networkDropdown := tview.NewDropDown().
SetLabel("Optional Docker Network: ").
SetOptions(networks, nil).
SetFieldBackgroundColor(tcell.ColorBlack).
SetLabelColor(tcell.ColorLightGray).
SetFieldTextColor(tcell.ColorLightGray)

// Set current value if exists.
currentNetwork := p.display.sidecarCfg.Get().DockerNetwork
if currentNetwork == "" {
networkDropdown.SetCurrentOption(0)
} else {
for i, network := range networks {
if network == currentNetwork {
networkDropdown.SetCurrentOption(i)

break
}
}
}

form.AddFormItem(networkDropdown)
}

form.AddInputField("Metrics Address", p.display.sidecarCfg.Get().MetricsAddress, 0, nil, nil)

// Add a save button and ensure we validate the input.
Expand Down Expand Up @@ -172,11 +216,30 @@ func (p *NetworkConfigPage) initPage() {
func validateAndUpdateNetwork(p *NetworkConfigPage) {
networkDropdown, _ := p.form.GetFormItem(0).(*tview.DropDown)
beaconInput, _ := p.form.GetFormItem(1).(*tview.InputField)
metricsInput, _ := p.form.GetFormItem(2).(*tview.InputField)

var (
dockerNetwork string
metricsAddress string
)

if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
dockerDropdown, ok := p.form.GetFormItem(2).(*tview.DropDown)
if ok {
_, dockerNetwork = dockerDropdown.GetCurrentOption()
if dockerNetwork == "<Please Select>" {
dockerNetwork = ""
}
}

metricsInput, _ := p.form.GetFormItem(3).(*tview.InputField)
metricsAddress = metricsInput.GetText()
} else {
metricsInput, _ := p.form.GetFormItem(2).(*tview.InputField)
metricsAddress = metricsInput.GetText()
}

_, networkName := networkDropdown.GetCurrentOption()
beaconAddress := beaconInput.GetText()
metricsAddress := metricsInput.GetText()

if err := validate.ValidateBeaconNodeAddress(beaconAddress); err != nil {
p.openErrorModal(err)
Expand All @@ -194,6 +257,7 @@ func validateAndUpdateNetwork(p *NetworkConfigPage) {
cfg.NetworkName = config.NetworkName(config.NetworkName_value[networkName])
cfg.BeaconNodeAddress = beaconAddress
cfg.MetricsAddress = metricsAddress
cfg.DockerNetwork = dockerNetwork
}); err != nil {
p.openErrorModal(err)

Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/commands/install/page_10_welcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (p *WelcomePage) initPage() {

modal := tview.NewModal().
SetText(helperText).
AddButtons([]string{tui.ButtonNext, tui.ButtonClose}).
AddButtons([]string{tui.ButtonNext}).
SetBackgroundColor(tui.ColorFormBackground).
SetButtonStyle(tcell.StyleDefault.
Background(tcell.ColorDefault).
Expand Down
2 changes: 1 addition & 1 deletion cmd/cli/commands/install/page_20_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func NewNetworkConfigPage(display *InstallDisplay) *NetworkConfigPage {

networkConfigPage.initPage()
networkConfigPage.page = tui.NewPage(
nil,
display.welcomePage.GetPage(),
"install-network",
"Network Selection",
"Select which network you're using",
Expand Down
3 changes: 3 additions & 0 deletions cmd/cli/commands/install/page_20_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func TestNetworkConfigPage(t *testing.T) {
app: tview.NewApplication(),
log: logrus.New(),
sidecarCfg: mockConfig,
welcomePage: &WelcomePage{
page: &tui.Page{ID: "welcome"},
},
}
}

Expand Down
129 changes: 112 additions & 17 deletions cmd/cli/commands/install/page_30_beacon_node.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package install

import (
"os/exec"
"strings"

"github.com/ethpandaops/contributoor-installer/internal/tui"
"github.com/ethpandaops/contributoor-installer/internal/validate"
"github.com/ethpandaops/contributoor/pkg/config/v1"
Expand Down Expand Up @@ -44,9 +47,9 @@ func (p *BeaconNodePage) initPage() {
var (
// Some basic dimensions for the page modal.
modalWidth = 70
lines = tview.WordWrap("Please enter the address of your Beacon Node.\nFor example: http://localhost:5052", modalWidth-4)
textViewHeight = len(lines) + 4
formHeight = 3 // Input field + a bit of padding.
lines = tview.WordWrap("Please enter the address of your Beacon Node.\nFor example: http://127.0.0.1:5052", modalWidth-4)
textViewHeight = len(lines) + 8
formHeight = 6 // Input field + network dropdown + button + padding

// Main grids.
contentGrid = tview.NewGrid()
Expand All @@ -71,6 +74,46 @@ func (p *BeaconNodePage) initPage() {
SetLabelColor(tcell.ColorLightGray)
form.AddFormItem(inputField)

// Only show docker network options if runMethod is Docker.
if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
// Get list of existing Docker networks the user has.
networks := []string{"<Please Select>"}
cmd := exec.Command("docker", "network", "ls", "--format", "{{.Name}}")

output, err := cmd.Output()
if err == nil {
for _, network := range strings.Split(strings.TrimSpace(string(output)), "\n") {
if network != "" && !strings.Contains(network, "contributoor") {
networks = append(networks, network)
}
}
}

// Add network dropdown.
networkDropdown := tview.NewDropDown().
SetLabel("Optional Docker Network: ").
SetOptions(networks, nil).
SetFieldBackgroundColor(tcell.ColorBlack).
SetLabelColor(tcell.ColorLightGray).
SetFieldTextColor(tcell.ColorLightGray)

// Set current value if exists.
currentNetwork := p.display.sidecarCfg.Get().DockerNetwork
if currentNetwork == "" {
networkDropdown.SetCurrentOption(0) // Select placeholder by default
} else {
for i, network := range networks {
if network == currentNetwork {
networkDropdown.SetCurrentOption(i)

break
}
}
}

form.AddFormItem(networkDropdown)
}

// Add our form to the page for easy access during validation.
p.form = form

Expand All @@ -93,32 +136,70 @@ func (p *BeaconNodePage) initPage() {
form.SetButtonActivatedStyle(tcell.StyleDefault.
Background(tui.ColorButtonActivated).
Foreground(tcell.ColorBlack))

// Set up dropdown callback now that we have the button
if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
if item := p.form.GetFormItem(1); item != nil {
if dropdown, ok := item.(*tview.DropDown); ok && dropdown != nil {
dropdown.SetSelectedFunc(func(text string, index int) {
p.display.app.SetFocus(button)
})
}
}
}

// Set up dropdown callback now that we have the button
if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
if item := p.form.GetFormItem(1); item != nil {
if dropdown, ok := item.(*tview.DropDown); ok && dropdown != nil {
dropdown.SetSelectedFunc(func(text string, index int) {
p.display.app.SetFocus(button)
})
}
}
}
}

// Create the main text view.
textView := tview.NewTextView()
textView.SetText("Please enter the address of your Beacon Node.\nFor example: http://localhost:5052")
textView.SetTextAlign(tview.AlignCenter)
textView.SetWordWrap(true)
textView.SetTextColor(tview.Styles.PrimaryTextColor)
textView.SetBackgroundColor(tui.ColorFormBackground)
textView.SetBorderPadding(0, 0, 0, 0)
// Create the header text view
headerView := tview.NewTextView()
headerView.SetText("Please enter the address of your Beacon Node.")
headerView.SetTextAlign(tview.AlignCenter)
headerView.SetTextColor(tview.Styles.PrimaryTextColor)
headerView.SetBackgroundColor(tui.ColorFormBackground)
headerView.SetBorderPadding(0, 0, 0, 0)

// Create the examples text view.
optionsView := tview.NewTextView()

var optionsText string
if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
optionsText = "\nExamples:\n1. Local beacon node (e.g., http://127.0.0.1:5052)\n2. Docker container (e.g., http://beacon:5052)\n - Optionally specify an existing Docker network to join"
} else {
optionsText = "\nExample: http://127.0.0.1:5052"
}

optionsView.SetText(optionsText)
optionsView.SetTextAlign(tview.AlignLeft)
optionsView.SetWordWrap(true)
optionsView.SetTextColor(tview.Styles.PrimaryTextColor)
optionsView.SetBackgroundColor(tui.ColorFormBackground)
optionsView.SetBorderPadding(0, 0, 0, 0)

// Set up the content grid.
contentGrid.SetRows(2, 2, 1, 4, 1, 2, 2)
contentGrid.SetRows(1, 1, 5, 1, 6, 1)
contentGrid.SetBackgroundColor(tui.ColorFormBackground)
contentGrid.SetBorder(true)
contentGrid.SetTitle(" Beacon Node ")

// Add items to content grid using spacers.
contentGrid.AddItem(tview.NewBox().SetBackgroundColor(tui.ColorFormBackground), 0, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(textView, 1, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(tview.NewBox().SetBackgroundColor(tui.ColorFormBackground), 2, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(formFrame, 3, 0, 2, 1, 0, 0, true)
contentGrid.AddItem(tview.NewBox().SetBackgroundColor(tui.ColorFormBackground), 5, 0, 2, 1, 0, 0, false)
contentGrid.AddItem(headerView, 1, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(optionsView, 2, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(tview.NewBox().SetBackgroundColor(tui.ColorFormBackground), 3, 0, 1, 1, 0, 0, false)
contentGrid.AddItem(formFrame, 4, 0, 2, 1, 0, 0, true)

// Border grid.
borderGrid.SetRows(0, textViewHeight+formHeight+4, 0, 2)
borderGrid.SetRows(0, textViewHeight+formHeight+1, 0, 2)
borderGrid.AddItem(contentGrid, 1, 1, 1, 1, 0, 0, true)

// Set initial focus.
Expand All @@ -127,6 +208,19 @@ func (p *BeaconNodePage) initPage() {
}

func validateAndUpdate(p *BeaconNodePage, input *tview.InputField) {
var networkName string

if p.display.sidecarCfg.Get().RunMethod == config.RunMethod_RUN_METHOD_DOCKER {
if item := p.form.GetFormItem(1); item != nil {
if dropdown, ok := item.(*tview.DropDown); ok {
_, networkName = dropdown.GetCurrentOption()
if networkName == "<Please Select>" {
networkName = ""
}
}
}
}

if err := validate.ValidateBeaconNodeAddress(input.GetText()); err != nil {
p.openErrorModal(err)

Expand All @@ -135,6 +229,7 @@ func validateAndUpdate(p *BeaconNodePage, input *tview.InputField) {

if err := p.display.sidecarCfg.Update(func(cfg *config.Config) {
cfg.BeaconNodeAddress = input.GetText()
cfg.DockerNetwork = networkName
}); err != nil {
p.openErrorModal(err)

Expand Down
9 changes: 9 additions & 0 deletions docker-compose.network.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
sentry:
networks:
- contributoor

networks:
contributoor:
name: ${CONTRIBUTOOR_DOCKER_NETWORK}
external: true
8 changes: 1 addition & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,8 @@ services:
image: ethpandaops/contributoor:${CONTRIBUTOOR_VERSION}
entrypoint: ["/usr/local/bin/sentry"]
command: ["--config=/config/config.yaml"]
networks:
- contributoor
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ${CONTRIBUTOOR_CONFIG_PATH}/config.yaml:/config/config.yaml:ro
restart: always

networks:
contributoor:
driver: bridge
restart: always
4 changes: 3 additions & 1 deletion install.bats
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ EOF
# Create compose files
touch "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.yml"
touch "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.ports.yml"
touch "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.network.yml"

return 0
}
Expand All @@ -413,6 +414,7 @@ EOF
if [[ "$3" == *"/bin/contributoor" ]]; then
cp "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.yml" "$(dirname "$3")/docker-compose.yml"
cp "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.ports.yml" "$(dirname "$3")/docker-compose.ports.yml"
cp "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.network.yml" "$(dirname "$3")/docker-compose.network.yml"
fi
fi
return 0
Expand All @@ -431,8 +433,8 @@ EOF
[ -f "$CONTRIBUTOOR_PATH/bin/contributoor" ]
[ -x "$CONTRIBUTOOR_PATH/bin/contributoor" ]
[ -f "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.yml" ]
[ -f "$CONTRIBUTOOR_PATH/bin/docker-compose.yml" ]
[ -f "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.ports.yml" ]
[ -f "$CONTRIBUTOOR_PATH/releases/installer-${INSTALLER_VERSION}/docker-compose.network.yml" ]
}

@test "setup_installer fails on checksum mismatch" {
Expand Down
5 changes: 5 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ setup_installer() {
chmod 644 "$release_dir/docker-compose.ports.yml"
chmod 755 "$release_dir"
} || fail "docker-compose.ports.yml not found after extraction"

[ -f "$release_dir/docker-compose.network.yml" ] && {
chmod 644 "$release_dir/docker-compose.network.yml"
chmod 755 "$release_dir"
} || fail "docker-compose.network.yml not found after extraction"

# Create/update symlink
rm -f "$CONTRIBUTOOR_BIN/contributoor" # Remove existing symlink or file
Expand Down
Loading
Loading