Skip to content
This repository was archived by the owner on Jan 26, 2021. It is now read-only.

Commit 6fa3211

Browse files
committed
get rid of file management - move over to memory storage
1 parent ec61212 commit 6fa3211

File tree

14 files changed

+299
-292
lines changed

14 files changed

+299
-292
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ module github.com/refs/pman
33
go 1.15
44

55
require (
6+
github.com/magiconair/properties v1.8.1
67
github.com/olekukonko/tablewriter v0.0.4
78
github.com/rs/zerolog v1.19.0
89
github.com/spf13/cobra v1.0.0
910
github.com/spf13/viper v1.7.0
11+
github.com/stretchr/testify v1.7.0
1012
golang.org/x/sys v0.0.0-20200523222454-059865788121
1113
)

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
189189
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
190190
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
191191
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
192+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
193+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
192194
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
193195
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
194196
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -323,6 +325,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
323325
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
324326
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
325327
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
328+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
329+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
326330
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
327331
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
328332
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

pkg/config/config.go

+6-16
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,28 @@
11
package config
22

3-
import (
4-
"io/ioutil"
5-
"log"
6-
)
7-
83
// Config determines behavior across the tool.
94
type Config struct {
105
// Hostname where the runtime is running. When using PMAN in cli mode, it determines where the host runtime is.
116
// Default is localhost.
127
Hostname string
138

149
// Port configures the port where a runtime is available. It defaults to 10666.
15-
Port string
16-
17-
// File configures where Pman's database of PID lives.
18-
File string
10+
Port string
1911

2012
// KeepAlive configures if restart attempts are made if the process supervised terminates. Default is false.
2113
KeepAlive bool
2214
}
2315

16+
var (
17+
defaultHostname = "localhost"
18+
defaultPort = "10666"
19+
)
20+
2421
// NewConfig returns a new config with a set of defaults.
2522
func NewConfig() *Config {
26-
f, err := ioutil.TempFile("", "*")
27-
if err != nil {
28-
log.Fatal(err)
29-
}
30-
mustWrite(f.Name(), []byte("{}"))
31-
3223
return &Config{
3324
Hostname: defaultHostname,
3425
Port: defaultPort,
35-
File: f.Name(),
3626
KeepAlive: false,
3727
}
3828
}

pkg/config/util.go

-18
This file was deleted.

pkg/controller/controller.go

