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.
|
||||
FROM golang:1.13-buster as go
|
||||
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
COPY ./ ./
|
||||
|
||||
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.
|
||||
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
|
||||
RUN apt update 2>/dev/null && \
|
||||
apt install -y --no-install-recommends \
|
||||
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
|
||||
|
||||
COPY --from=node /root/node_modules/.bin/mmdc ./mermaidcli
|
||||
COPY --from=go /root/bin/app ./app
|
||||
|
||||
# We should now have all the required dependencies to build the proto files.
|
||||
CMD ["./app", "--mermaid=./mermaidcli"]
|
||||
RUN mkdir -p ./in
|
||||
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
|
||||
|
||||
### 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:
|
||||
```
|
||||
go run cmd/app/main.go --mermaid=./mermaidcli/node_modules/.bin/mmdc --in=./in --out=./out
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"github.com/tomwright/mermaid-server/internal"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
|
@ -59,6 +60,7 @@ func main() {
|
|||
mermaid := flag.String("mermaid", "", "The full path to the mermaidcli executable.")
|
||||
in := flag.String("in", "", "Directory to store input files.")
|
||||
out := flag.String("out", "", "Directory to store output files.")
|
||||
puppeteer := flag.String("puppeteer", "", "Full path to optional puppeteer config.")
|
||||
flag.Parse()
|
||||
|
||||
if *mermaid == "" {
|
||||
|
|
@ -77,7 +79,7 @@ func main() {
|
|||
}
|
||||
|
||||
cache := internal.NewDiagramCache()
|
||||
generator := internal.NewGenerator(cache, *mermaid, *in, *out)
|
||||
generator := internal.NewGenerator(cache, *mermaid, *in, *out, *puppeteer)
|
||||
|
||||
httpHandler := internal.GenerateHTTPHandler(generator)
|
||||
|
||||
|
|
@ -88,11 +90,11 @@ func main() {
|
|||
Addr: ":80",
|
||||
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 {
|
||||
_, _ = 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)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(os.Stdout, "Shutdown")
|
||||
log.Printf("Shutdown")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package internal
|
||||
|
||||
import "fmt"
|
||||
|
||||
// DiagramCache provides the ability to cache diagram results.
|
||||
type DiagramCache interface {
|
||||
// Store stores a diagram in the cache.
|
||||
|
|
@ -26,7 +28,7 @@ type inMemoryDiagramCache struct {
|
|||
func (c *inMemoryDiagramCache) Store(diagram *Diagram) error {
|
||||
id, err := diagram.ID()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("cannot get diagram ID: %w", err)
|
||||
}
|
||||
c.idToDiagram[id] = diagram
|
||||
return nil
|
||||
|
|
@ -36,7 +38,7 @@ func (c *inMemoryDiagramCache) Store(diagram *Diagram) error {
|
|||
func (c *inMemoryDiagramCache) Has(diagram *Diagram) (bool, error) {
|
||||
id, err := diagram.ID()
|
||||
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 {
|
||||
return true, nil
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
|
|
@ -14,42 +16,44 @@ type Generator interface {
|
|||
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{
|
||||
cache: cache,
|
||||
mermaidCLIPath: mermaidCLIPath,
|
||||
inPath: inPath,
|
||||
outPath: outPath,
|
||||
cache: cache,
|
||||
mermaidCLIPath: mermaidCLIPath,
|
||||
inPath: inPath,
|
||||
outPath: outPath,
|
||||
puppeteerConfigPath: puppeteerConfigPath,
|
||||
}
|
||||
}
|
||||
|
||||
// cachingGenerator is an implementation of Generator.
|
||||
type cachingGenerator struct {
|
||||
cache DiagramCache
|
||||
mermaidCLIPath string
|
||||
inPath string
|
||||
outPath string
|
||||
cache DiagramCache
|
||||
mermaidCLIPath string
|
||||
inPath string
|
||||
outPath string
|
||||
puppeteerConfigPath string
|
||||
}
|
||||
|
||||
// Generate generates the given diagram.
|
||||
func (c cachingGenerator) Generate(diagram *Diagram) error {
|
||||
has, err := c.cache.Has(diagram)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("cache.Has failed: %w", err)
|
||||
}
|
||||
if has {
|
||||
cached, err := c.cache.Get(diagram)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("cache.Get failed: %w", err)
|
||||
}
|
||||
*diagram = *cached
|
||||
return 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 {
|
||||
return err
|
||||
return fmt.Errorf("cache.Store failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -58,35 +62,41 @@ func (c cachingGenerator) Generate(diagram *Diagram) error {
|
|||
func (c cachingGenerator) generate(diagram *Diagram) error {
|
||||
id, err := diagram.ID()
|
||||
if err != nil {
|
||||
return 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
|
||||
return fmt.Errorf("cannot get diagram ID: %w", err)
|
||||
}
|
||||
|
||||
inPath := fmt.Sprintf("%s/%s.mmd", c.inPath, id)
|
||||
outPath := fmt.Sprintf("%s/%s.svg", c.outPath, id)
|
||||
|
||||
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)
|
||||
var stdOut bytes.Buffer
|
||||
cmd.Stdout = bufio.NewWriter(&stdOut)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("%w: %s", err, string(stdOut.Bytes()))
|
||||
_, err = os.Stat(c.mermaidCLIPath)
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("mermaid executable does not exist: %w", err)
|
||||
}
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
|
@ -30,6 +31,8 @@ func writeSVG(rw http.ResponseWriter, data []byte, status int) {
|
|||
}
|
||||
|
||||
func writeErr(rw http.ResponseWriter, err error, status int) {
|
||||
log.Printf("[%d] %s", status, err)
|
||||
|
||||
writeJSON(rw, map[string]interface{}{
|
||||
"error": err,
|
||||
}, status)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"args": [
|
||||
"--no-sandbox"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue