-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add parser and depgraph implementation
- Loading branch information
1 parent
3d4aa5e
commit 7e80ddf
Showing
5 changed files
with
275 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,116 @@ | ||
package foreman | ||
package foreman | ||
|
||
import ( | ||
"errors" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/Eslam-Nawara/foreman/internal/depgraph" | ||
parser "github.com/Eslam-Nawara/foreman/internal/procparser" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
const interval = 50 * time.Millisecond | ||
|
||
type Foreman struct { | ||
services map[string]parser.Service | ||
verbose bool | ||
logger *log.Logger | ||
servicesMutex sync.Mutex | ||
} | ||
|
||
func New(filePath string, verbose bool) (*Foreman, error) { | ||
foreman := &Foreman{ | ||
services: map[string]parser.Service{}, | ||
servicesMutex: sync.Mutex{}, | ||
logger: log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime), | ||
verbose: verbose, | ||
} | ||
|
||
yamlMap := make(map[string]map[string]any) | ||
|
||
data, err := os.ReadFile(filePath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = yaml.Unmarshal([]byte(data), yamlMap) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for serviceName, serviceInfo := range yamlMap { | ||
service := parser.ParseService(serviceInfo) | ||
service.Name = serviceName | ||
foreman.services[serviceName] = service | ||
} | ||
return foreman, nil | ||
} | ||
|
||
// start all services from yaml file | ||
func (foreman *Foreman) Start() error { | ||
var wg sync.WaitGroup | ||
graph := foreman.buildDepGraph() | ||
|
||
if depgraph.IsCyclic(graph) { | ||
return errors.New("Cyclic dependencies detected") | ||
} | ||
|
||
services := depgraph.TopSort(graph) | ||
|
||
for _, serviceName := range services { | ||
wg.Add(1) | ||
go func(serviceName string) { | ||
defer wg.Done() | ||
foreman.runService(serviceName) | ||
}(serviceName) | ||
} | ||
|
||
go func() { | ||
sigChan := make(chan os.Signal) | ||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||
<-sigChan | ||
foreman.Exit(1) | ||
}() | ||
wg.Wait() | ||
return nil | ||
} | ||
|
||
// start one service and wait for it | ||
func (foreman *Foreman) runService(serviceName string) error { | ||
return nil | ||
} | ||
|
||
func (foreman *Foreman) checker(serviceName string, stopChecker chan bool) { | ||
} | ||
|
||
func (foreman *Foreman) buildDepGraph() map[string][]string { | ||
graph := make(map[string][]string) | ||
for serviceName, service := range foreman.services { | ||
graph[serviceName] = service.Deps | ||
} | ||
return graph | ||
} | ||
|
||
func (f *Foreman) vLog(msg string) { | ||
if f.verbose { | ||
f.logger.Print(msg) | ||
} | ||
} | ||
|
||
// ُExit kills all the running services and checkers. | ||
// exits foreman with the given exit status. | ||
func (f *Foreman) Exit(exitStatus int) { | ||
f.servicesMutex.Lock() | ||
for _, service := range f.services { | ||
if service.Process != nil { | ||
syscall.Kill(-service.Process.Pid, syscall.SIGINT) | ||
} | ||
} | ||
f.servicesMutex.Unlock() | ||
os.Exit(exitStatus) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module github.com/Eslam-Nawara/foreman | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/shirou/gopsutil v3.21.11+incompatible | ||
gopkg.in/yaml.v3 v3.0.1 | ||
) | ||
|
||
require ( | ||
github.com/go-ole/go-ole v1.2.6 // indirect | ||
github.com/tklauser/go-sysconf v0.3.11 // indirect | ||
github.com/tklauser/numcpus v0.6.0 // indirect | ||
github.com/yusufpapurcu/wmi v1.2.2 // indirect | ||
golang.org/x/sys v0.3.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= | ||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= | ||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= | ||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= | ||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= | ||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= | ||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= | ||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= | ||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= | ||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= | ||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,57 @@ | ||
package depgraph | ||
|
||
const ( | ||
unvisited = 0 | ||
visited = 1 | ||
currentlyVisiting = 2 | ||
) | ||
|
||
func TopSort(graph map[string][]string) []string { | ||
var out []string | ||
state := make(map[string]int) | ||
var ts func(string) | ||
|
||
ts = func(node string) { | ||
if state[node] == visited { | ||
return | ||
} | ||
state[node] = visited | ||
for _, child := range graph[node] { | ||
ts(child) | ||
} | ||
out = append(out, node) | ||
} | ||
|
||
for node := range graph { | ||
ts(node) | ||
} | ||
|
||
return out | ||
} | ||
|
||
func IsCyclic(graph map[string][]string) bool { | ||
state := make(map[string]int) | ||
cyclic := false | ||
var dfs func(string) | ||
|
||
dfs = func(node string) { | ||
if state[node] == visited { | ||
return | ||
} | ||
if state[node] == currentlyVisiting { | ||
cyclic = true | ||
return | ||
} | ||
|
||
state[node] = currentlyVisiting | ||
for _, child := range graph[node] { | ||
dfs(child) | ||
} | ||
state[node] = visited | ||
} | ||
|
||
for node := range graph { | ||
dfs(node) | ||
} | ||
return cyclic | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,69 @@ | ||
package procparser | ||
package procparser | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
type ( | ||
Service struct { | ||
Name string | ||
Status bool | ||
Process *os.Process | ||
Cmd string | ||
RunOnce bool | ||
Checks ServiceChecks | ||
Deps []string | ||
} | ||
|
||
ServiceChecks struct { | ||
Cmd string | ||
TcpPorts []string | ||
UdpPorts []string | ||
} | ||
) | ||
|
||
func ParseService(serviceMap map[string]any) Service { | ||
service := Service{} | ||
|
||
for key, value := range serviceMap { | ||
switch key { | ||
case "cmd": | ||
service.cmd = value.(string) | ||
case "run_once": | ||
service.RunOnce = value.(bool) | ||
case "deps": | ||
for _, dep := range value.([]any) { | ||
service.Deps = append(service.Deps, dep.(string)) | ||
} | ||
case "checks": | ||
service.Checks = parseChecks(value) | ||
} | ||
} | ||
return service | ||
} | ||
|
||
func parseChecks(serviceChecks any) ServiceChecks { | ||
checksMap := ServiceChecks{} | ||
|
||
for key, value := range serviceChecks.(map[string]any) { | ||
switch key { | ||
case "cmd": | ||
checksMap.Cmd = value.(string) | ||
case "tcp_ports": | ||
checksMap.TcpPorts = parsePorts(value) | ||
case "udp_ports": | ||
checksMap.UdpPorts = parsePorts(value) | ||
} | ||
} | ||
return checksMap | ||
} | ||
|
||
func parsePorts(ports any) []string { | ||
var parsedPorts []string | ||
for _, port := range ports.([]any) { | ||
parsedPorts = append(parsedPorts, fmt.Sprint(port.(int))) | ||
} | ||
|
||
return parsedPorts | ||
} |