mirror of
https://github.com/m1k1o/neko-rooms.git
synced 2026-11-02 22:51:34 -06:00
extract pull logic to own manager. (#54)
This commit is contained in:
+4
-2
@@ -12,13 +12,15 @@ import (
|
|||||||
type ApiManagerCtx struct {
|
type ApiManagerCtx struct {
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
rooms types.RoomManager
|
rooms types.RoomManager
|
||||||
|
pull types.PullManager
|
||||||
conf *config.API
|
conf *config.API
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(roomManager types.RoomManager, conf *config.API) *ApiManagerCtx {
|
func New(rooms types.RoomManager, pull types.PullManager, conf *config.API) *ApiManagerCtx {
|
||||||
return &ApiManagerCtx{
|
return &ApiManagerCtx{
|
||||||
logger: log.With().Str("module", "api").Logger(),
|
logger: log.With().Str("module", "api").Logger(),
|
||||||
rooms: roomManager,
|
rooms: rooms,
|
||||||
|
pull: pull,
|
||||||
conf: conf,
|
conf: conf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,25 +15,25 @@ func (manager *ApiManagerCtx) pullStart(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := manager.rooms.PullStart(request)
|
err := manager.pull.Start(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response := manager.rooms.PullStatus()
|
response := manager.pull.Status()
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *ApiManagerCtx) pullStatus(w http.ResponseWriter, r *http.Request) {
|
func (manager *ApiManagerCtx) pullStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
response := manager.rooms.PullStatus()
|
response := manager.pull.Status()
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *ApiManagerCtx) pullStop(w http.ResponseWriter, r *http.Request) {
|
func (manager *ApiManagerCtx) pullStop(w http.ResponseWriter, r *http.Request) {
|
||||||
err := manager.rooms.PullStop()
|
err := manager.pull.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package room
|
package pull
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -10,76 +10,73 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerTypes "github.com/docker/docker/api/types"
|
dockerTypes "github.com/docker/docker/api/types"
|
||||||
|
dockerClient "github.com/docker/docker/client"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"github.com/m1k1o/neko-rooms/internal/types"
|
"github.com/m1k1o/neko-rooms/internal/types"
|
||||||
"github.com/m1k1o/neko-rooms/internal/utils"
|
"github.com/m1k1o/neko-rooms/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PullConfig struct {
|
type PullManagerCtx struct {
|
||||||
|
logger zerolog.Logger
|
||||||
|
client *dockerClient.Client
|
||||||
|
images []string
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
cancel func()
|
cancel func()
|
||||||
status types.PullStatus
|
status types.PullStatus
|
||||||
layers map[string]int
|
layers map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pull *PullConfig) TryInitialize(cancel func()) bool {
|
func New(client *dockerClient.Client, nekoImages []string) *PullManagerCtx {
|
||||||
pull.mu.Lock()
|
return &PullManagerCtx{
|
||||||
defer pull.mu.Unlock()
|
logger: log.With().Str("module", "pull").Logger(),
|
||||||
|
client: client,
|
||||||
|
images: nekoImages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if pull.status.Active {
|
func (manager *PullManagerCtx) tryInitialize(cancel func()) bool {
|
||||||
|
manager.mu.Lock()
|
||||||
|
defer manager.mu.Unlock()
|
||||||
|
|
||||||
|
if manager.status.Active {
|
||||||
cancel()
|
cancel()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
pull.cancel = cancel
|
manager.cancel = cancel
|
||||||
|
|
||||||
pull.status = types.PullStatus{
|
manager.status = types.PullStatus{
|
||||||
Active: true,
|
Active: true,
|
||||||
Started: &now,
|
Started: &now,
|
||||||
Layers: []types.PullLayer{},
|
Layers: []types.PullLayer{},
|
||||||
Status: []string{},
|
Status: []string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
pull.layers = map[string]int{}
|
manager.layers = map[string]int{}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pull *PullConfig) SetDone() {
|
func (manager *PullManagerCtx) setDone() {
|
||||||
pull.mu.Lock()
|
manager.mu.Lock()
|
||||||
defer pull.mu.Unlock()
|
defer manager.mu.Unlock()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
pull.status.Active = false
|
manager.status.Active = false
|
||||||
pull.status.Finished = &now
|
manager.status.Finished = &now
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pull *PullConfig) Stop() bool {
|
func (manager *PullManagerCtx) Start(request types.PullStart) error {
|
||||||
pull.mu.Lock()
|
if in, _ := utils.ArrayIn(request.NekoImage, manager.images); !in {
|
||||||
defer pull.mu.Unlock()
|
|
||||||
|
|
||||||
if pull.status.Active {
|
|
||||||
pull.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
return pull.status.Active
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pull *PullConfig) Get() types.PullStatus {
|
|
||||||
pull.mu.Lock()
|
|
||||||
defer pull.mu.Unlock()
|
|
||||||
|
|
||||||
return pull.status
|
|
||||||
}
|
|
||||||
|
|
||||||
func (manager *RoomManagerCtx) PullStart(request types.PullStart) error {
|
|
||||||
if in, _ := utils.ArrayIn(request.NekoImage, manager.config.NekoImages); !in {
|
|
||||||
return fmt.Errorf("unknown neko image")
|
return fmt.Errorf("unknown neko image")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
if !manager.pull.TryInitialize(cancel) {
|
if !manager.tryInitialize(cancel) {
|
||||||
return fmt.Errorf("pull is already in progess")
|
return fmt.Errorf("pull is already in progess")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +90,7 @@ func (manager *RoomManagerCtx) PullStart(request types.PullStart) error {
|
|||||||
|
|
||||||
encodedJSON, err := json.Marshal(authConfig)
|
encodedJSON, err := json.Marshal(authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = dockerTypes.ImagePullOptions{
|
opts = dockerTypes.ImagePullOptions{
|
||||||
@@ -104,7 +101,7 @@ func (manager *RoomManagerCtx) PullStart(request types.PullStart) error {
|
|||||||
reader, err := manager.client.ImagePull(ctx, request.NekoImage, opts)
|
reader, err := manager.client.ImagePull(ctx, request.NekoImage, opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
manager.pull.SetDone()
|
manager.setDone()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,52 +112,59 @@ func (manager *RoomManagerCtx) PullStart(request types.PullStart) error {
|
|||||||
|
|
||||||
layer := types.PullLayer{}
|
layer := types.PullLayer{}
|
||||||
if err := json.Unmarshal(data, &layer); err != nil {
|
if err := json.Unmarshal(data, &layer); err != nil {
|
||||||
manager.pull.status.Status = append(
|
manager.status.Status = append(
|
||||||
manager.pull.status.Status,
|
manager.status.Status,
|
||||||
fmt.Sprintf("Error while parsing pull response: %s", err),
|
fmt.Sprintf("Error while parsing pull response: %s", err),
|
||||||
)
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if layer.ProgressDetail != nil {
|
if layer.ProgressDetail != nil {
|
||||||
// map layer id to slice index
|
// map layer id to slice index
|
||||||
if index, ok := manager.pull.layers[layer.ID]; ok {
|
if index, ok := manager.layers[layer.ID]; ok {
|
||||||
manager.pull.status.Layers[index] = layer
|
manager.status.Layers[index] = layer
|
||||||
} else {
|
} else {
|
||||||
manager.pull.layers[layer.ID] = len(manager.pull.layers)
|
manager.layers[layer.ID] = len(manager.layers)
|
||||||
manager.pull.status.Layers = append(manager.pull.status.Layers, layer)
|
manager.status.Layers = append(manager.status.Layers, layer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
manager.pull.status.Status = append(
|
manager.status.Status = append(
|
||||||
manager.pull.status.Status,
|
manager.status.Status,
|
||||||
layer.Status,
|
layer.Status,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
manager.pull.status.Status = append(
|
manager.status.Status = append(
|
||||||
manager.pull.status.Status,
|
manager.status.Status,
|
||||||
fmt.Sprintf("Error while reading pull response: %s", err),
|
fmt.Sprintf("Error while reading pull response: %s", err),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Close()
|
reader.Close()
|
||||||
manager.pull.SetDone()
|
manager.setDone()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *RoomManagerCtx) PullStop() error {
|
func (manager *PullManagerCtx) Stop() error {
|
||||||
if !manager.pull.Stop() {
|
manager.mu.Lock()
|
||||||
|
defer manager.mu.Unlock()
|
||||||
|
|
||||||
|
if !manager.status.Active {
|
||||||
return fmt.Errorf("pull is not in progess")
|
return fmt.Errorf("pull is not in progess")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
manager.cancel()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *RoomManagerCtx) PullStatus() types.PullStatus {
|
func (manager *PullManagerCtx) Status() types.PullStatus {
|
||||||
return manager.pull.Get()
|
manager.mu.Lock()
|
||||||
|
defer manager.mu.Unlock()
|
||||||
|
|
||||||
|
return manager.status
|
||||||
}
|
}
|
||||||
@@ -34,20 +34,13 @@ const (
|
|||||||
privateStorageGid = 1000
|
privateStorageGid = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(config *config.Room) *RoomManagerCtx {
|
func New(client *dockerClient.Client, config *config.Room) *RoomManagerCtx {
|
||||||
logger := log.With().Str("module", "room").Logger()
|
logger := log.With().Str("module", "room").Logger()
|
||||||
|
|
||||||
cli, err := dockerClient.NewClientWithOpts(dockerClient.FromEnv)
|
|
||||||
if err != nil {
|
|
||||||
logger.Panic().Err(err).Msg("unable to connect to docker client")
|
|
||||||
} else {
|
|
||||||
logger.Info().Msg("successfully connected to docker client")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &RoomManagerCtx{
|
return &RoomManagerCtx{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
config: config,
|
config: config,
|
||||||
client: cli,
|
client: client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +48,6 @@ type RoomManagerCtx struct {
|
|||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
config *config.Room
|
config *config.Room
|
||||||
client *dockerClient.Client
|
client *dockerClient.Client
|
||||||
pull PullConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager *RoomManagerCtx) Config() types.RoomsConfig {
|
func (manager *RoomManagerCtx) Config() types.RoomsConfig {
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type PullStart struct {
|
||||||
|
NekoImage string `json:"neko_image"`
|
||||||
|
RegistryUser string `json:"registry_user"`
|
||||||
|
RegistryPass string `json:"registry_pass"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PullLayer struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
ProgressDetail *struct {
|
||||||
|
Current int `json:"current"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
} `json:"progressDetail"`
|
||||||
|
Progress string `json:"progress"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PullStatus struct {
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Started *time.Time `json:"started"`
|
||||||
|
Layers []PullLayer `json:"layers"`
|
||||||
|
Status []string `json:"status"`
|
||||||
|
Finished *time.Time `json:"finished"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PullManager interface {
|
||||||
|
Start(request PullStart) error
|
||||||
|
Stop() error
|
||||||
|
Status() PullStatus
|
||||||
|
}
|
||||||
@@ -246,30 +246,6 @@ type RoomMember struct {
|
|||||||
Muted bool `json:"muted"`
|
Muted bool `json:"muted"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PullStart struct {
|
|
||||||
NekoImage string `json:"neko_image"`
|
|
||||||
RegistryUser string `json:"registry_user"`
|
|
||||||
RegistryPass string `json:"registry_pass"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PullLayer struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
ProgressDetail *struct {
|
|
||||||
Current int `json:"current"`
|
|
||||||
Total int `json:"total"`
|
|
||||||
} `json:"progressDetail"`
|
|
||||||
Progress string `json:"progress"`
|
|
||||||
ID string `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PullStatus struct {
|
|
||||||
Active bool `json:"active"`
|
|
||||||
Started *time.Time `json:"started"`
|
|
||||||
Layers []PullLayer `json:"layers"`
|
|
||||||
Status []string `json:"status"`
|
|
||||||
Finished *time.Time `json:"finished"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoomManager interface {
|
type RoomManager interface {
|
||||||
Config() RoomsConfig
|
Config() RoomsConfig
|
||||||
List() ([]RoomEntry, error)
|
List() ([]RoomEntry, error)
|
||||||
@@ -284,8 +260,4 @@ type RoomManager interface {
|
|||||||
Start(id string) error
|
Start(id string) error
|
||||||
Stop(id string) error
|
Stop(id string) error
|
||||||
Restart(id string) error
|
Restart(id string) error
|
||||||
|
|
||||||
PullStart(request PullStart) error
|
|
||||||
PullStop() error
|
|
||||||
PullStatus() PullStatus
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/m1k1o/neko-rooms/internal/api"
|
"github.com/m1k1o/neko-rooms/internal/api"
|
||||||
"github.com/m1k1o/neko-rooms/internal/config"
|
"github.com/m1k1o/neko-rooms/internal/config"
|
||||||
"github.com/m1k1o/neko-rooms/internal/http"
|
"github.com/m1k1o/neko-rooms/internal/http"
|
||||||
|
"github.com/m1k1o/neko-rooms/internal/pull"
|
||||||
"github.com/m1k1o/neko-rooms/internal/room"
|
"github.com/m1k1o/neko-rooms/internal/room"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ type MainCtx struct {
|
|||||||
|
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
roomManager *room.RoomManagerCtx
|
roomManager *room.RoomManagerCtx
|
||||||
|
pullManager *pull.PullManagerCtx
|
||||||
apiManager *api.ApiManagerCtx
|
apiManager *api.ApiManagerCtx
|
||||||
httpManager *http.HttpManagerCtx
|
httpManager *http.HttpManagerCtx
|
||||||
}
|
}
|
||||||
@@ -117,12 +120,26 @@ func (main *MainCtx) Preflight() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (main *MainCtx) Start() {
|
func (main *MainCtx) Start() {
|
||||||
|
client, err := client.NewClientWithOpts(client.FromEnv)
|
||||||
|
if err != nil {
|
||||||
|
main.logger.Panic().Err(err).Msg("unable to connect to docker client")
|
||||||
|
} else {
|
||||||
|
main.logger.Info().Msg("successfully connected to docker client")
|
||||||
|
}
|
||||||
|
|
||||||
main.roomManager = room.New(
|
main.roomManager = room.New(
|
||||||
|
client,
|
||||||
main.Configs.Room,
|
main.Configs.Room,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
main.pullManager = pull.New(
|
||||||
|
client,
|
||||||
|
main.Configs.Room.NekoImages,
|
||||||
|
)
|
||||||
|
|
||||||
main.apiManager = api.New(
|
main.apiManager = api.New(
|
||||||
main.roomManager,
|
main.roomManager,
|
||||||
|
main.pullManager,
|
||||||
main.Configs.API,
|
main.Configs.API,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,11 +152,8 @@ func (main *MainCtx) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (main *MainCtx) Shutdown() {
|
func (main *MainCtx) Shutdown() {
|
||||||
if err := main.httpManager.Shutdown(); err != nil {
|
err := main.httpManager.Shutdown()
|
||||||
main.logger.Err(err).Msg("http manager shutdown with an error")
|
main.logger.Err(err).Msg("http manager shutdown")
|
||||||
} else {
|
|
||||||
main.logger.Debug().Msg("http manager shutdown")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (main *MainCtx) ServeCommand(cmd *cobra.Command, args []string) {
|
func (main *MainCtx) ServeCommand(cmd *cobra.Command, args []string) {
|
||||||
@@ -151,7 +165,7 @@ func (main *MainCtx) ServeCommand(cmd *cobra.Command, args []string) {
|
|||||||
signal.Notify(quit, os.Interrupt)
|
signal.Notify(quit, os.Interrupt)
|
||||||
sig := <-quit
|
sig := <-quit
|
||||||
|
|
||||||
main.logger.Warn().Msgf("received %s, attempting graceful shutdown: \n", sig)
|
main.logger.Warn().Msgf("received %s, attempting graceful shutdown.", sig)
|
||||||
main.Shutdown()
|
main.Shutdown()
|
||||||
main.logger.Info().Msg("shutdown complete")
|
main.logger.Info().Msg("shutdown complete")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user