Skip to content

Commit

Permalink
Add a generic S3 provider for cloud saves
Browse files Browse the repository at this point in the history
  • Loading branch information
sergystepanov committed Nov 13, 2023
1 parent 2e91feb commit e6e537d
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 244 deletions.
29 changes: 20 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,55 @@ require (
github.com/gorilla/websocket v1.5.0
github.com/knadh/koanf/maps v0.1.1
github.com/knadh/koanf/v2 v2.0.1
github.com/minio/minio-go/v7 v7.0.63
github.com/pion/ice/v3 v3.0.2
github.com/pion/interceptor v0.1.24
github.com/pion/interceptor v0.1.25
github.com/pion/logging v0.2.2
github.com/pion/webrtc/v4 v4.0.0-beta.6
github.com/pion/webrtc/v4 v4.0.0-beta.7
github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.31.0
github.com/veandco/go-sdl2 v0.4.35
golang.org/x/crypto v0.14.0
golang.org/x/crypto v0.15.0
golang.org/x/image v0.13.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pion/datachannel v1.5.5 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/mdns v0.0.9 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.10 // indirect
github.com/pion/rtp v1.8.2 // indirect
github.com/pion/rtcp v1.2.12 // indirect
github.com/pion/rtp v1.8.3 // indirect
github.com/pion/sctp v1.8.9 // indirect
github.com/pion/sdp/v3 v3.0.6 // indirect
github.com/pion/srtp/v3 v3.0.0 // indirect
github.com/pion/srtp/v3 v3.0.1 // indirect
github.com/pion/stun/v2 v2.0.0 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pion/transport/v3 v3.0.1 // indirect
github.com/pion/turn/v3 v3.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
github.com/valyala/histogram v1.2.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
67 changes: 46 additions & 21 deletions go.sum

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions pkg/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,19 @@ recording:
# save directory
folder: ./recording

# cloud storage options
# it is mandatory to use a cloud storage when running
# a distributed multi-server configuration in order to
# share save states between nodes (resume games on a different worker)
storage:
# cloud storage provider:
# - empty (No op storage stub)
# - oracle [Oracle Object Storage](https://www.oracle.com/cloud/storage/object-storage.html)
# - s3 (S3 API compatible object storage)
provider:
# this value contains arbitrary key attribute:
# - oracle: pre-authenticated URL (see: https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingpreauthenticatedrequests.htm)
key:
s3Endpoint:
s3BucketName:
s3AccessKeyId:
s3SecretAccessKey:

webrtc:
# turn off default Pion interceptors (see: https://github.com/pion/interceptor)
Expand Down
7 changes: 5 additions & 2 deletions pkg/config/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ type WorkerConfig struct {
}

type Storage struct {
Provider string
Key string
Provider string
S3Endpoint string
S3BucketName string
S3AccessKeyId string
S3SecretAccessKey string
}

type Worker struct {
Expand Down
16 changes: 8 additions & 8 deletions pkg/worker/caged/libretro/caged.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ func (c *Caged) EnableRecording(nowait bool, user string, game string) {
}

func (c *Caged) EnableCloudStorage(uid string, storage cloud.Storage) {
if storage != nil {
wc, err := WithCloud(c.Emulator, uid, storage)
if err != nil {
c.log.Error().Err(err).Msgf("couldn't init %v", wc.HashPath())
} else {
c.log.Info().Msgf("cloud state %v has been initialized", wc.HashPath())
c.Emulator = wc
}
if storage == nil {
return
}
if wc, err := WithCloud(c.Emulator, uid, storage); err == nil {
c.Emulator = wc
c.log.Info().Msgf("cloud storage has been initialized")
} else {
c.log.Error().Err(err).Msgf("couldn't init cloud storage")
}
}

Expand Down
44 changes: 27 additions & 17 deletions pkg/worker/caged/libretro/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,37 @@ import (

type CloudFrontend struct {
Emulator
stateName string
stateLocalPath string
storage cloud.Storage // a cloud storage to store room state online
uid string
storage cloud.Storage // a cloud storage to store room state online
}

func WithCloud(fe Emulator, stateName string, storage cloud.Storage) (*CloudFrontend, error) {
r := &CloudFrontend{Emulator: fe, stateLocalPath: fe.HashPath(), stateName: stateName, storage: storage}
// WithCloud adds the ability to keep game states in the cloud storage like Amazon S3.
// It supports only one file of main save state.
func WithCloud(fe Emulator, uid string, storage cloud.Storage) (*CloudFrontend, error) {
r := &CloudFrontend{Emulator: fe, uid: uid, storage: storage}

// saveOnlineRoomToLocal save online room to local.
// !Supports only one file of main save state.
data, err := r.storage.Load(stateName)
if err != nil {
return nil, err
}
// save the data fetched from the cloud to a local directory
if data != nil {
if err := os.WriteFile(r.stateLocalPath, data, 0644); err != nil {
name := fe.SaveStateName()

if r.storage.Has(name) {
data, err := r.storage.Load(fe.SaveStateName())
if err != nil {
return nil, err
}
// save the data fetched from the cloud to a local directory
if data != nil {
if err := os.WriteFile(fe.HashPath(), data, 0644); err != nil {
return nil, err
}
}
}

return r, nil
}

// !to use emulator save/load calls instead of the storage

func (c *CloudFrontend) HasSave() bool {
_, err := c.storage.Load(c.stateName)
_, err := c.storage.Load(c.SaveStateName())
if err == nil {
return true
}
Expand All @@ -43,8 +48,13 @@ func (c *CloudFrontend) SaveGameState() error {
if err := c.Emulator.SaveGameState(); err != nil {
return err
}
if err := c.storage.Save(c.stateName, c.stateLocalPath); err != nil {
path := c.Emulator.HashPath()
data, err := os.ReadFile(path)
if err != nil {
return err
}
return nil
return c.storage.Save(c.SaveStateName(), data, map[string]string{
"uid": c.uid,
"type": "cloudretro-main-save",
})
}
2 changes: 2 additions & 0 deletions pkg/worker/caged/libretro/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Emulator interface {
// SetSessionId sets distinct name for the game session (in order to save/load it later)
SetSessionId(name string)
SaveGameState() error
SaveStateName() string
// HashPath returns the path emulator will save state to
HashPath() string
// HasSave returns true if the current ROM was saved before
Expand Down Expand Up @@ -295,6 +296,7 @@ func (f *Frontend) RestoreGameState() error { return f.Load() }
func (f *Frontend) Rotation() uint { return f.nano.Rot }
func (f *Frontend) SRAMPath() string { return f.storage.GetSRAMPath() }
func (f *Frontend) SaveGameState() error { return f.Save() }
func (f *Frontend) SaveStateName() string { return filepath.Base(f.HashPath()) }
func (f *Frontend) Scale() float64 { return f.scale }
func (f *Frontend) SetAudioCb(cb func(app.Audio)) { f.onAudio = cb }
func (f *Frontend) SetSessionId(name string) { f.storage.SetMainSaveName(name) }
Expand Down
128 changes: 0 additions & 128 deletions pkg/worker/cloud/cloudstore.go

This file was deleted.

54 changes: 0 additions & 54 deletions pkg/worker/cloud/cloudstore_test.go

This file was deleted.

Loading

0 comments on commit e6e537d

Please sign in to comment.