Skip to content

Commit d6a7ee8

Browse files
committed
cmd/relui: load development state on boot
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]>
1 parent 6f3f353 commit d6a7ee8

File tree

3 files changed

+118
-4
lines changed

3 files changed

+118
-4
lines changed

cmd/relui/main.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,18 @@ var (
2323

2424
func main() {
2525
flag.Parse()
26-
s := &server{store: newFileStore(*devDataDir), configs: loadWorkflowConfig("./workflows")}
26+
fs := newFileStore(*devDataDir)
27+
if err := fs.load(); err != nil {
28+
log.Fatalf("Error loading state from %q: %v", *devDataDir, err)
29+
}
30+
s := &server{store: fs, configs: loadWorkflowConfig("./workflows")}
2731
http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
2832
http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
2933
http.Handle("/", fileServerHandler(relativeFile("./static"), http.HandlerFunc(s.homeHandler)))
3034
port := os.Getenv("PORT")
3135
if port == "" {
3236
port = "8080"
3337
}
34-
3538
log.Printf("Listening on :" + port)
3639
log.Fatal(http.ListenAndServe(":"+port, http.DefaultServeMux))
3740
}

cmd/relui/store.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type fileStore struct {
4242
ls *reluipb.LocalStorage
4343

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

@@ -83,3 +84,21 @@ func (f *fileStore) persist() error {
8384
}
8485
return nil
8586
}
87+
88+
// load reads fileStore state from persistDir/fileStoreName.
89+
func (f *fileStore) load() error {
90+
if f.persistDir == "" {
91+
return nil
92+
}
93+
path := filepath.Join(f.persistDir, fileStoreName)
94+
b, err := ioutil.ReadFile(path)
95+
if err != nil {
96+
if os.IsNotExist(err) {
97+
return nil
98+
}
99+
return fmt.Errorf("ioutil.ReadFile(%q) = _, %v", path, err)
100+
}
101+
f.mu.Lock()
102+
defer f.mu.Unlock()
103+
return proto.UnmarshalText(string(b), f.ls)
104+
}

cmd/relui/store_test.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import (
1616
)
1717

1818
func TestFileStorePersist(t *testing.T) {
19-
dir, err := ioutil.TempDir("", "memory-store-test")
19+
dir, err := ioutil.TempDir("", "fileStore-test")
2020
if err != nil {
21-
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "memory-store-test", err)
21+
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "fileStore-test", err)
2222
}
2323
defer os.RemoveAll(dir)
2424
want := &reluipb.LocalStorage{
@@ -50,3 +50,95 @@ func TestFileStorePersist(t *testing.T) {
5050
t.Errorf("reluipb.LocalStorage mismatch (-want, +got):\n%s", diff)
5151
}
5252
}
53+
54+
func TestFileStoreLoad(t *testing.T) {
55+
dir, err := ioutil.TempDir("", "fileStore-test")
56+
if err != nil {
57+
t.Fatalf("ioutil.TempDir(%q, %q) = _, %v", "", "fileStore-test", err)
58+
}
59+
defer os.RemoveAll(dir)
60+
if err := os.MkdirAll(filepath.Join(dir, "relui"), 0755); err != nil {
61+
t.Errorf("os.MkDirAll(%q, %v) = %w", filepath.Join(dir, "relui"), 0755, err)
62+
}
63+
want := &reluipb.LocalStorage{
64+
Workflows: []*reluipb.Workflow{
65+
{
66+
Name: "Load Test",
67+
BuildableTasks: []*reluipb.BuildableTask{{Name: "Load Test Task"}},
68+
},
69+
},
70+
}
71+
data := []byte(proto.MarshalTextString(want))
72+
dst := filepath.Join(dir, "relui", fileStoreName)
73+
if err := ioutil.WriteFile(dst, data, 0644); err != nil {
74+
t.Fatalf("ioutil.WriteFile(%q, _, %v) = %v", dst, 0644, err)
75+
}
76+
77+
fs := newFileStore(filepath.Join(dir, "relui"))
78+
if err := fs.load(); err != nil {
79+
t.Errorf("reluipb.load() = %v, wanted no error", err)
80+
}
81+
82+
if diff := cmp.Diff(want, fs.localStorage()); diff != "" {
83+
t.Errorf("reluipb.LocalStorage mismatch (-want, +got):\n%s", diff)
84+
}
85+
}
86+
87+
func TestFileStoreLoadErrors(t *testing.T) {
88+
empty, err := ioutil.TempDir("", "fileStoreLoad")
89+
if err != nil {
90+
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
91+
}
92+
defer os.RemoveAll(empty)
93+
94+
collision, err := ioutil.TempDir("", "fileStoreLoad")
95+
if err != nil {
96+
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
97+
}
98+
defer os.RemoveAll(collision)
99+
// We want to trigger an error when trying to read the file, so make a directory with the same name.
100+
if err := os.MkdirAll(filepath.Join(collision, fileStoreName), 0755); err != nil {
101+
t.Errorf("os.MkDirAll(%q, %v) = %w", filepath.Join(collision, fileStoreName), 0755, err)
102+
}
103+
104+
corrupt, err := ioutil.TempDir("", "fileStoreLoad")
105+
if err != nil {
106+
t.Fatalf("ioutil.TempDir(%q, %q) = %v, wanted no error", "", "fileStoreLoad", err)
107+
}
108+
defer os.RemoveAll(corrupt)
109+
if err := ioutil.WriteFile(filepath.Join(corrupt, fileStoreName), []byte("oh no"), 0644); err != nil {
110+
t.Fatalf("ioutil.WriteFile(%q, %q, %v) = %v, wanted no error", filepath.Join(corrupt, fileStoreName), "oh no", 0644, err)
111+
}
112+
113+
cases := []struct {
114+
desc string
115+
dir string
116+
wantErr bool
117+
}{
118+
{
119+
desc: "no persistDir configured",
120+
},
121+
{
122+
desc: "no file in persistDir",
123+
dir: empty,
124+
},
125+
{
126+
desc: "other error reading file",
127+
dir: collision,
128+
wantErr: true,
129+
},
130+
{
131+
desc: "corrupt data in persistDir",
132+
dir: corrupt,
133+
wantErr: true,
134+
},
135+
}
136+
for _, c := range cases {
137+
t.Run(c.desc, func(t *testing.T) {
138+
f := newFileStore(c.dir)
139+
if err := f.load(); (err != nil) != c.wantErr {
140+
t.Errorf("f.load() = %v, wantErr = %t", err, c.wantErr)
141+
}
142+
})
143+
}
144+
}

0 commit comments

Comments
 (0)