mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 09:05:30 +00:00
Handled graceful restart of server
This commit is contained in:
264
app/kgpz.go
Normal file
264
app/kgpz.go
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
|
||||||
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AGENTS_PATH = "XML/akteure.xml"
|
||||||
|
PLACES_PATH = "XML/orte.xml"
|
||||||
|
WORKS_PATH = "XML/werke.xml"
|
||||||
|
CATEGORIES_PATH = "XML/kategorien.xml"
|
||||||
|
|
||||||
|
ISSUES_DIR = "XML/stuecke/"
|
||||||
|
PIECES_DIR = "XML/beitraege/"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Library struct {
|
||||||
|
Agents *providers.AgentProvider
|
||||||
|
Places *providers.PlaceProvider
|
||||||
|
Works *providers.WorkProvider
|
||||||
|
Categories *providers.CategoryProvider
|
||||||
|
Issues *providers.IssueProvider
|
||||||
|
Pieces *providers.PieceProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
type KGPZ struct {
|
||||||
|
lmu sync.Mutex
|
||||||
|
Config *providers.ConfigProvider
|
||||||
|
Repo *providers.GitProvider
|
||||||
|
Library
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewKGPZ(config *providers.ConfigProvider) *KGPZ {
|
||||||
|
if config == nil {
|
||||||
|
panic("ConfigProvider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.Validate(); err != nil {
|
||||||
|
helpers.MaybePanic(err, "Error validating config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &KGPZ{Config: config}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) IsDebug() bool {
|
||||||
|
return k.Config.Debug
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) Pull() {
|
||||||
|
// TODO: what happens if the application quits mid-pull?
|
||||||
|
// We need to make sure to exit gracefully
|
||||||
|
go func(k *KGPZ) {
|
||||||
|
if k.Repo == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err, changed := k.Repo.Pull()
|
||||||
|
if err != nil {
|
||||||
|
helpers.LogOnErr(&k.Repo, err, "Error pulling repo")
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
if k.IsDebug() {
|
||||||
|
helpers.LogOnDebug(&k.Repo, "GitProvider changed")
|
||||||
|
}
|
||||||
|
// Locking is handled in Serialize()
|
||||||
|
k.Serialize()
|
||||||
|
}
|
||||||
|
}(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitRepo() {
|
||||||
|
gp, err := providers.NewGitProvider(k.Config.Config.GitURL, k.Config.Config.FolderPath, k.Config.Config.GitBranch)
|
||||||
|
if err != nil {
|
||||||
|
helpers.LogOnErr(&gp, err, "Error creating GitProvider")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("InitRepo")
|
||||||
|
k.Repo = gp
|
||||||
|
k.Pull()
|
||||||
|
|
||||||
|
if k.IsDebug() {
|
||||||
|
helpers.LogOnDebug(&gp, "GitProvider")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This panics if the data cant be read, and there is no data read
|
||||||
|
func (k *KGPZ) Serialize() {
|
||||||
|
// TODO: maybe dont panic.
|
||||||
|
// We need to check the requirements only when starting the server
|
||||||
|
// We can serve an error page
|
||||||
|
new := Library{}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(6)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Agents = k.InitAgents()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Places = k.InitPlaces()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Works = k.InitWorks()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Categories = k.InitCategories()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Issues = k.InitIssues()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
new.Pieces = k.InitPieces()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
k.lmu.Lock()
|
||||||
|
defer k.lmu.Unlock()
|
||||||
|
k.Library = new
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: on error, we need to log the error, and use stale data to recover gracefully
|
||||||
|
// If Repo != nil we can try the last commit; if k != nil we can try the last data
|
||||||
|
func (k *KGPZ) InitAgents() *providers.AgentProvider {
|
||||||
|
ap := providers.NewAgentProvider([]string{filepath.Join(k.Config.FolderPath, AGENTS_PATH)})
|
||||||
|
if err := ap.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&ap, err, "Error loading agents")
|
||||||
|
k.lmu.Lock()
|
||||||
|
ap.Items = k.Agents.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
// TODO: mark as stale
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&ap, "AgentProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitPlaces() *providers.PlaceProvider {
|
||||||
|
pp := providers.NewPlaceProvider([]string{filepath.Join(k.Config.FolderPath, PLACES_PATH)})
|
||||||
|
if err := pp.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&pp, err, "Error loading places")
|
||||||
|
k.lmu.Lock()
|
||||||
|
pp.Items = k.Places.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
// TODO: mark as stale
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&pp, "PlaceProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitWorks() *providers.WorkProvider {
|
||||||
|
wp := providers.NewWorkProvider([]string{filepath.Join(k.Config.FolderPath, WORKS_PATH)})
|
||||||
|
if err := wp.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&wp, err, "Error loading works")
|
||||||
|
k.lmu.Lock()
|
||||||
|
wp.Items = k.Works.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
// TODO: mark as stale
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&wp, "WorkProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return wp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitCategories() *providers.CategoryProvider {
|
||||||
|
cp := providers.NewCategoryProvider([]string{filepath.Join(k.Config.FolderPath, CATEGORIES_PATH)})
|
||||||
|
if err := cp.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&cp, err, "Error loading categories")
|
||||||
|
k.lmu.Lock()
|
||||||
|
cp.Items = k.Categories.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&cp, "CategoryProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitIssues() *providers.IssueProvider {
|
||||||
|
files, err := getXMLFiles(filepath.Join(k.Config.FolderPath, ISSUES_DIR))
|
||||||
|
|
||||||
|
helpers.MaybePanic(err, "Error getting issues files")
|
||||||
|
|
||||||
|
cp := providers.NewIssueProvider(*files)
|
||||||
|
if err := cp.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&cp, err, "Error loading issues")
|
||||||
|
k.lmu.Lock()
|
||||||
|
cp.Items = k.Issues.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
// TODO: mark as stale
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&cp, "IssueProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) InitPieces() *providers.PieceProvider {
|
||||||
|
files, err := getXMLFiles(filepath.Join(k.Config.FolderPath, PIECES_DIR))
|
||||||
|
|
||||||
|
helpers.MaybePanic(err, "Error getting pieces files")
|
||||||
|
|
||||||
|
cp := providers.NewPieceProvider(*files)
|
||||||
|
if err := cp.Load(); err != nil {
|
||||||
|
helpers.LogOnErr(&cp, err, "Error loading pieces")
|
||||||
|
k.lmu.Lock()
|
||||||
|
cp.Items = k.Pieces.Items
|
||||||
|
k.lmu.Unlock()
|
||||||
|
// TODO: mark as stale
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Config.LogData {
|
||||||
|
helpers.LogOnDebug(&cp, "PieceProvider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KGPZ) Shutdown() {
|
||||||
|
k.Repo.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getXMLFiles(path string) (*[]string, error) {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, err := filepath.Glob(filepath.Join(path, "*.xml"))
|
||||||
|
|
||||||
|
return &matches, err
|
||||||
|
}
|
||||||
281
kgpz_web.go
281
kgpz_web.go
@@ -4,12 +4,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
||||||
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
|
||||||
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
||||||
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 1. Check if folder exists
|
// 1. Check if folder exists
|
||||||
@@ -23,273 +23,25 @@ import (
|
|||||||
// - setup commit date & hash
|
// - setup commit date & hash
|
||||||
// - Setup GitHub webhook if set
|
// - Setup GitHub webhook if set
|
||||||
|
|
||||||
const (
|
|
||||||
AGENTS_PATH = "XML/akteure.xml"
|
|
||||||
PLACES_PATH = "XML/orte.xml"
|
|
||||||
WORKS_PATH = "XML/werke.xml"
|
|
||||||
CATEGORIES_PATH = "XML/kategorien.xml"
|
|
||||||
|
|
||||||
ISSUES_DIR = "XML/stuecke/"
|
|
||||||
PIECES_DIR = "XML/beitraege/"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Library struct {
|
|
||||||
Agents *providers.AgentProvider
|
|
||||||
Places *providers.PlaceProvider
|
|
||||||
Works *providers.WorkProvider
|
|
||||||
Categories *providers.CategoryProvider
|
|
||||||
Issues *providers.IssueProvider
|
|
||||||
Pieces *providers.PieceProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
type KGPZ struct {
|
|
||||||
lmu sync.Mutex
|
|
||||||
Config *providers.ConfigProvider
|
|
||||||
Repo *providers.GitProvider
|
|
||||||
Library
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewKGPZ(config *providers.ConfigProvider) *KGPZ {
|
|
||||||
if config == nil {
|
|
||||||
panic("ConfigProvider is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := config.Validate(); err != nil {
|
|
||||||
helpers.MaybePanic(err, "Error validating config")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &KGPZ{Config: config}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) IsDebug() bool {
|
|
||||||
return k.Config.Debug
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) Pull() {
|
|
||||||
// TODO: what happens if the application quits mid-pull?
|
|
||||||
// We need to make sure to exit gracefully
|
|
||||||
go func(k *KGPZ) {
|
|
||||||
if k.Repo == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err, changed := k.Repo.Pull()
|
|
||||||
if err != nil {
|
|
||||||
helpers.LogOnErr(&k.Repo, err, "Error pulling repo")
|
|
||||||
}
|
|
||||||
|
|
||||||
if changed {
|
|
||||||
if k.IsDebug() {
|
|
||||||
helpers.LogOnDebug(&k.Repo, "GitProvider changed")
|
|
||||||
}
|
|
||||||
// Locking is handled in Serialize()
|
|
||||||
k.Serialize()
|
|
||||||
}
|
|
||||||
}(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitRepo() {
|
|
||||||
gp, err := providers.NewGitProvider(k.Config.Config.GitURL, k.Config.Config.FolderPath, k.Config.Config.GitBranch)
|
|
||||||
if err != nil {
|
|
||||||
helpers.LogOnErr(&gp, err, "Error creating GitProvider")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("InitRepo")
|
|
||||||
k.Repo = gp
|
|
||||||
k.Pull()
|
|
||||||
|
|
||||||
if k.IsDebug() {
|
|
||||||
helpers.LogOnDebug(&gp, "GitProvider")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This panics if the data cant be read, and there is no data read
|
|
||||||
func (k *KGPZ) Serialize() {
|
|
||||||
// TODO: maybe dont panic.
|
|
||||||
// We need to check the requirements only when starting the server
|
|
||||||
// We can serve an error page
|
|
||||||
new := Library{}
|
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
wg.Add(6)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Agents = k.InitAgents()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Places = k.InitPlaces()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Works = k.InitWorks()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Categories = k.InitCategories()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Issues = k.InitIssues()
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
new.Pieces = k.InitPieces()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
k.lmu.Lock()
|
|
||||||
defer k.lmu.Unlock()
|
|
||||||
k.Library = new
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: on error, we need to log the error, and use stale data to recover gracefully
|
|
||||||
// If Repo != nil we can try the last commit; if k != nil we can try the last data
|
|
||||||
func (k *KGPZ) InitAgents() *providers.AgentProvider {
|
|
||||||
ap := providers.NewAgentProvider([]string{filepath.Join(k.Config.FolderPath, AGENTS_PATH)})
|
|
||||||
if err := ap.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&ap, err, "Error loading agents")
|
|
||||||
k.lmu.Lock()
|
|
||||||
ap.Items = k.Agents.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
// TODO: mark as stale
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&ap, "AgentProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return ap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitPlaces() *providers.PlaceProvider {
|
|
||||||
pp := providers.NewPlaceProvider([]string{filepath.Join(k.Config.FolderPath, PLACES_PATH)})
|
|
||||||
if err := pp.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&pp, err, "Error loading places")
|
|
||||||
k.lmu.Lock()
|
|
||||||
pp.Items = k.Places.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
// TODO: mark as stale
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&pp, "PlaceProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return pp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitWorks() *providers.WorkProvider {
|
|
||||||
wp := providers.NewWorkProvider([]string{filepath.Join(k.Config.FolderPath, WORKS_PATH)})
|
|
||||||
if err := wp.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&wp, err, "Error loading works")
|
|
||||||
k.lmu.Lock()
|
|
||||||
wp.Items = k.Works.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
// TODO: mark as stale
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&wp, "WorkProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return wp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitCategories() *providers.CategoryProvider {
|
|
||||||
cp := providers.NewCategoryProvider([]string{filepath.Join(k.Config.FolderPath, CATEGORIES_PATH)})
|
|
||||||
if err := cp.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&cp, err, "Error loading categories")
|
|
||||||
k.lmu.Lock()
|
|
||||||
cp.Items = k.Categories.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&cp, "CategoryProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitIssues() *providers.IssueProvider {
|
|
||||||
files, err := getXMLFiles(filepath.Join(k.Config.FolderPath, ISSUES_DIR))
|
|
||||||
|
|
||||||
helpers.MaybePanic(err, "Error getting issues files")
|
|
||||||
|
|
||||||
cp := providers.NewIssueProvider(*files)
|
|
||||||
if err := cp.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&cp, err, "Error loading issues")
|
|
||||||
k.lmu.Lock()
|
|
||||||
cp.Items = k.Issues.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
// TODO: mark as stale
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&cp, "IssueProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) InitPieces() *providers.PieceProvider {
|
|
||||||
files, err := getXMLFiles(filepath.Join(k.Config.FolderPath, PIECES_DIR))
|
|
||||||
|
|
||||||
helpers.MaybePanic(err, "Error getting pieces files")
|
|
||||||
|
|
||||||
cp := providers.NewPieceProvider(*files)
|
|
||||||
if err := cp.Load(); err != nil {
|
|
||||||
helpers.LogOnErr(&cp, err, "Error loading pieces")
|
|
||||||
k.lmu.Lock()
|
|
||||||
cp.Items = k.Pieces.Items
|
|
||||||
k.lmu.Unlock()
|
|
||||||
// TODO: mark as stale
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Config.LogData {
|
|
||||||
helpers.LogOnDebug(&cp, "PieceProvider")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KGPZ) Shutdown() {
|
|
||||||
k.Repo.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getXMLFiles(path string) (*[]string, error) {
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
matches, err := filepath.Glob(filepath.Join(path, "*.xml"))
|
|
||||||
|
|
||||||
return &matches, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cfg := providers.NewConfigProvider([]string{"config.dev.json", "config.json"})
|
cfg := providers.NewConfigProvider([]string{"config.dev.json", "config.json"})
|
||||||
if err := cfg.Read(); err != nil {
|
if err := cfg.Read(); err != nil {
|
||||||
helpers.MaybePanic(err, "Error reading config")
|
helpers.MaybePanic(err, "Error reading config")
|
||||||
}
|
}
|
||||||
|
|
||||||
kgpz := NewKGPZ(cfg)
|
kgpz := app.NewKGPZ(cfg)
|
||||||
kgpz.InitRepo()
|
Bootstrap(kgpz)
|
||||||
kgpz.Serialize()
|
|
||||||
|
|
||||||
EnsureCleanup(kgpz)
|
server := server.Start(kgpz)
|
||||||
|
Start(kgpz, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnsureCleanup(k *KGPZ) {
|
func Bootstrap(k *app.KGPZ) {
|
||||||
|
k.InitRepo()
|
||||||
|
k.Serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(k *app.KGPZ, s *server.Server) {
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
done := make(chan bool, 1)
|
done := make(chan bool, 1)
|
||||||
|
|
||||||
@@ -299,10 +51,19 @@ func EnsureCleanup(k *KGPZ) {
|
|||||||
_ = <-sigs
|
_ = <-sigs
|
||||||
fmt.Println("Received signal. Cleaning up.")
|
fmt.Println("Received signal. Cleaning up.")
|
||||||
// INFO: here we add cleanup functions
|
// INFO: here we add cleanup functions
|
||||||
|
s.Kill()
|
||||||
k.Shutdown()
|
k.Shutdown()
|
||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// INFO: if the server fails to start this exits the program
|
||||||
|
// We handle graceful server recovery in the server itself.
|
||||||
|
go func() {
|
||||||
|
s.Start()
|
||||||
|
<-s.Killed
|
||||||
|
fmt.Println("Server exited. Cleaning up.")
|
||||||
|
}()
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
fmt.Println("Cleanup finished. Exiting.")
|
fmt.Println("Cleanup finished. Exiting.")
|
||||||
}
|
}
|
||||||
|
|||||||
106
server/server.go
Normal file
106
server/server.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"githib.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
// INFO: Server is a meta-package that handles the current router, which it starts in a goroutine.
|
||||||
|
// The router must be able to restart itself, if the data validation fails, so we subscribe to a channel on the app,
|
||||||
|
// which indicates that the data has changed
|
||||||
|
// On data change:
|
||||||
|
// - we invalidate all caches if data is valid
|
||||||
|
// - we reload all clients
|
||||||
|
// - if data validity catastrophically fails, we restart the router to map error pages.
|
||||||
|
type Server struct {
|
||||||
|
running chan bool
|
||||||
|
alive chan bool
|
||||||
|
Killed chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(k *app.KGPZ) *Server {
|
||||||
|
return &Server{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start() {
|
||||||
|
srv := &http.Server{Addr: ":8080"}
|
||||||
|
s.running = make(chan bool, 1)
|
||||||
|
s.alive = make(chan bool, 1)
|
||||||
|
s.Killed = s.killHandler(srv, s.alive)
|
||||||
|
shuttingdown := s.shutdownHandler(srv, s.running)
|
||||||
|
shutdown := s.runnerHandler(srv, shuttingdown)
|
||||||
|
s.restartHandler(shutdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Restart() {
|
||||||
|
s.running <- false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Kill() {
|
||||||
|
s.alive <- false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) killHandler(srv *http.Server, alive chan bool) chan bool {
|
||||||
|
kill := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-alive
|
||||||
|
|
||||||
|
if err := srv.Shutdown(nil); err != nil {
|
||||||
|
fmt.Println("Error shutting down server")
|
||||||
|
}
|
||||||
|
|
||||||
|
kill <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
return kill
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) restartHandler(shutdown chan bool) {
|
||||||
|
go func() {
|
||||||
|
<-shutdown
|
||||||
|
s.Start()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) runnerHandler(srv *http.Server, shuttingdown chan bool) chan bool {
|
||||||
|
shutdown := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// EXAMPLE:
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
io.WriteString(w, "hello world\n")
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
fmt.Println("Error starting server")
|
||||||
|
}
|
||||||
|
|
||||||
|
<-shuttingdown
|
||||||
|
shutdown <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
return shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) shutdownHandler(srv *http.Server, running chan bool) chan bool {
|
||||||
|
shuttingdown := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-running
|
||||||
|
|
||||||
|
if err := srv.Shutdown(nil); err != nil {
|
||||||
|
fmt.Println("Error shutting down server")
|
||||||
|
}
|
||||||
|
|
||||||
|
shuttingdown <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
return shuttingdown
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Shutdown() {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user