commit 8e4d7ddd72d3d3877d2a7ae40481e24722220a22 Author: Mya Pitzeruse Date: Sat May 21 08:32:18 2022 -0500 port to new repo diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3a6da02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Mya Pitzeruse + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1b009f --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# emc + +_The minimally declarative service catalog._ + +Pronounced "MC" as in the master of ceremonies. + +## Background + +In the last three jobs I've worked at, it's always been a hassle to trying to locate the various dashboards, +documentation, and support for a given project. I joined effx to try and help address just that. As I've been spinning +up a new cluster, I found myself wanting a landing page for the systems that I use regularly. + +## Building your catalog + +The `emc` service catalog is defined using a simple Golang script. This makes it easy for engineers to drop in their +own functionality for rendering links, link groups, or services. For an example, see the provided `grafana` package +which includes several of my personal dashboards for different systems. + +```go +// catalog.go + +//go:build ignore +// +build ignore + +package main + +import ( + "github.com/mjpitz/emc/catalog" + "github.com/mjpitz/emc/catalog/grafana" + "github.com/mjpitz/emc/catalog/linkgroup" + "github.com/mjpitz/emc/catalog/service" +) + +func main() { + catalog.Serve( + catalog.Service( + "Drone", + service.LogoURL("https://path/to/drone-logo.png"), + service.URL("https://drone.example.com"), + service.Description("Drone is a self-service Continuous Integration platform for busy development teams."), + service.Metadata("Contact", "drone@example.com"), + service.LinkGroup( + "Dashboards", + linkgroup.Link("Drone", grafana.Drone("cicd", "drone")), + linkgroup.Link("Golang", grafana.Golang("cicd", "drone")), + linkgroup.Link("Litestream", grafana.Litestream("cicd", "drone")), + linkgroup.Link("Redis Queue", grafana.Redis("cicd", "drone-redis-queue")), + ), + service.LinkGroup( + "Documentation", + linkgroup.Link("docs.drone.io", "https://docs.drone.io/"), + ), + ), + // ... + ) +} +``` + +## Hosting your catalog + +Once you've built your catalog, you can easily run a landing page by executing the catalog file. + +``` +$ 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 +the bind address be passing the `-bind_address` flag with the desired host and port. + +![Screenshot](screenshot.png) diff --git a/catalog/grafana/dsl.go b/catalog/grafana/dsl.go new file mode 100644 index 0000000..1526e5d --- /dev/null +++ b/catalog/grafana/dsl.go @@ -0,0 +1,78 @@ +// Copyright (c) 2022 Mya Pitzeruse +// The MIT License (MIT) + +package grafana + +import ( + "fmt" + "net/url" +) + +// Grafana provides functionality for rendering links to dashboards. This implementation provides some shorthand calls +// that assume the use of my personal dashboards (provided at https://github.com/mjpitz/mjpitz/tree/main/monitoring). +type Grafana string + +// Link renders a link to a grafana deployment given a dashboard id, namespace, and job (assumes a kubernetes based +// deployment). +func (g Grafana) Link(dashboard, namespace, job string) string { + return fmt.Sprintf( + "%s/d/%s?var-namespace=%s&var-job=%s", + g, url.PathEscape(dashboard), url.QueryEscape(namespace), url.QueryEscape(job), + ) +} + +func (g Grafana) Maddy(namespace, job string) string { + return g.Link("82a7b6b2c9516ef16c08616edc8a90c1", namespace, job) +} + +func (g Grafana) Redis(namespace, job string) string { + return g.Link("36cf4a03d9f16d8a4221fecbbd2ff5c6", namespace, job) +} + +func (g Grafana) Drone(namespace, job string) string { + return g.Link("2c241b4cea8d493ef632bf33b10d04cf", namespace, job) +} + +func (g Grafana) Litestream(namespace, job string) string { + return g.Link("bf44e72e619451e2c85cda80fe17b28b", namespace, job) +} + +func (g Grafana) Golang(namespace, job string) string { + return g.Link("8cd750f455ca9fc93a465fd9a34993cc", namespace, job) +} + +func (g Grafana) Gitea(namespace, job string) string { + return g.Link("354a485fe64f93ea707a7f6e061ff71b", namespace, job) +} + +// default instance + +var grafana = Grafana("https://grafana.pitz.tech") + +func Link(dashboard, namespace, job string) string { + return grafana.Link(dashboard, namespace, job) +} + +func Maddy(namespace, job string) string { + return grafana.Maddy(namespace, job) +} + +func Redis(namespace, job string) string { + return grafana.Redis(namespace, job) +} + +func Drone(namespace, job string) string { + return grafana.Drone(namespace, job) +} + +func Litestream(namespace, job string) string { + return grafana.Litestream(namespace, job) +} + +func Golang(namespace, job string) string { + return grafana.Golang(namespace, job) +} + +func Gitea(namespace, job string) string { + return grafana.Gitea(namespace, job) +} diff --git a/catalog/index.html.tpl b/catalog/index.html.tpl new file mode 100644 index 0000000..054e4a6 --- /dev/null +++ b/catalog/index.html.tpl @@ -0,0 +1,132 @@ + + + + + Service Catalog + + + + + +
+

Service Catalog

+ {{- range $service := .Services }} +
+
+
+
+ {{- if $service.LogoURL }} + + {{- end }} + +

