Skip to content

Commit

Permalink
Merge pull request #16 from F1bonacc1/15-expose-cli-rpc-in-addition-t…
Browse files Browse the repository at this point in the history
…o-http

15 expose cli rpc in addition to http
  • Loading branch information
F1bonacc1 authored Oct 24, 2022
2 parents 2c66707 + 5ed1033 commit 496b987
Show file tree
Hide file tree
Showing 27 changed files with 603 additions and 127 deletions.
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Process Compose is a simple and flexible scheduler and orchestrator to manage no
- Forking (services or daemons) processes
- REST API (OpenAPI a.k.a Swagger)
- Logs caching
- Functions as both server and client

It is heavily inspired by [docker-compose](https://github.com/docker/compose), but without the need for containers. The configuration syntax tries to follow the docker-compose specifications, with a few minor additions and lots of subtractions.

Expand Down Expand Up @@ -394,10 +395,54 @@ Default environment variables:

#### ✅ <u>REST API</u>

A convenient Swagger API is provided: http://localhost:8080/swagger/index.html
A convenient Swagger API is provided: http://localhost:8080

<img src="./imgs/swagger.png" alt="Swagger" style="zoom:67%;" />

Default port is 8080. Specify your own port:

```shell
process-compose -p PORT
```

#### ✅ Client Mode

Process compose can also connect to itself as client. Available commands:

##### Processes List

```shell
process-compose process list #lists available processes
```

##### Process Start

```shell
process-compose process start [PROCESS] #starts one of the available non running processes
```

##### Process Stop

```shell
process-compose process stop [PROCESS] #stops one of the running processes
```

##### Process Restart

```shell
process-compose process start [PROCESS] #restarts one of the available processes
```

Restart will wait `process.availability.backoff_seconds` seconds between `stop` and `start` of the process. If not configured the default value is 1s.

By default the client will try to use the default port `8080` and default address `localhost` to connect to the locally running instance of process-compose. You can provide deferent values:

```shell
process-compose -p PORT process -a ADDRESS list
```



#### ✅ <u>Configuration</u>

##### ✅ Support .env file
Expand Down
2 changes: 1 addition & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pkgs.buildGoModule rec {
src = ./.;
ldflags = [ "-X main.version=v${version}" ];

vendorSha256 = "bJXym+JTAIbEh/dIkY22HdNwgYbdCGgUIts0KkS0GYk=";
vendorSha256 = "RqPH8gm8K8sLuRl4FTpGeitS++t3ygAgZ6OMvBCsCB8=";

postInstall = "mv $out/bin/{src,process-compose}";

Expand Down
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/gin-gonic/gin v1.8.1
github.com/joho/godotenv v1.4.0
github.com/rivo/tview v0.0.0-20220916081518-2e69b7385a37
github.com/swaggo/swag v1.8.6
github.com/swaggo/swag v1.8.7
gopkg.in/yaml.v2 v2.4.0
)

Expand All @@ -18,7 +18,9 @@ replace github.com/InVisionApp/go-health/v2 => github.com/f1bonacc1/go-health/v2
require (
github.com/InVisionApp/go-logger v1.0.1 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
Expand All @@ -28,6 +30,7 @@ require (
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
Expand All @@ -38,7 +41,11 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/cobra v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect
golang.org/x/net v0.0.0-20220926192436-02166a98028e // indirect
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
Expand Down
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand All @@ -32,6 +34,7 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04=
github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0=
github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
Expand Down Expand Up @@ -77,6 +80,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down Expand Up @@ -150,12 +155,18 @@ github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
Expand All @@ -174,10 +185,13 @@ github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/swaggo/swag v1.8.6 h1:2rgOaLbonWu1PLP6G+/rYjSvPg0jQE0HtrEKuE380eg=
github.com/swaggo/swag v1.8.6/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
Expand Down
19 changes: 19 additions & 0 deletions src/api/pc_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,22 @@ func StartProcess(c *gin.Context) {

c.JSON(http.StatusOK, gin.H{"name": name})
}

// @Schemes
// @Description Restarts the process
// @Tags Process
// @Summary Restart a process
// @Produce json
// @Param name path string true "Process Name"
// @Success 200 {string} string "Restarted Process Name"
// @Router /process/restart/{name} [post]
func RestartProcess(c *gin.Context) {
name := c.Param("name")
err := app.PROJ.RestartProcess(name)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"name": name})
}
8 changes: 8 additions & 0 deletions src/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"net/http"
"net/url"
)

// @title Process Compose API
Expand Down Expand Up @@ -32,10 +34,16 @@ func InitRoutes(useLogger bool) *gin.Engine {

//url := ginSwagger.URL("http://localhost:8080/swagger/doc.json") // The url pointing to API definition
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.GET("/", func(c *gin.Context) {
location := url.URL{Path: "/swagger/index.html"}
c.Redirect(http.StatusFound, location.RequestURI())
})

r.GET("/processes", GetProcesses)
r.GET("/process/logs/:name/:endOffset/:limit", GetProcessLogs)
r.PATCH("/process/stop/:name", StopProcess)
r.POST("/process/start/:name", StartProcess)
r.POST("/process/restart/:name", RestartProcess)

return r
}
4 changes: 4 additions & 0 deletions src/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ type ProcessState struct {
Pid int `json:"pid"`
}

type ProcessStates struct {
States []ProcessState `json:"data"`
}

