main: save state of weldr server to /var/lib

This commit is contained in:
Lars Karlitski 2019-09-15 18:51:21 +02:00
parent 73655f600e
commit 11af3f0c0b
3 changed files with 124 additions and 35 deletions

61
main.go
View file

@ -3,16 +3,20 @@ package main
import (
"context"
"flag"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/signal"
"path/filepath"
"osbuild-composer/rpmmd"
"osbuild-composer/weldr"
)
const StateFile = "/var/lib/osbuild-composer/weldr-state.json"
func main() {
var verbose bool
flag.BoolVar(&verbose, "v", false, "Print access log")
@ -44,9 +48,28 @@ func main() {
logger = log.New(os.Stdout, "", 0)
}
api := weldr.New(repo, packages, logger)
server := http.Server{Handler: api}
err = os.MkdirAll("/var/lib/osbuild-composer", 0755)
if err != nil {
panic(err)
}
state, err := ioutil.ReadFile(StateFile)
if err != nil && !os.IsNotExist(err) {
log.Fatalf("cannot read state: %v", err)
}
stateChannel := make(chan []byte, 10)
api := weldr.New(repo, packages, logger, state, stateChannel)
go func() {
for {
err := writeFileAtomically(StateFile, <-stateChannel, 0755)
if err != nil {
log.Fatalf("cannot write state: %v", err)
}
}
}()
server := http.Server{Handler: api}
shutdownDone := make(chan struct{}, 1)
go func() {
channel := make(chan os.Signal, 1)
@ -63,3 +86,37 @@ func main() {
<-shutdownDone
}
func writeFileAtomically(filename string, data []byte, mode os.FileMode) error {
dir, name := filepath.Dir(filename), filepath.Base(filename)
tmpfile, err := ioutil.TempFile(dir, name + "-*.tmp")
if err != nil {
return err
}
_, err = tmpfile.Write(data)
if err != nil {
os.Remove(tmpfile.Name())
return err
}
err = tmpfile.Chmod(mode)
if err != nil {
return err
}
err = tmpfile.Close()
if err != nil {
os.Remove(tmpfile.Name())
return err
}
err = os.Rename(tmpfile.Name(), filename)
if err != nil {
os.Remove(tmpfile.Name())
return err
}
return nil
}

View file

@ -22,22 +22,24 @@ type API struct {
router *httprouter.Router
}
func New(repo rpmmd.RepoConfig, packages rpmmd.PackageList, logger *log.Logger) *API {
func New(repo rpmmd.RepoConfig, packages rpmmd.PackageList, logger *log.Logger, initialState []byte, stateChannel chan<- []byte) *API {
api := &API{
store: newStore(),
store: newStore(initialState, stateChannel),
repo: repo,
packages: packages,
logger: logger,
}
// sample blueprint
api.store.pushBlueprint(blueprint{
Name: "example",
Description: "An Example",
Version: "1",
Packages: []blueprintPackage{{"httpd", "2.*"}},
Modules: []blueprintPackage{},
})
// sample blueprint on first run
if initialState == nil {
api.store.pushBlueprint(blueprint{
Name: "example",
Description: "An Example",
Version: "1",
Packages: []blueprintPackage{{"httpd", "2.*"}},
Modules: []blueprintPackage{},
})
}
api.router = httprouter.New()
api.router.RedirectTrailingSlash = false

View file

@ -1,6 +1,8 @@
package weldr
import (
"log"
"encoding/json"
"sort"
"sync"
)
@ -9,7 +11,8 @@ type store struct {
Blueprints map[string]blueprint `json:"blueprints"`
Workspace map[string]blueprint `json:"workspace"`
mu sync.RWMutex // protects all fields
mu sync.RWMutex // protects all fields
stateChannel chan<- []byte
}
type blueprint struct {
@ -25,10 +28,41 @@ type blueprintPackage struct {
Version string `json:"version,omitempty"`
}
func newStore() *store {
return &store{
Blueprints: make(map[string]blueprint),
Workspace: make(map[string]blueprint),
func newStore(initialState []byte, stateChannel chan<- []byte) *store {
var s store
if initialState != nil {
err := json.Unmarshal(initialState, &s)
if err != nil {
log.Fatalf("invalid initial state: %v", err)
}
}
if s.Blueprints == nil {
s.Blueprints = make(map[string]blueprint)
}
if s.Workspace == nil {
s.Workspace = make(map[string]blueprint)
}
s.stateChannel = stateChannel
return &s
}
func (s *store) change(f func()) {
s.mu.Lock()
defer s.mu.Unlock()
f()
if s.stateChannel != nil {
serialized, err := json.Marshal(s)
if err != nil {
// we ought to know all types that go into the store
panic(err)
}
s.stateChannel <- serialized
}
}
@ -75,31 +109,27 @@ func (s *store) getBlueprint(name string, bp *blueprint, changed *bool) bool {
}
func (s *store) pushBlueprint(bp blueprint) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.Workspace, bp.Name)
s.Blueprints[bp.Name] = bp
s.change(func() {
delete(s.Workspace, bp.Name)
s.Blueprints[bp.Name] = bp
})
}
func (s *store) pushBlueprintToWorkspace(bp blueprint) {
s.mu.Lock()
defer s.mu.Unlock()
s.Workspace[bp.Name] = bp
s.change(func() {
s.Workspace[bp.Name] = bp
})
}
func (s *store) deleteBlueprint(name string) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.Workspace, name)
delete(s.Blueprints, name)
s.change(func() {
delete(s.Workspace, name)
delete(s.Blueprints, name)
})
}
func (s *store) deleteBlueprintFromWorkspace(name string) {
s.mu.Lock()
defer s.mu.Unlock()
delete(s.Workspace, name)
s.change(func() {
delete(s.Workspace, name)
})
}