{{ $service.Label }}

+
+ + {{- if $service.URL }} +

{{ $service.URL }}

+ {{- end }} + + {{- if $service.Description }} +

{{ $service.Description }}

+ {{- end }} + + {{- range $key, $value := $service.Metadata }} + + {{- end }} +
+ +
+ {{- range $group := $service.LinkGroups }} + + {{- end }} +
+
+
+ {{- end }} +
+ + \ No newline at end of file diff --git a/catalog/link/dsl.go b/catalog/link/dsl.go new file mode 100644 index 0000000..6d396d7 --- /dev/null +++ b/catalog/link/dsl.go @@ -0,0 +1,18 @@ +// Copyright (c) 2022 Mya Pitzeruse +// The MIT License (MIT) + +package link + +// New constructs a link from the given label and url. +func New(label, url string) Spec { + return Spec{ + Label: label, + URL: url, + } +} + +// Spec defines the elements needed to render a link. +type Spec struct { + Label string + URL string +} diff --git a/catalog/linkgroup/dsl.go b/catalog/linkgroup/dsl.go new file mode 100644 index 0000000..c3b1437 --- /dev/null +++ b/catalog/linkgroup/dsl.go @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Mya Pitzeruse +// The MIT License (MIT) + +package linkgroup + +import ( + "github.com/mjpitz/emc/catalog/link" +) + +func New(label string, options ...Option) Spec { + spec := Spec{ + Label: label, + } + + for _, opt := range options { + opt(&spec) + } + + return spec +} + +// Option defines an optional component of the spec. +type Option func(spec *Spec) + +// Spec defines the elements needed to render a link group. +type Spec struct { + Label string + Links []link.Spec +} + +func Link(label, url string) Option { + return func(spec *Spec) { + spec.Links = append(spec.Links, link.New(label, url)) + } +} diff --git a/catalog/serve.go b/catalog/serve.go new file mode 100644 index 0000000..4e5eb96 --- /dev/null +++ b/catalog/serve.go @@ -0,0 +1,65 @@ +// Copyright (C) 2022 Mya Pitzeruse +// The MIT License (MIT) + +package catalog + +import ( + "bytes" + _ "embed" + "flag" + "html/template" + "log" + "net/http" + "time" + + "github.com/mjpitz/emc/catalog/service" +) + +//go:embed index.html.tpl +var catalog string + +// Spec defines the requirements for hosting a catalog. +type Spec struct { + Services []service.Spec +} + +// Option defines an optional component of the spec. +type Option func(spec *Spec) + +// Service registers a known service with the catalog. +func Service(label string, options ...service.Option) Option { + return func(spec *Spec) { + spec.Services = append(spec.Services, service.New(label, options...)) + } +} + +// Serve provides command line functionality for running the service catalog. +func Serve(options ...Option) { + addr := flag.String("bind_address", "127.0.0.1:8080", "the address the service should bind to when serving content") + flag.Parse() + + start := time.Now() + + t := template.Must(template.New("catalog").Parse(catalog)) + + spec := Spec{} + for _, opt := range options { + opt(&spec) + } + + html := bytes.NewBuffer(nil) + err := t.Execute(html, spec) + if err != nil { + panic(err) + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "", start, bytes.NewReader(html.Bytes())) + }) + + log.Printf("serving on %s\n", *addr) + err = http.ListenAndServe(*addr, http.DefaultServeMux) + if err != nil { + panic(err) + } +} diff --git a/catalog/service/dsl.go b/catalog/service/dsl.go new file mode 100644 index 0000000..5712a3c --- /dev/null +++ b/catalog/service/dsl.go @@ -0,0 +1,70 @@ +// Copyright (C) 2022 Mya Pitzeruse +// The MIT License (MIT) + +package service + +import ( + "github.com/mjpitz/mjpitz/apps/emc/internal/catalog/linkgroup" +) + +// New constructs a spec given a label and set of options. +func New(label string, options ...Option) Spec { + spec := Spec{ + Label: label, + Metadata: make(map[string]string), + } + + for _, opt := range options { + opt(&spec) + } + + return spec +} + +// Option defines an optional component of the spec. +type Option func(spec *Spec) + +// Spec defines the elements needed to render a service. +type Spec struct { + Label string + LogoURL string + Description string + URL string + Metadata map[string]string + LinkGroups []linkgroup.Spec +} + +// LogoURL configures the icon for the service. +func LogoURL(url string) Option { + return func(spec *Spec) { + spec.LogoURL = url + } +} + +// Description specifies a short, brief description about the service. +func Description(description string) Option { + return func(spec *Spec) { + spec.Description = description + } +} + +// URL configures the services public facing URL that's presented to end users. +func URL(url string) Option { + return func(spec *Spec) { + spec.URL = url + } +} + +// Metadata allows additional metadata to be attached to a service. +func Metadata(key, value string) Option { + return func(spec *Spec) { + spec.Metadata[key] = value + } +} + +// LinkGroup appends a group of links to the provided service. +func LinkGroup(label string, options ...linkgroup.Option) Option { + return func(spec *Spec) { + spec.LinkGroups = append(spec.LinkGroups, linkgroup.New(label, options...)) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..55f470a --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/mjpitz/emc + +go 1.18 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..2209650 Binary files /dev/null and b/screenshot.png differ