Skip to content

Commit

Permalink
cmd/relui: load development state on boot
Browse files Browse the repository at this point in the history
When relui starts, it will look in dev-data-directory for data
persisted from the last boot. This enables local development to continue
when testing changes manually, building on CL 246298.

For golang/go#40279

Change-Id: I02f8b6e1178f82425cafcd2a0544327ba84e028e
Reviewed-on: https://go-review.googlesource.com/c/build/+/250917
Run-TryBot: Alexander Rakoczy <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Carlos Amedee <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
  • Loading branch information
toothrot committed Aug 27, 2020
1 parent 6f3f353 commit d6a7ee8
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 4 deletions.
7 changes: 5 additions & 2 deletions cmd/relui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ var (

func main() {
flag.Parse()
s := &server{store: newFileStore(*devDataDir), configs: loadWorkflowConfig("./workflows")}
fs := newFileStore(*devDataDir)
if err := fs.load(); err != nil {
log.Fatalf("Error loading state from %q: %v", *devDataDir, err)
}
s := &server{store: fs, configs: loadWorkflowConfig("./workflows")}
http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
http.Handle("/", fileServerHandler(relativeFile("./static"), http.HandlerFunc(s.homeHandler)))
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

log.Printf("Listening on :" + port)
log.Fatal(http.ListenAndServe(":"+port, http.DefaultServeMux))
}
Expand Down
19 changes: 19 additions & 0 deletions cmd/relui/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type fileStore struct {
ls *reluipb.LocalStorage

// persistDir is a path to a directory for saving application data in textproto format.
// Set persistDir to an empty string to disable saving and loading from the filesystem.
persistDir string
}

Expand Down Expand Up @@ -83,3 +84,21 @@ func (f *fileStore) persist() error {
}
return nil
}

// load reads fileStore state from persistDir/fileStoreName.
func (f *fileStore) load() error {
if f.persistDir == "" {
return nil
}
path := filepath.Join(f.persistDir, fileStoreName)
b, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("ioutil.ReadFile(%q) = _, %v", path, err)
}
f.mu.Lock()
defer f.mu.Unlock()
return proto.UnmarshalText(string(b), f.ls)
}
96 changes: 94 additions & 2 deletions cmd/relui/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import (
)

func TestFileStorePersist(t *testing.T) {
dir, err := ioutil.TempDir("", "memory-store-test")
dir, err := ioutil.TempDir("", "fileStore-test")
if err != nil {
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "memory-store-test", err)
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "fileStore-test", err)
}
defer os.RemoveAll(dir)
want := &reluipb.LocalStorage{
Expand Down Expand Up @@ -50,3 +50,95 @@ func TestFileStorePersist(t *testing.T) {
t.Errorf("reluipb.LocalStorage mismatch (-want, +got):\n%s", diff)
}
}

func TestFileStoreLoad(t *testing.T) {
dir, err := ioutil.TempDir("", "fileStore-test")
if err != nil {
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "fileStore-test", err)
}
defer os.RemoveAll(dir)
if err := os.MkdirAll(filepath.Join(dir, "relui"), 0755); err != nil {
t.Errorf("os.MkDirAll(%q, %v) = %w", filepath.Join(dir, "relui"), 0755, err)
}
want := &reluipb.LocalStorage{
Workflows: []*reluipb.Workflow{
{
Name: "Load Test",
BuildableTasks: []*reluipb.BuildableTask{{Name: "Load Test Task"}},
},
},
}
data := []byte(proto.MarshalTextString(want))
dst := filepath.Join(dir, "relui", fileStoreName)
if err := ioutil.WriteFile(dst, data, 0644); err != nil {
t.Fatalf("ioutil.WriteFile(%q, _, %v) = %v", dst, 0644, err)
}

fs := newFileStore(filepath.Join(dir, "relui"))
if err := fs.load(); err != nil {
t.Errorf("reluipb.load() = %v, wanted no error", err)
}

if diff := cmp.Diff(want, fs.localStorage()); diff != "" {
t.Errorf("reluipb.LocalStorage mismatch (-want, +got):\n%s", diff)
}
}

func TestFileStoreLoadErrors(t *testing.T) {
empty, err := ioutil.TempDir("", "fileStoreLoad")
if err != nil {
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
}
defer os.RemoveAll(empty)

collision, err := ioutil.TempDir("", "fileStoreLoad")
if err != nil {
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
}
defer os.RemoveAll(collision)
// We want to trigger an error when trying to read the file, so make a directory with the same name.
if err := os.MkdirAll(filepath.Join(collision, fileStoreName), 0755); err != nil {
t.Errorf("os.MkDirAll(%q, %v) = %w", filepath.Join(collision, fileStoreName), 0755, err)
}

corrupt, err := ioutil.TempDir("", "fileStoreLoad")
if err != nil {
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
}
defer os.RemoveAll(corrupt)
if err := ioutil.WriteFile(filepath.Join(corrupt, fileStoreName), []byte("oh no"), 0644); err != nil {
t.Fatalf("ioutil.WriteFile(%q, %q, %v) = %v, wanted no error", filepath.Join(corrupt, fileStoreName), "oh no", 0644, err)
}

cases := []struct {
desc string
dir string
wantErr bool
}{
{
desc: "no persistDir configured",
},
{
desc: "no file in persistDir",
dir: empty,
},
{
desc: "other error reading file",
dir: collision,
wantErr: true,
},
{
desc: "corrupt data in persistDir",
dir: corrupt,
wantErr: true,
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
f := newFileStore(c.dir)
if err := f.load(); (err != nil) != c.wantErr {
t.Errorf("f.load() = %v, wantErr = %t", err, c.wantErr)
}
})
}
}

0 comments on commit d6a7ee8

Please sign in to comment.