Get docker running properly and add more logging
This commit is contained in:
parent
06b5694125
commit
1bdfc9e612
71
Dockerfile
71
Dockerfile
|
|
@ -1,19 +1,8 @@
|
||||||
# This stage builds the mermaidcli executable.
|
|
||||||
FROM node:12.12.0-buster as node
|
|
||||||
|
|
||||||
WORKDIR /root
|
|
||||||
|
|
||||||
# copy the mermaidcli node package into the container and install
|
|
||||||
COPY ./mermaidcli/* .
|
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
|
|
||||||
# This stage builds the go executable.
|
# This stage builds the go executable.
|
||||||
FROM golang:1.13-buster as go
|
FROM golang:1.13-buster as go
|
||||||
|
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
COPY . .
|
COPY ./ ./
|
||||||
|
|
||||||
RUN go build -o bin/app cmd/app/main.go
|
RUN go build -o bin/app cmd/app/main.go
|
||||||
|
|
||||||
|
|
@ -21,14 +10,66 @@ RUN go build -o bin/app cmd/app/main.go
|
||||||
# Final stage that will be pushed.
|
# Final stage that will be pushed.
|
||||||
FROM debian:buster-slim
|
FROM debian:buster-slim
|
||||||
|
|
||||||
|
FROM node:12.12.0-buster-slim as node
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
|
||||||
|
# copy the mermaidcli node package into the container and install
|
||||||
|
COPY ./mermaidcli/* ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
RUN apt update 2>/dev/null && \
|
RUN apt update 2>/dev/null && \
|
||||||
apt install -y --no-install-recommends \
|
apt install -y --no-install-recommends \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
|
gconf-service \
|
||||||
|
libasound2 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libc6 \
|
||||||
|
libcairo2 \
|
||||||
|
libcups2 \
|
||||||
|
libdbus-1-3 \
|
||||||
|
libexpat1 \
|
||||||
|
libfontconfig1 \
|
||||||
|
libgcc1 \
|
||||||
|
libgconf-2-4 \
|
||||||
|
libgdk-pixbuf2.0-0 \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libgtk-3-0 \
|
||||||
|
libnspr4 \
|
||||||
|
libpango-1.0-0 \
|
||||||
|
libpangocairo-1.0-0 \
|
||||||
|
libstdc++6 \
|
||||||
|
libx11-6 \
|
||||||
|
libx11-xcb1 \
|
||||||
|
libxcb1 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxcursor1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxext6 \
|
||||||
|
libxfixes3 \
|
||||||
|
libxi6 \
|
||||||
|
libxrandr2 \
|
||||||
|
libxrender1 \
|
||||||
|
libxss1 \
|
||||||
|
libxtst6 \
|
||||||
|
ca-certificates \
|
||||||
|
fonts-liberation \
|
||||||
|
libappindicator1 \
|
||||||
|
libnss3 \
|
||||||
|
lsb-release \
|
||||||
|
xdg-utils \
|
||||||
|
wget \
|
||||||
2>/dev/null
|
2>/dev/null
|
||||||
|
|
||||||
COPY --from=node /root/node_modules/.bin/mmdc ./mermaidcli
|
|
||||||
COPY --from=go /root/bin/app ./app
|
COPY --from=go /root/bin/app ./app
|
||||||
|
|
||||||
# We should now have all the required dependencies to build the proto files.
|
RUN mkdir -p ./in
|
||||||
CMD ["./app", "--mermaid=./mermaidcli"]
|
RUN mkdir -p ./out
|
||||||
|
RUN chmod 0777 ./in
|
||||||
|
RUN chmod 0777 ./out
|
||||||
|
|
||||||
|
CMD ["./app", "--mermaid=./node_modules/.bin/mmdc", "--in=./in", "--out=./out", "--puppeteer=./puppeteer-config.json"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,15 @@ While this currently serves the diagrams via HTTP, it could easily be manipulate
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
Run the container:
|
||||||
|
```
|
||||||
|
docker run -d --name mermaid-server -p 80:80 tomwright/mermaid-server:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manually as a go command
|
||||||
|
|
||||||
Start the HTTP server:
|
Start the HTTP server:
|
||||||
```
|
```
|
||||||
go run cmd/app/main.go --mermaid=./mermaidcli/node_modules/.bin/mmdc --in=./in --out=./out
|
go run cmd/app/main.go --mermaid=./mermaidcli/node_modules/.bin/mmdc --in=./in --out=./out
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tomwright/mermaid-server/internal"
|
"github.com/tomwright/mermaid-server/internal"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
@ -59,6 +60,7 @@ func main() {
|
||||||
mermaid := flag.String("mermaid", "", "The full path to the mermaidcli executable.")
|
mermaid := flag.String("mermaid", "", "The full path to the mermaidcli executable.")
|
||||||
in := flag.String("in", "", "Directory to store input files.")
|
in := flag.String("in", "", "Directory to store input files.")
|
||||||
out := flag.String("out", "", "Directory to store output files.")
|
out := flag.String("out", "", "Directory to store output files.")
|
||||||
|
puppeteer := flag.String("puppeteer", "", "Full path to optional puppeteer config.")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *mermaid == "" {
|
if *mermaid == "" {
|
||||||
|
|
@ -77,7 +79,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cache := internal.NewDiagramCache()
|
cache := internal.NewDiagramCache()
|
||||||
generator := internal.NewGenerator(cache, *mermaid, *in, *out)
|
generator := internal.NewGenerator(cache, *mermaid, *in, *out, *puppeteer)
|
||||||
|
|
||||||
httpHandler := internal.GenerateHTTPHandler(generator)
|
httpHandler := internal.GenerateHTTPHandler(generator)
|
||||||
|
|
||||||
|
|
@ -88,11 +90,11 @@ func main() {
|
||||||
Addr: ":80",
|
Addr: ":80",
|
||||||
Handler: r,
|
Handler: r,
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(os.Stdout, "Listening on address %s", httpServer.Addr)
|
log.Printf("Listening on address %s", httpServer.Addr)
|
||||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "Could not listen for http connections: %s", err)
|
log.Printf("Could not listen for http connections: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(os.Stdout, "Shutdown")
|
log.Printf("Shutdown")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package internal
|
package internal
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// DiagramCache provides the ability to cache diagram results.
|
// DiagramCache provides the ability to cache diagram results.
|
||||||
type DiagramCache interface {
|
type DiagramCache interface {
|
||||||
// Store stores a diagram in the cache.
|
// Store stores a diagram in the cache.
|
||||||
|
|
@ -26,7 +28,7 @@ type inMemoryDiagramCache struct {
|
||||||
func (c *inMemoryDiagramCache) Store(diagram *Diagram) error {
|
func (c *inMemoryDiagramCache) Store(diagram *Diagram) error {
|
||||||
id, err := diagram.ID()
|
id, err := diagram.ID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cannot get diagram ID: %w", err)
|
||||||
}
|
}
|
||||||
c.idToDiagram[id] = diagram
|
c.idToDiagram[id] = diagram
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -36,7 +38,7 @@ func (c *inMemoryDiagramCache) Store(diagram *Diagram) error {
|
||||||
func (c *inMemoryDiagramCache) Has(diagram *Diagram) (bool, error) {
|
func (c *inMemoryDiagramCache) Has(diagram *Diagram) (bool, error) {
|
||||||
id, err := diagram.ID()
|
id, err := diagram.ID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, fmt.Errorf("cannot get diagram ID: %w", err)
|
||||||
}
|
}
|
||||||
if d, ok := c.idToDiagram[id]; ok && d != nil {
|
if d, ok := c.idToDiagram[id]; ok && d != nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -14,42 +16,44 @@ type Generator interface {
|
||||||
Generate(diagram *Diagram) error
|
Generate(diagram *Diagram) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGenerator(cache DiagramCache, mermaidCLIPath string, inPath string, outPath string) Generator {
|
func NewGenerator(cache DiagramCache, mermaidCLIPath string, inPath string, outPath string, puppeteerConfigPath string) Generator {
|
||||||
return &cachingGenerator{
|
return &cachingGenerator{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
mermaidCLIPath: mermaidCLIPath,
|
mermaidCLIPath: mermaidCLIPath,
|
||||||
inPath: inPath,
|
inPath: inPath,
|
||||||
outPath: outPath,
|
outPath: outPath,
|
||||||
|
puppeteerConfigPath: puppeteerConfigPath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cachingGenerator is an implementation of Generator.
|
// cachingGenerator is an implementation of Generator.
|
||||||
type cachingGenerator struct {
|
type cachingGenerator struct {
|
||||||
cache DiagramCache
|
cache DiagramCache
|
||||||
mermaidCLIPath string
|
mermaidCLIPath string
|
||||||
inPath string
|
inPath string
|
||||||
outPath string
|
outPath string
|
||||||
|
puppeteerConfigPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate generates the given diagram.
|
// Generate generates the given diagram.
|
||||||
func (c cachingGenerator) Generate(diagram *Diagram) error {
|
func (c cachingGenerator) Generate(diagram *Diagram) error {
|
||||||
has, err := c.cache.Has(diagram)
|
has, err := c.cache.Has(diagram)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cache.Has failed: %w", err)
|
||||||
}
|
}
|
||||||
if has {
|
if has {
|
||||||
cached, err := c.cache.Get(diagram)
|
cached, err := c.cache.Get(diagram)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cache.Get failed: %w", err)
|
||||||
}
|
}
|
||||||
*diagram = *cached
|
*diagram = *cached
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := c.generate(diagram); err != nil {
|
if err := c.generate(diagram); err != nil {
|
||||||
return err
|
return fmt.Errorf("cachingGenerater.generate failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := c.cache.Store(diagram); err != nil {
|
if err := c.cache.Store(diagram); err != nil {
|
||||||
return err
|
return fmt.Errorf("cache.Store failed: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -58,35 +62,41 @@ func (c cachingGenerator) Generate(diagram *Diagram) error {
|
||||||
func (c cachingGenerator) generate(diagram *Diagram) error {
|
func (c cachingGenerator) generate(diagram *Diagram) error {
|
||||||
id, err := diagram.ID()
|
id, err := diagram.ID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("cannot get diagram ID: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
has, err := c.cache.Has(diagram)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if has {
|
|
||||||
cached, err := c.cache.Get(diagram)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*diagram = *cached
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inPath := fmt.Sprintf("%s/%s.mmd", c.inPath, id)
|
inPath := fmt.Sprintf("%s/%s.mmd", c.inPath, id)
|
||||||
outPath := fmt.Sprintf("%s/%s.svg", c.outPath, id)
|
outPath := fmt.Sprintf("%s/%s.svg", c.outPath, id)
|
||||||
|
|
||||||
if err := ioutil.WriteFile(inPath, diagram.description, 0644); err != nil {
|
if err := ioutil.WriteFile(inPath, diagram.description, 0644); err != nil {
|
||||||
return err
|
return fmt.Errorf("could not write to input file [%s]: %w", inPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(c.mermaidCLIPath, "-i", inPath, "-o", outPath)
|
_, err = os.Stat(c.mermaidCLIPath)
|
||||||
var stdOut bytes.Buffer
|
if os.IsNotExist(err) {
|
||||||
cmd.Stdout = bufio.NewWriter(&stdOut)
|
return fmt.Errorf("mermaid executable does not exist: %w", err)
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return fmt.Errorf("%w: %s", err, string(stdOut.Bytes()))
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not stat mermaid executable: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-i", inPath,
|
||||||
|
"-o", outPath,
|
||||||
|
}
|
||||||
|
if c.puppeteerConfigPath != "" {
|
||||||
|
args = append(args, "-p", c.puppeteerConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(c.mermaidCLIPath, args...)
|
||||||
|
var stdOut bytes.Buffer
|
||||||
|
var stdErr bytes.Buffer
|
||||||
|
cmd.Stdout = bufio.NewWriter(&stdOut)
|
||||||
|
cmd.Stderr = bufio.NewWriter(&stdErr)
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed when executing mermaid: %w: %s: %s", err, string(stdOut.Bytes()), string(stdErr.Bytes()))
|
||||||
|
}
|
||||||
|
log.Printf("generated with output: %s: %s", string(stdOut.Bytes()), string(stdErr.Bytes()))
|
||||||
|
|
||||||
diagram.Output = outPath
|
diagram.Output = outPath
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -30,6 +31,8 @@ func writeSVG(rw http.ResponseWriter, data []byte, status int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeErr(rw http.ResponseWriter, err error, status int) {
|
func writeErr(rw http.ResponseWriter, err error, status int) {
|
||||||
|
log.Printf("[%d] %s", status, err)
|
||||||
|
|
||||||
writeJSON(rw, map[string]interface{}{
|
writeJSON(rw, map[string]interface{}{
|
||||||
"error": err,
|
"error": err,
|
||||||
}, status)
|
}, status)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
"--no-sandbox"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue