1
1
package serve
2
2
3
3
import (
4
+ "bufio"
4
5
"context"
5
6
_ "embed"
6
7
"encoding/json"
@@ -10,6 +11,7 @@ import (
10
11
"strconv"
11
12
"strings"
12
13
14
+ "github.com/containerd/errdefs"
13
15
"github.com/docker/docker/api/types/container"
14
16
"github.com/docker/docker/api/types/network"
15
17
"github.com/docker/go-connections/nat"
@@ -68,6 +70,30 @@ const (
68
70
var mainFuncEmbed string
69
71
70
72
func Run (ctx context.Context , envFilePath string , noVerifyJWT * bool , importMapPath string , runtimeOption RuntimeOption , fsys afero.Fs ) error {
73
+ // 1. Sanity checks.
74
+ if err := restartEdgeRuntime (ctx , envFilePath , noVerifyJWT , importMapPath , runtimeOption , fsys ); err != nil {
75
+ return err
76
+ }
77
+ watcher := NewFileWatcher ()
78
+ go watcher .Start (ctx )
79
+ streamer := NewLogStreamer ()
80
+ go streamer .Start (ctx )
81
+ for {
82
+ select {
83
+ case <- ctx .Done ():
84
+ fmt .Println ("Stopped serving " + utils .Bold (utils .FunctionsDir ))
85
+ return ctx .Err ()
86
+ case <- watcher .RestartCh :
87
+ if err := restartEdgeRuntime (ctx , envFilePath , noVerifyJWT , importMapPath , runtimeOption , fsys ); err != nil {
88
+ return err
89
+ }
90
+ case err := <- streamer .ErrCh :
91
+ return err
92
+ }
93
+ }
94
+ }
95
+
96
+ func restartEdgeRuntime (ctx context.Context , envFilePath string , noVerifyJWT * bool , importMapPath string , runtimeOption RuntimeOption , fsys afero.Fs ) error {
71
97
// 1. Sanity checks.
72
98
if err := flags .LoadConfig (fsys ); err != nil {
73
99
return err
@@ -84,14 +110,50 @@ func Run(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPa
84
110
dbUrl := fmt .Sprintf ("postgresql://postgres:postgres@%s:5432/postgres" , utils .DbAliases [0 ])
85
111
// 3. Serve and log to console
86
112
fmt .Fprintln (os .Stderr , "Setting up Edge Functions runtime..." )
87
- if err := ServeFunctions (ctx , envFilePath , noVerifyJWT , importMapPath , dbUrl , runtimeOption , fsys ); err != nil {
88
- return err
113
+ return ServeFunctions (ctx , envFilePath , noVerifyJWT , importMapPath , dbUrl , runtimeOption , fsys )
114
+ }
115
+
116
+ type logStreamer struct {
117
+ ErrCh chan error
118
+ }
119
+
120
+ func NewLogStreamer () logStreamer {
121
+ return logStreamer {
122
+ ErrCh : make (chan error , 1 ),
89
123
}
90
- if err := utils .DockerStreamLogs (ctx , utils .EdgeRuntimeId , os .Stdout , os .Stderr ); err != nil {
91
- return err
124
+ }
125
+
126
+ func (s logStreamer ) Start (ctx context.Context ) {
127
+ for {
128
+ if err := utils .DockerStreamLogs (ctx , utils .EdgeRuntimeId , os .Stdout , os .Stderr , func (lo * container.LogsOptions ) {
129
+ lo .Timestamps = true
130
+ }); err != nil &&
131
+ ! errdefs .IsNotFound (err ) &&
132
+ ! strings .HasSuffix (err .Error (), "exit 137" ) &&
133
+ ! strings .HasSuffix (err .Error (), "can not get logs from container which is dead or marked for removal" ) {
134
+ s .ErrCh <- err
135
+ break
136
+ }
137
+ }
138
+ }
139
+
140
+ type fileWatcher struct {
141
+ RestartCh chan struct {}
142
+ }
143
+
144
+ func NewFileWatcher () fileWatcher {
145
+ return fileWatcher {
146
+ RestartCh : make (chan struct {}, 1 ),
147
+ }
148
+ }
149
+
150
+ func (w * fileWatcher ) Start (ctx context.Context ) {
151
+ // TODO: implement fs.notify
152
+ fmt .Fprintln (os .Stderr , "Press enter to reload..." )
153
+ scanner := bufio .NewScanner (os .Stdin )
154
+ for scanner .Scan () {
155
+ w .RestartCh <- struct {}{}
92
156
}
93
- fmt .Println ("Stopped serving " + utils .Bold (utils .FunctionsDir ))
94
- return nil
95
157
}
96
158
97
159
func ServeFunctions (ctx context.Context , envFilePath string , noVerifyJWT * bool , importMapPath string , dbUrl string , runtimeOption RuntimeOption , fsys afero.Fs ) error {
0 commit comments