Skip to content
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 Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build-win: generate
GOOS=windows CGO_ENABLED=0 go build -o bin/svctl.exe ./

test: generate
go run gotest.tools/gotestsum@v1.11.0 -- -count=1 ./...
go run gotest.tools/gotestsum@v1.13.0 -- -count=1 ./...

lint:
go run github.com/golangci/golangci-lint/cmd/golangci-lint run
Expand Down
5 changes: 0 additions & 5 deletions internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,6 @@ func (s *Daemon) Start(path string) error {
return err
}

err = sv.Server().Render(false)
if err != nil {
return err
}

err = sv.Event(fsm.EventStart)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions internal/fsm/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type GameServer interface {
IsRunning() (bool, error)
Render(bool) error
Status() (*server.Status, error)
ApplyPatches() error
}

type FSM struct {
Expand Down
2 changes: 2 additions & 0 deletions internal/fsm/fsm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func (s *FSMSuite) TestStartStop() {
state := NewStateStopped()
fsm := New(gameServerMock, slog.Default(), state)

gameServerMock.EXPECT().ApplyPatches().Return(nil)
gameServerMock.EXPECT().Render(false).Return(nil)
gameServerMock.EXPECT().Start().Return(nil)

err := fsm.Event(EventStart)
Expand Down
14 changes: 14 additions & 0 deletions internal/fsm/mock_game_server.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion internal/fsm/state_restarting.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ func (s *StateRestarting) OnEnter(fsm *FSM) {
}
}

log.Info("Applying patches")
err := fsm.Server().ApplyPatches()
if err != nil {
log.Error("Failed to apply patches", "err", err)
}

log.Info("Rendering templates")
err := fsm.Server().Render(false)
err = fsm.Server().Render(false)
if err != nil {
log.Error("Failed to render templates", "err", err)
}
Expand Down
12 changes: 11 additions & 1 deletion internal/fsm/state_stopped.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ func (s *StateStopped) EventHandler(event Event, fsm *FSM) (State, error) {

switch event {
case EventStart:
err := fsm.Server().Start()
err := fsm.Server().ApplyPatches()
if err != nil {
return NewStateErrored(err), err
}

err = fsm.Server().Render(false)
if err != nil {
return NewStateErrored(err), err
}

err = fsm.Server().Start()
if err != nil {
return NewStateErrored(err), err
}
Expand Down
4 changes: 4 additions & 0 deletions internal/fsm/states_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ func (s *StatesSuite) TestStateStopped() {
state := NewStateStopped()
fsm := New(gameServerMock, slog.Default(), state)

gameServerMock.EXPECT().ApplyPatches().Return(nil)
gameServerMock.EXPECT().Render(false).Return(nil)
gameServerMock.EXPECT().Start().Return(nil)

nextState, stateErr := state.EventHandler(EventStart, fsm)
Expand Down Expand Up @@ -107,11 +109,13 @@ func (s *StatesSuite) TestStateRestarting() {

state := NewStateRestarting(NewRestartCounter(3))

gameServerMock.EXPECT().ApplyPatches().Return(nil)
gameServerMock.EXPECT().Render(false).Return(nil)
gameServerMock.EXPECT().Start().Return(nil)
fsm := New(gameServerMock, slog.Default(), state)

s.Run("Succesfull restart", func() {
gameServerMock.EXPECT().ApplyPatches().Return(nil)
gameServerMock.EXPECT().Render(false).Return(nil)
gameServerMock.EXPECT().Start().Return(nil)
state.OnEnter(fsm)
Expand Down
29 changes: 23 additions & 6 deletions internal/game/docker/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"errors"
"io"
"os"
"path"

"github.com/docker/docker/api/types/container"
Expand Down Expand Up @@ -71,23 +72,34 @@ func (c *Container) IsRunning() (bool, error) {
}

func (c *Container) WriteFile(filePath string, data []byte) error {
return c.WriteFileFromReader(filePath, bytes.NewReader(data), int64(len(data)))
}

func (c *Container) WriteFileFromReader(filePath string, reader io.Reader, size int64) error {
ctx := context.Background()

fullPath := path.Join(c.workDir, filePath)

mode := int64(0644)

stat, err := c.docker.ContainerStatPath(ctx, c.name, fullPath)
if err == nil {
mode = int64(stat.Mode)
}

buf := new(bytes.Buffer)
f, err := os.CreateTemp("", "svctl-tar-*.tar")
if err != nil {
return err
}

tarWriter := tar.NewWriter(buf)
defer func() {
f.Close()
os.Remove(f.Name())
}()

tarWriter := tar.NewWriter(f)
err = tarWriter.WriteHeader(&tar.Header{
Name: path.Base(filePath),
Size: int64(len(data)),
Size: size,
Mode: mode,
Uid: c.uid,
Gid: c.gid,
Expand All @@ -96,7 +108,7 @@ func (c *Container) WriteFile(filePath string, data []byte) error {
return err
}

_, err = tarWriter.Write(data)
_, err = io.Copy(tarWriter, reader)
if err != nil {
return err
}
Expand All @@ -106,7 +118,12 @@ func (c *Container) WriteFile(filePath string, data []byte) error {
return err
}

return c.docker.CopyToContainer(ctx, c.name, path.Dir(fullPath), buf, container.CopyToContainerOptions{})
_, err = f.Seek(0, io.SeekStart)
if err != nil {
return err
}

return c.docker.CopyToContainer(ctx, c.name, path.Dir(fullPath), f, container.CopyToContainerOptions{})
}

func (c *Container) ReadFile(filePath string) ([]byte, error) {
Expand Down
6 changes: 5 additions & 1 deletion internal/game/game.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package game

import "errors"
import (
"errors"
"io"
)

type GameServer interface {
Start() error
IsRunning() (bool, error)
Stop() error
ReadFile(path string) ([]byte, error)
WriteFile(path string, data []byte) error
WriteFileFromReader(path string, reader io.Reader, size int64) error
}

var (
Expand Down
22 changes: 22 additions & 0 deletions internal/game/local/game.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"runtime"
"strconv"
"strings"

"github.com/sboon-gg/svctl/internal/game"
)

const (
Expand All @@ -21,6 +23,8 @@ type runningProcess interface {
PID() int
}

var _ game.GameServer = &Server{}

type Server struct {
path string
process runningProcess
Expand Down Expand Up @@ -54,6 +58,24 @@ func (s *Server) WriteFile(path string, data []byte) error {
return os.WriteFile(fullPath, data, 0644)
}

func (s *Server) WriteFileFromReader(path string, r io.Reader, _ int64) error {
fullPath := filepath.Join(s.path, path)

if _, err := os.Stat(filepath.Dir(fullPath)); os.IsNotExist(err) {
// Ignore error, the write will fail if the directory doesn't exist.
_ = os.MkdirAll(filepath.Dir(fullPath), 0755)
}

f, err := os.Create(fullPath)
if err != nil {
return err
}
defer f.Close()

_, err = io.Copy(f, r)
return err
}

func (s *Server) Update(ctx context.Context, outW io.Writer, inR io.Reader, errW io.Writer) error {
return s.update(ctx, outW, inR, errW)
}
Expand Down
32 changes: 32 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
"os"
"path/filepath"

"github.com/docker/docker/client"
Expand Down Expand Up @@ -96,3 +97,34 @@ func (s *Server) DryRender() ([]templates.RenderOutput, error) {

return s.Settings.Templates.Render(gameConfig, values)
}

func (s *Server) ApplyPatches() error {
cfg, err := s.Settings.Config()
if err != nil {
return err
}

for _, patch := range cfg.Patches {
if patch.Source == "" || patch.Destination == "" {
continue
}

f, err := os.Open(filepath.Join(s.Settings.Path, patch.Source))
if err != nil {
return err
}
defer f.Close()

stat, err := f.Stat()
if err != nil {
return err
}

err = s.WriteFileFromReader(patch.Destination, f, stat.Size())
if err != nil {
return err
}
}

return nil
}
8 changes: 7 additions & 1 deletion internal/settings/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@ type DockerConfig struct {
ContainerName string `yaml:"containerName"`
}

type PatchConfig struct {
Source string `yaml:"source"`
Destination string `yaml:"destination"`
}

type Config struct {
Values []ValuesSource `yaml:"values"`
Loggers []LoggerConfig `yaml:"loggers"`
TemplatesPath string `yaml:"templates"`
Patches []PatchConfig `yaml:"patches"`
Game GameConfig `yaml:"game"`
Docker *DockerConfig `yaml:"docker,omtempty"`
}

func (s *Settings) Config() (*Config, error) {
var config Config

content, err := os.ReadFile(filepath.Join(s.path, configFileName))
content, err := os.ReadFile(filepath.Join(s.Path, configFileName))
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const (
)

type Settings struct {
path string
Path string
Templates *templates.Renderer
Log *slog.Logger
}

func Open(path string) (*Settings, error) {
s := &Settings{
path: path,
Path: path,
}

config, err := s.Config()
Expand Down
2 changes: 1 addition & 1 deletion internal/settings/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (s *Settings) TemplateData() (templates.Values, *GameConfig, error) {
if source.File != "" {
sourceFile := source.File
if !filepath.IsAbs(sourceFile) {
sourceFile = filepath.Join(s.path, sourceFile)
sourceFile = filepath.Join(s.Path, sourceFile)
}

content, err := os.ReadFile(sourceFile)
Expand Down
7 changes: 3 additions & 4 deletions pkg/templates/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"maps"
"os"
"text/template"

Expand All @@ -21,14 +22,12 @@ func (r *Renderer) FuncMap() template.FuncMap {
"maplist": r.maplist,
}

for k, v := range extra {
f[k] = v
}
maps.Copy(f, extra)

return f
}

func (t *Renderer) maplist(filterMap interface{}, rawMaplist string) (string, error) {
func (t *Renderer) maplist(filterMap any, rawMaplist string) (string, error) {
var filter maplist.MapInfo
if f, ok := filterMap.(string); ok {
filter = maplist.Parse(fmt.Sprintf("%s %s", maplist.MaplistAppendStr, f))[0]
Expand Down
4 changes: 2 additions & 2 deletions pkg/templates/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ mapList.append sahel gpm_coop 64
"test": "changed-string",
"quoted": "but different",
"overriddenByZeroValue": 0,
"maps": []interface{}{
map[string]interface{}{
"maps": []any{
map[string]any{
"name": "saaremaa",
},
},
Expand Down