feat(catalog): support exporting catalog to different output formats
This commit is contained in:
parent
8e11da51d5
commit
4bb39c9199
33
README.md
33
README.md
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
_The minimal, declarative service catalog._
|
_The minimal, declarative service catalog._
|
||||||
|
|
||||||
Pronounced "MC" as in the master of ceremonies.
|
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
In the last three jobs I've worked at, it's always been a hassle trying to locate the various dashboards, documentation,
|
In the last three jobs I've worked at, it's always been a hassle trying to locate the various dashboards, documentation,
|
||||||
@ -60,11 +58,38 @@ func main() {
|
|||||||
|
|
||||||
Once you've built your catalog, you can easily run a landing page by executing the catalog file.
|
Once you've built your catalog, you can easily run a landing page by executing the catalog file.
|
||||||
|
|
||||||
```
|
```sh
|
||||||
$ go run ./catalog.go
|
$ go run ./catalog.go
|
||||||
```
|
```
|
||||||
|
|
||||||
This starts a web server for you to interact with on `localhost:8080`. If `:8080` is already in use, you can configure
|
This starts a web server for you to interact with on `localhost:8080`. If `:8080` is already in use, you can configure
|
||||||
the bind address by passing the `-bind_address` flag with the desired host and port.
|
the bind address by passing the `-bind_address` flag with the desired host and port.
|
||||||
|
|
||||||
![Screenshot](screenshot.png)
|
<center>
|
||||||
|
<img src="screenshot.png" alt="Screenshot" width="72%"/>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
### Exporting your catalog
|
||||||
|
|
||||||
|
Instead of needing to compile a binary or host your catalog using `go run`, you can export your catalog to HTML or JSON.
|
||||||
|
This makes it easy to drop into existing self-host platforms or leverage with other popular systems.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go run ./catalog.go -output html > index.html
|
||||||
|
$ go run ./catalog.go -output json > catalog.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Protecting your catalog using oauth-proxy
|
||||||
|
|
||||||
|
Regardless of how you host your catalog, you'll likely want to protect access to it. An easy way to do this is using the
|
||||||
|
[oauth-proxy][] project. This project provides common OAuth2 client functionality to any project, making it easy to
|
||||||
|
require authentication in order to access a system / project.
|
||||||
|
|
||||||
|
<!-- TODO: write up guide and link to it from here -->
|
||||||
|
|
||||||
|
Until I have more of a concrete guide, you can follow my setup [here](https://github.com/mjpitz/mjpitz/blob/main/infra/helm/catalog/values.yaml).
|
||||||
|
A simple analogy to this deployment would be a docker compose file with two services, one for the oauth-proxy and the
|
||||||
|
other for the catalog (bound to 127.0.0.1). Using the new `-output` functionality, this deployment could definitely
|
||||||
|
be simplified.
|
||||||
|
|
||||||
|
[oauth-proxy]: https://oauth2-proxy.github.io/oauth2-proxy
|
||||||
|
@ -6,10 +6,13 @@ package catalog
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mjpitz/emc/catalog/service"
|
"github.com/mjpitz/emc/catalog/service"
|
||||||
@ -18,6 +21,11 @@ import (
|
|||||||
//go:embed index.html.tpl
|
//go:embed index.html.tpl
|
||||||
var catalog string
|
var catalog string
|
||||||
|
|
||||||
|
var funcs = map[string]any{
|
||||||
|
"mod": func(mod, v int) int { return v % mod },
|
||||||
|
"eq": func(exp, act int) bool { return exp == act },
|
||||||
|
}
|
||||||
|
|
||||||
// Spec defines the requirements for hosting a catalog.
|
// Spec defines the requirements for hosting a catalog.
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
Services []service.Spec
|
Services []service.Spec
|
||||||
@ -36,39 +44,44 @@ func Service(label string, options ...service.Option) Option {
|
|||||||
// Serve provides command line functionality for running the service catalog.
|
// Serve provides command line functionality for running the service catalog.
|
||||||
func Serve(options ...Option) {
|
func Serve(options ...Option) {
|
||||||
addr := flag.String("bind_address", "127.0.0.1:8080", "the address the service should bind to when serving content")
|
addr := flag.String("bind_address", "127.0.0.1:8080", "the address the service should bind to when serving content")
|
||||||
|
output := flag.String("output", "", "set to output the catalog to stdout (valid html, json)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
t := template.Must(template.New("catalog").
|
|
||||||
Funcs(map[string]any{
|
|
||||||
"mod": func(mod, v int) int {
|
|
||||||
return v % mod
|
|
||||||
},
|
|
||||||
"eq": func(exp, act int) bool {
|
|
||||||
return exp == act
|
|
||||||
},
|
|
||||||
}).
|
|
||||||
Parse(catalog))
|
|
||||||
|
|
||||||
spec := Spec{}
|
spec := Spec{}
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
opt(&spec)
|
opt(&spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
html := bytes.NewBuffer(nil)
|
buffer := bytes.NewBuffer(nil)
|
||||||
err := t.Execute(html, spec)
|
var err error
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
switch {
|
||||||
|
case output == nil || *output == "" || *output == "html":
|
||||||
|
err = template.Must(template.New("catalog").Funcs(funcs).Parse(catalog)).Execute(buffer, spec)
|
||||||
|
case *output == "json":
|
||||||
|
err = json.NewEncoder(buffer).Encode(spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to render output", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != nil && *output != "" {
|
||||||
|
_, err = io.Copy(os.Stdout, bytes.NewReader(buffer.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to write buffer to stdout", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeContent(w, r, "", start, bytes.NewReader(html.Bytes()))
|
http.ServeContent(w, r, "", start, bytes.NewReader(buffer.Bytes()))
|
||||||
})
|
})
|
||||||
|
|
||||||
log.Printf("serving on %s\n", *addr)
|
log.Printf("serving on %s\n", *addr)
|
||||||
err = http.ListenAndServe(*addr, http.DefaultServeMux)
|
err = http.ListenAndServe(*addr, http.DefaultServeMux)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal("failed to serve content", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user