This commit is contained in:
Julien Neuhart
2018-12-10 16:09:19 +01:00
committed by GitHub
parent c1f6100382
commit 495203c112
473 changed files with 4631 additions and 174161 deletions

View File

@@ -0,0 +1,70 @@
package pm2
import (
"context"
"fmt"
"time"
"github.com/mafredri/cdp/devtool"
"github.com/thecodingmachine/gotenberg/internal/pkg/notify"
)
// Chrome facilitates starting or shutting down
// Chrome headless with PM2.
type Chrome struct{}
// Launch starts Chrome headless with PM2.
func (c *Chrome) Launch() error {
return launch(c)
}
// Shutdown stops Chrome headless and
// removes it from the list of PM2
// processes.
func (c *Chrome) Shutdown() error {
return shutdown(c)
}
func (c *Chrome) getArgs() []string {
return []string{
"--no-sandbox",
"--headless",
"--remote-debugging-port=9222",
"--disable-gpu",
"--disable-translate",
"--disable-extensions",
"--disable-background-networking",
"--safebrowsing-disable-auto-update",
"--disable-sync",
"--disable-default-apps",
"--hide-scrollbars",
"--metrics-recording-only",
"--mute-audio",
"--no-first-run",
}
}
func (c *Chrome) getName() string {
return "google-chrome-stable"
}
func (c *Chrome) getFullname() string {
return "Chrome headless"
}
func (c *Chrome) isViable() bool {
// check if Chrome is correctly running.
devt := devtool.New("http://127.0.0.1:9222")
_, err := devt.Create(context.TODO())
return err == nil
}
func (c *Chrome) warmup() {
notify.Println(fmt.Sprintf("warming-up %s", c.getFullname()))
time.Sleep(5 * time.Second)
}
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Process(new(Chrome))
)

View File

@@ -0,0 +1,19 @@
package pm2
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestChromeLaunch(t *testing.T) {
p := &Chrome{}
err := p.Launch()
require.Nil(t, err)
}
func TestChromeShutdown(t *testing.T) {
p := &Chrome{}
err := p.Shutdown()
require.Nil(t, err)
}

12
internal/pkg/pm2/doc.go Normal file
View File

@@ -0,0 +1,12 @@
/*
Package pm2 facilitates starting external
processes on which our API depends.
For instance, it starts Chrome headless and
unoconv listener with PM2.
The PM2 process manager launch those processes and keep
them running in the background. If for some reason they
crash, it will also restart them.
*/
package pm2

71
internal/pkg/pm2/pm2.go Normal file
View File

@@ -0,0 +1,71 @@
package pm2
import (
"fmt"
"os/exec"
"github.com/thecodingmachine/gotenberg/internal/pkg/notify"
)
// Process is a type that can launch or
// shutdown a process with PM2.
type Process interface {
Launch() error
Shutdown() error
getArgs() []string
getName() string
getFullname() string
isViable() bool
warmup()
}
const maxRestartAttempts int = 5
var humanNames = map[string]string{
"start": "started",
"restart": "restarted",
"stop": "stopped",
}
func launch(p Process) error {
if err := run(p, "start"); err != nil {
return err
}
p.warmup()
if !p.isViable() {
attempts := 0
for attempts < maxRestartAttempts && !p.isViable() {
run(p, "restart")
p.warmup()
attempts++
}
if !p.isViable() {
return fmt.Errorf("failed to launch %s", p.getFullname())
}
}
return nil
}
func shutdown(p Process) error {
return run(p, "stop")
}
func run(p Process, cmdName string) error {
cmdArgs := []string{
cmdName,
p.getName(),
}
if cmdName == "start" {
cmdArgs = append(cmdArgs, "--interpreter none", "--")
cmdArgs = append(cmdArgs, p.getArgs()...)
}
cmd := exec.Command(
"pm2",
cmdArgs...,
)
if err := cmd.Start(); err != nil {
return fmt.Errorf("%s %s with PM2: %v", cmdName, p.getFullname(), err)
}
notify.Println(fmt.Sprintf("%s %s with PM2", p.getFullname(), humanNames[cmdName]))
return nil
}

View File

@@ -0,0 +1,47 @@
package pm2
// Unoconv facilitates starting or shutting down
// unoconv listener with PM2.
type Unoconv struct{}
// Launch starts unoconv listener with PM2.
func (u *Unoconv) Launch() error {
return launch(u)
}
// Shutdown stops unoconv listener and
// removes it from the list of PM2
// processes.
func (u *Unoconv) Shutdown() error {
return shutdown(u)
}
func (u *Unoconv) getArgs() []string {
return []string{
"--listener",
"--verbose",
}
}
func (u *Unoconv) getName() string {
return "unoconv"
}
func (u *Unoconv) getFullname() string {
return "unoconv listener"
}
func (u *Unoconv) isViable() bool {
// TODO find a way to check if
// unoconv is correctly started?
return true
}
func (u *Unoconv) warmup() {
// let's do nothing.
}
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = Process(new(Unoconv))
)

View File

@@ -0,0 +1,19 @@
package pm2
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestUnoconvLaunch(t *testing.T) {
p := &Unoconv{}
err := p.Launch()
require.Nil(t, err)
}
func TestUnoconvShutdown(t *testing.T) {
p := &Unoconv{}
err := p.Shutdown()
require.Nil(t, err)
}