+32-68
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package controller
33
import (
44
"fmt"
55
"github.com/refs/pman/pkg/config"
6-
"io/ioutil"
6+
"github.com/refs/pman/pkg/process"
7+
"github.com/refs/pman/pkg/storage"
8+
"github.com/refs/pman/pkg/watcher"
9+
"github.com/rs/zerolog"
710
"os"
811
"os/exec"
912
"sort"
@@ -12,25 +15,25 @@ import (
1215
"sync"
1316
"time"
1417

15-
"github.com/refs/pman/pkg/log"
16-
"github.com/refs/pman/pkg/process"
17-
"github.com/refs/pman/pkg/watcher"
18-
"github.com/rs/zerolog"
19-
2018
"github.com/olekukonko/tablewriter"
2119
)
2220

23-
// Controller writes the current managed processes onto a file, or any ReadWrite.
21+
// Controller supervises processes.
2422
type Controller struct {
2523
m *sync.RWMutex
2624
options Options
2725
log zerolog.Logger
2826
Config *config.Config
27+
28+
Store *storage.Map
29+
2930
// Bin is the OCIS single binary name.
3031
Bin string
32+
3133
// BinPath is the OCIS single binary path withing the host machine.
3234
// The Controller needs to know the binary location in order to spawn new extensions.
3335
BinPath string
36+
3437
// Terminated facilitates communication from Watcher <-> Controller. Writes to this
3538
// channel WILL always attempt to restart the crashed process.
3639
Terminated chan process.ProcEntry
@@ -49,14 +52,14 @@ func NewController(o ...Option) Controller {
4952
}
5053

5154
c := Controller{
52-
m: &sync.RWMutex{},
53-
options: *opts,
54-
log: log.NewLogger(
55-
log.WithPretty(true),
56-
),
57-
Config: opts.Config,
55+
m: &sync.RWMutex{},
56+
options: *opts,
57+
log: *opts.Log,
5858
Bin: "ocis",
5959
Terminated: make(chan process.ProcEntry),
60+
Store: storage.NewMapStorage(),
61+
62+
Config: opts.Config,
6063
}
6164

6265
if opts.Bin != "" {
@@ -69,45 +72,33 @@ func NewController(o ...Option) Controller {
6972
c.log.Debug().Msg("OCIS binary not present in PATH, using Args[0]")
7073
path = os.Args[0]
7174
}
72-
7375
c.BinPath = path
74-
75-
if _, err := os.Stat(opts.Config.File); err != nil {
76-
c.log.Debug().Str("package", "controller").Msgf("setting up db")
77-
ioutil.WriteFile(opts.Config.File, []byte("{}"), 0644)
78-
}
79-
8076
return c
8177
}
8278

8379
// Start and watches a process.
8480
func (c *Controller) Start(pe process.ProcEntry) error {
85-
var err error
86-
var pid int
87-
88-
if pid, err = c.storedPID(pe.Extension); pid != 0 {
81+
if pid := c.Store.Load(pe.Extension); pid != 0 {
8982
c.log.Debug().Msg(fmt.Sprintf("extension already running: %s", pe.Extension))
9083
return nil
9184
}
92-
if err != nil {
93-
return err
94-
}
9585

9686
w := watcher.NewWatcher()
9787
if err := pe.Start(c.BinPath); err != nil {
9888
return err
9989
}
10090

101-
if err := c.write(pe); err != nil {
91+
// store the spawned child process PID.
92+
if err := c.Store.Store(pe); err != nil {
10293
return err
10394
}
95+
10496
w.Follow(pe, c.Terminated, c.options.Config.KeepAlive)
10597

10698
once.Do(func() {
10799
j := janitor{
108-
c.m,
109-
c.Config.File,
110100
time.Second,
101+
c.Store,
111102
}
112103

113104
go j.run()
@@ -117,40 +108,29 @@ func (c *Controller) Start(pe process.ProcEntry) error {
117108
}
118109

119110
// Kill a managed process.
120-
// TODO(refs) this interface MUST also work with PIDs.
121111
// Should a process managed by the runtime be allowed to be killed if the runtime is configured not to?
122-
func (c *Controller) Kill(ext *string) error {
123-
pid, err := c.storedPID(*ext)
124-
if err != nil {
125-
return err
126-
}
112+
func (c *Controller) Kill(pe process.ProcEntry) error {
113+
// load stored PID
114+
pid := c.Store.Load(pe.Extension)
115+
116+
// find process in host by PID
127117
p, err := os.FindProcess(pid)
128118
if err != nil {
129119
return err
130120
}
131121

132-
if err := c.delete(*ext); err != nil {
122+
if err := c.Store.Delete(pe); err != nil {
133123
return err
134124
}
135-
c.log.Info().Str("package", "watcher").Msgf("terminating %v", *ext)
125+
c.log.Info().Str("package", "watcher").Msgf("terminating %v", pe.Extension)
126+
127+
// terminate child process
136128
return p.Kill()
137129
}
138130

139131
// Shutdown a running runtime.
140132
func (c *Controller) Shutdown(ch chan struct{}) error {
141-
// We cannot be sure when was the last write before a shutdown routine begins without a plan. "Plan" in the sense of
142-
// an argument with every extension that must be started. This way we could block access to the io.Writer the "db"
143-
// uses, and either halt and prevent main from forking more children, or let them all run and once the reader gets
144-
// freed, start shutting them all down, a.k.a reverse the process. For the time being a simple Sleep would ensure
145-
// that all children are spawned and the last writer has been executed. Alternatively proper synchronization can
146-
// be ensured with the combination of a set of extensions that must run and a wait group.
147-
time.Sleep(1 * time.Second)
148-
149-
entries, err := loadDB(c.Config.File)
150-
if err != nil {
151-
return err
152-
}
153-
133+
entries := c.Store.LoadAll()
154134
for cmd, pid := range entries {
155135
c.log.Info().Str("package", "watcher").Msgf("gracefully terminating %v", cmd)
156136
p, _ := os.FindProcess(pid)
@@ -159,10 +139,6 @@ func (c *Controller) Shutdown(ch chan struct{}) error {
159139
}
160140
}
161141

162-
if err := c.Reset(); err != nil {
163-
return err
164-
}
165-
166142
ch <- struct{}{}
167143
return nil
168144
}
@@ -173,12 +149,7 @@ func (c *Controller) List() string {
173149
table := tablewriter.NewWriter(tableString)
174150
table.SetHeader([]string{"Extension", "PID"})
175151

176-
c.m.Lock()
177-
entries, err := loadDB(c.Config.File)
178-
if err != nil {
179-
c.log.Err(err).Msg(fmt.Sprintf("error loading file: %s", c.Config.File))
180-
}
181-
c.m.Unlock()
152+
entries := c.Store.LoadAll()
182153

183154
keys := make([]string, 0, len(entries))
184155
for k := range entries {
@@ -194,10 +165,3 @@ func (c *Controller) List() string {
194165
table.Render()
195166
return tableString.String()
196167
}
197-
198-
// Reset clears the db file.
199-
func (c *Controller) Reset() error {
200-
c.m.RLock()
201-
defer c.m.RUnlock()
202-
return os.Remove(c.Config.File)
203-
}

pkg/controller/janitor.go

+12-23
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
package controller
22

33
import (
4-
"encoding/json"
5-
"io/ioutil"
4+
"github.com/refs/pman/pkg/process"
5+
"github.com/refs/pman/pkg/storage"
66
"os"
77
"os/signal"
8-
"sync"
98
"syscall"
109
"time"
1110
)
1211

1312
type janitor struct {
14-
// shared mutex with the Controller instance.
15-
m *sync.RWMutex
16-
// file containing the location of the runtime service | process registry.
17-
db string
1813
// interval at which db is cleared.
1914
interval time.Duration
15+
16+
store *storage.Map
2017
}
2118

2219
func (j *janitor) run() {
@@ -29,31 +26,23 @@ func (j *janitor) run() {
2926
case <-work:
3027
return
3128
case <-ticker.C:
32-
cleanup(j.db, j.m)
29+
j.cleanup()
3330
}
3431
}
3532
}
3633

3734
// cleanup removes orphaned extension + pid that were killed via SIGKILL given the nature of is being un-catchable,
3835
// the only way to update pman's database is by polling.
39-
func cleanup(f string, m *sync.RWMutex) {
40-
m.Lock()
41-
entries, _ := loadDB(f)
42-
m.Unlock()
43-
44-
m.RLock()
45-
for name, pid := range entries {
36+
func (j *janitor) cleanup() {
37+
for name, pid := range j.store.LoadAll() {
4638
// On unix like systems (linux, freebsd, etc) os.FindProcess will never return an error
4739
if p, err := os.FindProcess(pid); err == nil {
4840
if err := p.Signal(syscall.Signal(0)); err != nil {
49-
// TODO(refs) use configured logger and log cleaning info
50-
delete(entries, name)
41+
j.store.Delete(process.ProcEntry{
42+
Pid: pid,
43+
Extension: name,
44+
})
5145
}
5246
}
5347
}
54-
55-
bytes, _ := json.Marshal(entries)
56-
57-
_ = ioutil.WriteFile(f, bytes, 0644)
58-
m.RUnlock()
59-
}
48+
}

pkg/controller/option.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package controller
22

3-
import "github.com/refs/pman/pkg/config"
3+
import (
4+
"github.com/refs/pman/pkg/config"
5+
"github.com/rs/zerolog"
6+
)
47

58
// Options are the configurable options for a Controller.
69
type Options struct {
7-
Bin string
10+
Bin string
811
Restart bool
9-
Config *config.Config
12+
Config *config.Config
13+
Log *zerolog.Logger
1014
}
1115

1216
// Option represents an option.
@@ -23,3 +27,10 @@ func WithConfig(cfg *config.Config) Option {
2327
o.Config = cfg
2428
}
2529
}
30+
31+
// WithLog sets Controller config.
32+
func WithLog(l *zerolog.Logger) Option {
33+
return func(o *Options) {
34+
o.Log = l
35+
}
36+
}

0 commit comments

Comments
 (0)