Skip to content

Commit

Permalink
Support watch scripts for serve
Browse files Browse the repository at this point in the history
  • Loading branch information
malcolmholmes committed Aug 23, 2024
1 parent b9e382f commit 04a20da
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 18 deletions.
1 change: 1 addition & 0 deletions cmd/grr/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Opts struct {
ProxyPort int
CanSave bool
Watch bool
WatchScript string
}

func configPathCmd() *cli.Command {
Expand Down
18 changes: 13 additions & 5 deletions cmd/grr/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,16 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
}

resourcesPath := ""
if len(args) > 0 {
resourcesPath = args[0]
}
watchPaths := []string{resourcesPath}
if len(args) > 1 {
watchPaths = args[1:]
if opts.WatchScript != "" {
watchPaths = args
} else {
if len(args) > 0 {
resourcesPath = args[0]
}
if len(args) > 1 {
watchPaths = args[1:]
}
}

targets := currentContext.GetTargets(opts.Targets)
Expand All @@ -405,6 +409,9 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
server.SetFormatting(onlySpec, format)
if opts.Watch {
server.Watch(watchPaths)
if opts.WatchScript != "" {
server.WatchScript(opts.WatchScript)
}
}
if opts.OpenBrowser {
server.OpenBrowser()
Expand All @@ -414,6 +421,7 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
cmd.Flags().BoolVarP(&opts.Watch, "watch", "w", false, "Watch filesystem for changes")
cmd.Flags().BoolVarP(&opts.OpenBrowser, "open-browser", "b", false, "Open Grizzly in default browser")
cmd.Flags().IntVarP(&opts.ProxyPort, "port", "p", 8080, "Port on which the server will listen")
cmd.Flags().StringVarP(&opts.WatchScript, "script", "S", "", "Script to execute on filesystem change")
cmd = initialiseOnlySpec(cmd, &opts)
return initialiseCmd(cmd, &opts)
}
Expand Down
10 changes: 2 additions & 8 deletions docs/content/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,13 @@ grr serve -w examples/grr.jsonnet examples/*.*sonnet examples/vendor
### Reviewing changes to code in other languages in Grafana
The [Grafana Foundation SDK](https://github.com/grafana/grafana-foundation-sdk) provides libraries in a
range of languages that can be used to render Grafana dashboards. Watching changes to these with Grizzly
is a two stage process, currently requiring an additional tool to watch for changes to source code and
render your dashboard(s) to files. One such tool is [entr](https://github.com/eradman/entr), which can be
used like so (with the Foundation SDK's TypeScript support):
is also possible.

```
git clone https://github.com/grafana/grafana-foundation-sdk
cd grafana-foundation-sdk/examples/typescript/red-method
npm install
find . | entr -s 'npm run -s dev > ts.json'
```
Then, in another window:
```
grr serve -w ts.json
grr serve -w -S 'npm run -s dev' .
```
Finally, open the Grizzly server at [http://localhost:8080](http://localhost:8080) and select the Red
Method dashboard.
Expand Down
14 changes: 14 additions & 0 deletions examples/array-of-resources.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
local dashboard(uid, title) = {
uid: uid,
title: title,
tags: ['templated'],
timezone: 'browser',
schemaVersion: 17,
panels: [],
};

[
dashboard("dashboard-1", "Dashboard 1"),
dashboard("dashboard-2", "Dashboard 2"),
dashboard("dashboard-3", "Dashboard 3"),
]
72 changes: 67 additions & 5 deletions pkg/grizzly/server.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package grizzly

import (
"bytes"
"errors"
"fmt"
"io/fs"
"net/http"
"net/http/httputil"
"os"
"os/exec"

"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
Expand All @@ -31,6 +33,7 @@ type Server struct {
UserAgent string
ResourcePath string
WatchPaths []string
watchScript string
OnlySpec bool
OutputFormat string
watch bool
Expand Down Expand Up @@ -85,6 +88,10 @@ func (s *Server) Watch(watchPaths []string) {
s.WatchPaths = watchPaths
}

func (s *Server) WatchScript(script string) {
s.watchScript = script
}

func (s *Server) SetFormatting(onlySpec bool, outputFormat string) {
s.OnlySpec = onlySpec
s.OutputFormat = outputFormat
Expand Down Expand Up @@ -174,10 +181,19 @@ func (s *Server) Start() error {
r.Get("/grizzly/{kind}/{name}", s.IframeHandler)
r.Get("/api/live/ws", livereload.LiveReloadHandlerFunc(upgrader))

if _, err := s.ParseResources(s.ResourcePath); err != nil {
if s.watchScript != "" {
var b []byte
b, err = s.executeWatchScript()
if err != nil {
return err
}
_, err = s.ParseBytes(b)
} else {
_, err = s.ParseResources(s.ResourcePath)
}
if err != nil {
fmt.Print(err)
}

if s.openBrowser {
browser, err := NewBrowserInterface(s.Registry, s.ResourcePath, s.port)
if err != nil {
Expand Down Expand Up @@ -217,6 +233,26 @@ func (s *Server) ParseResources(resourcePath string) (Resources, error) {
return resources, err
}

func (s *Server) ParseBytes(b []byte) (Resources, error) {
f, err := os.CreateTemp(".", fmt.Sprintf("*.%s", s.OutputFormat))
if err != nil {
return Resources{}, err
}
defer os.Remove(f.Name())
_, err = f.Write(b)
if err != nil {
return Resources{}, err
}
err = f.Close()
if err != nil {
return Resources{}, err
}
resources, err := s.parser.Parse(f.Name(), s.parserOpts)
s.parserErr = err
s.Resources.Merge(resources)
return resources, err
}

func (s *Server) URL(path string) string {
if len(path) == 0 || path[0] != '/' {
path = "/" + path
Expand All @@ -226,10 +262,19 @@ func (s *Server) URL(path string) string {
}

func (s *Server) updateWatchedResource(name string) error {
if !s.parser.Accept(name) {
return nil
var resources Resources
var err error

if s.watchScript != "" {
var b []byte
b, err = s.executeWatchScript()
if err != nil {
return err
}
resources, err = s.ParseBytes(b)
} else {
resources, err = s.ParseResources(s.ResourcePath)
}
resources, err := s.ParseResources(s.ResourcePath)
if errors.As(err, &UnrecognisedFormatError{}) {
uerr := err.(UnrecognisedFormatError)
log.Printf("Skipping %s", uerr.File)
Expand All @@ -239,6 +284,7 @@ func (s *Server) updateWatchedResource(name string) error {
log.Error("Error: ", err)
return err
}

for _, resource := range resources.AsList() {
handler, err := s.Registry.GetHandler(resource.Kind())
if err != nil {
Expand All @@ -257,6 +303,22 @@ func (s *Server) updateWatchedResource(name string) error {
return nil
}

func (s *Server) executeWatchScript() ([]byte, error) {
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command("sh", "-c", s.watchScript)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return nil, err
}
if stderr.Len() > 0 {
log.Errorf("%s", stderr.String())
}
return stdout.Bytes(), nil
}

func (s *Server) blockHandler(response string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
Expand Down

0 comments on commit 04a20da

Please sign in to comment.