func (p ProcessConfig) GetDependencies() []string {
dependencies := make([]string, len(p.DependsOn))

Expand Down
17 changes: 9 additions & 8 deletions src/app/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"syscall"
"time"

"github.com/f1bonacc1/process-compose/src/cmd"
"github.com/f1bonacc1/process-compose/src/command"
"github.com/f1bonacc1/process-compose/src/health"
"github.com/f1bonacc1/process-compose/src/pclog"

Expand Down Expand Up @@ -85,7 +85,7 @@ func (p *Process) run() error {
}
for {
starter := func() error {
p.command = cmd.BuildCommand(p.getCommand())
p.command = command.BuildCommand(p.getCommand())
p.command.Env = p.getProcessEnvironment()
p.setProcArgs()
stdout, _ := p.command.StdoutPipe()
Expand Down Expand Up @@ -116,7 +116,7 @@ func (p *Process) run() error {
p.waitForDaemonCompletion()
}

if !p.isRestartable(p.procState.ExitCode) {
if !p.isRestartable() {
break
}
p.setState(ProcessStateRestarting)
Expand Down Expand Up @@ -149,7 +149,8 @@ func (p *Process) getProcessEnvironment() []string {
return env
}

func (p *Process) isRestartable(exitCode int) bool {
func (p *Process) isRestartable() bool {
exitCode := p.procState.ExitCode
if p.procConf.RestartPolicy.Restart == RestartPolicyNo ||
p.procConf.RestartPolicy.Restart == "" {
return false
Expand Down Expand Up @@ -223,10 +224,10 @@ func (p *Process) doConfiguredStop(params ShutDownParams) error {
defer cancel()
defer p.notifyDaemonStopped()

command := cmd.BuildCommandContext(ctx, params.ShutDownCommand)
command.Env = p.getProcessEnvironment()
cmd := command.BuildCommandContext(ctx, params.ShutDownCommand)
cmd.Env = p.getProcessEnvironment()

if err := command.Run(); err != nil {
if err := cmd.Run(); err != nil {
// the process termination timedout and it will be killed
log.Error().Msgf("terminating %s with timeout %d failed - %s", p.getName(), timeout, err.Error())
return p.stop(int(syscall.SIGKILL))
Expand Down Expand Up @@ -393,7 +394,7 @@ func (p *Process) stopProbes() {
}
}

func (p *Process) onLivenessCheckEnd(isOk, isFatal bool, err string) {
func (p *Process) onLivenessCheckEnd(_, isFatal bool, err string) {
if isFatal {
log.Info().Msgf("%s is not alive anymore - %s", p.getName(), err)
p.logBuffer.Write("Error: liveness check fail - " + err)
Expand Down
32 changes: 25 additions & 7 deletions src/app/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package app
import (
"errors"
"fmt"
"io/ioutil"
"github.com/f1bonacc1/process-compose/src/pclog"
"os"
"path/filepath"
"sort"
"strings"

"github.com/f1bonacc1/process-compose/src/pclog"
"time"

"github.com/joho/godotenv"
"github.com/rs/zerolog"
Expand Down Expand Up @@ -170,9 +169,9 @@ func (p *Project) StartProcess(name string) error {
log.Error().Msgf("Process %s is already running", name)
return fmt.Errorf("process %s is already running", name)
}
if proc, ok := p.Processes[name]; ok {
proc.Name = name
p.runProcess(proc)
if processConfig, ok := p.Processes[name]; ok {
processConfig.Name = name
p.runProcess(processConfig)
} else {
return fmt.Errorf("no such process: %s", name)
}
Expand All @@ -190,6 +189,25 @@ func (p *Project) StopProcess(name string) error {
return nil
}

func (p *Project) RestartProcess(name string) error {
proc := p.getRunningProcess(name)
if proc != nil {
_ = proc.shutDown()
if proc.isRestartable() {
return nil
}
time.Sleep(proc.getBackoff())
}

if processConfig, ok := p.Processes[name]; ok {
processConfig.Name = name
p.runProcess(processConfig)
} else {
return fmt.Errorf("no such process: %s", name)
}
return nil
}

func (p *Project) ShutDownProject() {
p.mapMutex.Lock()
runProc := p.runningProcesses
Expand Down Expand Up @@ -331,7 +349,7 @@ func (p *Project) GetLexicographicProcessNames() []string {
}

func CreateProject(inputFile string) *Project {
yamlFile, err := ioutil.ReadFile(inputFile)
yamlFile, err := os.ReadFile(inputFile)

if err != nil {
if errors.Is(err, os.ErrNotExist) {
Expand Down
5 changes: 5 additions & 0 deletions src/client/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package client

type pcError struct {
Error string `json:"error"`
}
29 changes: 29 additions & 0 deletions src/client/processes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package client

import (
"encoding/json"
"fmt"
"github.com/f1bonacc1/process-compose/src/app"
"net/http"
)

func GetProcessesName(address string, port int) ([]string, error) {
url := fmt.Sprintf("http://%s:%d/processes", address, port)
resp, err := http.Get(url)
if err != nil {
return []string{}, err
}
defer resp.Body.Close()
//Create a variable of the same type as our model
var sResp app.ProcessStates

//Decode the data
if err := json.NewDecoder(resp.Body).Decode(&sResp); err != nil {
return []string{}, err
}
procs := make([]string, len(sResp.States))
for i, proc := range sResp.States {
procs[i] = proc.Name
}
return procs, nil
}
Loading

0 comments on commit 496b987

Please sign in to comment.