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

Jlo/fix-relay-restarts #15

Merged
merged 4 commits into from
Aug 26, 2024
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.22-bookworm as builder
FROM golang:1.22-bookworm AS builder

WORKDIR /app

Expand Down
18 changes: 15 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ import (
"syscall"

"github.com/okteto/supervisor/pkg/monitor"
"github.com/okteto/supervisor/pkg/setup"
reaper "github.com/ramr/go-reaper"
log "github.com/sirupsen/logrus"
)

// CommitString is the commit used to build the server
var CommitString string

const (
defaultSyncthingData = "/var/syncthing/data"
defaultSyncthingConfig = "/var/syncthing"
defaultSyncthingSecret = "/var/syncthing/secret"
)

func main() {
log.WithField("commit", CommitString).Infof("supervisor started")

Expand All @@ -25,6 +32,7 @@ func main() {
remoteFlag := flag.Bool("remote", false, "start the remote server")
resetFlag := flag.Bool("reset", false, "reset syncthing database")
verboseFlag := flag.Bool("verbose", true, "syncthing verbosity")

flag.Parse()

ctx, cancel := context.WithCancel(context.Background())
Expand All @@ -36,16 +44,20 @@ func main() {
}()

if *resetFlag {
cmd := exec.Command(monitor.SyncthingBin, "-home", "/var/syncthing", "-reset-database")
if err := setup.Setup(defaultSyncthingSecret, defaultSyncthingConfig); err != nil {
log.WithError(err).Error("error setting up syncthing")
os.Exit(1)
}
cmd := exec.Command(monitor.SyncthingBin, "-config", defaultSyncthingConfig, "-data", defaultSyncthingData, "-reset-database")
output, err := cmd.CombinedOutput()
if err != nil {
log.WithError(err).Errorf("error resetting syncthing database: %s", output)
}
}

m := monitor.NewMonitor(ctx)
m := monitor.NewMonitor(ctx, monitor.NewSyncthingConfig(defaultSyncthingConfig, defaultSyncthingSecret, defaultSyncthingData))

syncthingArgs := []string{"-home", "/var/syncthing", "-gui-address", "0.0.0.0:8384"}
syncthingArgs := []string{"-config", defaultSyncthingConfig, "-data", defaultSyncthingData, "-gui-address", "0.0.0.0:8384"}
if *verboseFlag {
syncthingArgs = append(syncthingArgs, "-verbose")
}
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/ramr/go-reaper v0.2.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.4.2
github.com/spf13/afero v1.11.0
)

require (
Expand All @@ -15,5 +16,6 @@ require (
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
Expand All @@ -28,5 +30,7 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
40 changes: 23 additions & 17 deletions okteto.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
name: supervisor
image: okteto/golang:1
command:
- bash
workdir: /okteto
volumes:
- /go/pkg/
- /root/.cache/go-build/
securityContext:
capabilities:
add:
- SYS_PTRACE
forward:
- 8080:8080
- 2345:2345
persistentVolume:
enabled: true
build:
supervisor:
context: .
image: ${REGISTRY_AND_USER:-okteto}/supervisor:${VERSION:-latest}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think if we use some more conventional env vars?

If we use OKTETO_NAMESPACE instead of USER it's replaced out of the box. For the Registry, unfortunatelty it's only injected in the deploy steps, but still we could have a comment as example.

Suggested change
image: ${REGISTRY_AND_USER:-okteto}/supervisor:${VERSION:-latest}
image: ${OKTETO_REGISTRY_URL}/${OKTETO_NAMESPACE}/supervisor:${VERSION:-latest}
# Example: OKTETO_REGISTRY_URL=registry.product.okteto.dev okteto build`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that approach it's difficult to know that you can push to other registries rather than the okteto one, like docker hub.

dev:
supervisor:
image: okteto/golang:1
command:
- bash
workdir: /okteto
volumes:
- /go/pkg/
- /root/.cache/go-build/
securityContext:
capabilities:
add:
- SYS_PTRACE
forward:
- 8080:8080
- 2345:2345
persistentVolume:
enabled: true
autocreate: true
58 changes: 50 additions & 8 deletions pkg/monitor/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,40 @@ import (
"sync"
"time"

"github.com/okteto/supervisor/pkg/setup"
log "github.com/sirupsen/logrus"
)

// Monitor makes sure the list of processes is running
type Monitor struct {
ps []*Process
err chan error
ctx context.Context
ps []*Process
err chan error
ctx context.Context
syncthingConfig SyncthingConfig
}

// SyncthingConfig represents the syncthing configuration
type SyncthingConfig struct {
config string
data string
secret string
}

// NewSyncthingConfig returns a new syncthing config
func NewSyncthingConfig(config, secret, data string) SyncthingConfig {
return SyncthingConfig{
config: config,
data: data,
secret: secret,
}
}

// NewMonitor returns an initialized monitor
func NewMonitor(ctx context.Context) *Monitor {
func NewMonitor(ctx context.Context, syncthingConfig SyncthingConfig) *Monitor {
return &Monitor{
ctx: ctx,
err: make(chan error, 1),
ctx: ctx,
err: make(chan error, 1),
syncthingConfig: syncthingConfig,
}
}

Expand Down Expand Up @@ -69,18 +88,35 @@ func (m *Monitor) checkState(p *Process, wg *sync.WaitGroup) {
defer wg.Done()
switch p.state {
case neverStarted:
log.Infof("starting %s for the first time", p.Name)
p.logger.Printf("starting %s for the first time", p.Name)
if p.Path == SyncthingBin {
if err := setup.Setup(m.syncthingConfig.secret, m.syncthingConfig.config); err != nil {
p.logger.Errorf("failed to setup %s: %s", p.Name, err.Error())
m.err <- fmt.Errorf("failed to setup %s: %w", p.Name, err)
return
}
}
p.start()
p.logger.Printf("%s started 1", p.Name)
case stopped:
if !p.shouldStart() {
log.Errorf("%s started %d times", p.Name, p.startCount)
m.err <- fmt.Errorf("%s started %d times", p.Name, p.startCount)
return
}

p.killAllByName()

log.Infof("Restarting process %s", p.Name)
if p.Path == SyncthingBin {
if err := setup.Setup(m.syncthingConfig.secret, m.syncthingConfig.config); err != nil {
p.logger.Printf("failed to setup %s: %s", p.Name, err.Error())
m.err <- fmt.Errorf("failed to setup %s: %w", p.Name, err)
return
}
}
p.start()
p.logger.Printf("%s started 2", p.Name)
case fatal:
if !p.shouldStart() {
m.err <- fmt.Errorf("%s started %d times", p.Name, p.startCount)
Expand All @@ -91,7 +127,12 @@ func (m *Monitor) checkState(p *Process, wg *sync.WaitGroup) {

if p.Path == SyncthingBin {
log.Info("Resetting syncthing database after error...")
cmd := exec.Command(SyncthingBin, "-home", "/var/syncthing", "-reset-database")
if err := setup.Setup(m.syncthingConfig.secret, m.syncthingConfig.config); err != nil {
p.logger.Errorf("failed to setup %s: %s", p.Name, err.Error())
m.err <- fmt.Errorf("failed to setup %s: %w", p.Name, err)
return
}
cmd := exec.Command(SyncthingBin, "-config", m.syncthingConfig.config, "-data", m.syncthingConfig.data, "-reset-database")
output, err := cmd.CombinedOutput()
if err != nil {
log.WithError(err).Errorf("error resetting syncthing database: %s", output)
Expand All @@ -102,6 +143,7 @@ func (m *Monitor) checkState(p *Process, wg *sync.WaitGroup) {

log.Errorf("Restarting process %s after failure", p.Name)
p.start()
p.logger.Printf("%s started 3", p.Name)
case started:
//do nothing
default:
Expand Down
2 changes: 2 additions & 0 deletions pkg/monitor/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ func (p *Process) shouldStart() bool {
}

func (p *Process) monitor() {
p.logger.Infof("monitoring process %s", p.Name)
<-p.cmd.Done()
p.logger.Infof("process %s done", p.Name)
if p.state == stopping {
return
}
Expand Down
60 changes: 60 additions & 0 deletions pkg/setup/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package setup

import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
)

func copy(src, dst string) error {
log.Default().Printf("copying %s to %s", src, dst)

fs, err := os.Stat(dst)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if err := os.MkdirAll(dst, 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
} else {
return fmt.Errorf("failed to get file info: %w", err)
}
}
if !fs.IsDir() {
return fmt.Errorf("source is not a directory: %s", dst)
}

files := []string{
"cert.pem",
"config.xml",
"key.pem",
}
for _, file := range files {
src := filepath.Join(src, file)
dst := filepath.Join(dst, file)
if _, err := os.Stat(dst); err == nil {
log.Default().Printf("file already exists: %s", dst)
continue
}
log.Default().Printf("copying %s to %s", src, dst)
err := runCommand("cp", src, dst)
if err != nil {
return fmt.Errorf("failed to copy files: %w", err)
}
}
return nil
}

func runCommand(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Default().Printf("running command: %s", cmd.String())
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to run command: %w", err)
}
log.Default().Printf("command executed correctly: %s", cmd.String())
return nil
}
28 changes: 28 additions & 0 deletions pkg/setup/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package setup

import (
"fmt"
"log"
"os"
"path/filepath"
)

func addPermissions(configPath string) error {
// Define the files to set permissions
files := []string{
filepath.Join(configPath, "cert.pem"),
filepath.Join(configPath, "config.xml"),
filepath.Join(configPath, "key.pem"),
}

// Set the permissions to 644
for _, file := range files {
log.Default().Printf("setting permissions to 644 for %s", file)
err := os.Chmod(file, 0644)
if err != nil {
return fmt.Errorf("failed to change permissions for %s: %w", file, err)
}
log.Default().Printf("permissions set to 644 for %s", file)
}
return nil
}
16 changes: 16 additions & 0 deletions pkg/setup/setup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package setup

import "log"

func Setup(secretPath, configPath string) error {
log.Default().Printf("starting setup")
if err := copy(secretPath, configPath); err != nil {
return err
}
log.Default().Printf("copy done")
if err := addPermissions(configPath); err != nil {
return err
}
log.Default().Printf("permissions done")
return nil
}
Loading