reg/server.go
Jess Frazelle 298ecf1510
move server under main command closes #106
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-07-15 19:05:12 -04:00

177 lines
4.7 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"html/template"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/genuinetools/reg/clair"
"github.com/gorilla/mux"
wordwrap "github.com/mitchellh/go-wordwrap"
"github.com/sirupsen/logrus"
)
const serverHelp = `Run a static UI server for a registry.`
func (cmd *serverCommand) Name() string { return "server" }
func (cmd *serverCommand) Args() string { return "[OPTIONS]" }
func (cmd *serverCommand) ShortHelp() string { return serverHelp }
func (cmd *serverCommand) LongHelp() string { return serverHelp }
func (cmd *serverCommand) Hidden() bool { return false }
func (cmd *serverCommand) Register(fs *flag.FlagSet) {
fs.DurationVar(&cmd.interval, "interval", time.Hour, "interval to generate new index.html's at")
fs.StringVar(&cmd.registryServer, "registry", "", "URL to the private registry (ex. r.j3ss.co)")
fs.StringVar(&cmd.registryServer, "r", "", "URL to the private registry (ex. r.j3ss.co)")
fs.StringVar(&cmd.clairServer, "clair", "", "url to clair instance")
fs.StringVar(&cmd.cert, "cert", "", "path to ssl cert")
fs.StringVar(&cmd.key, "key", "", "path to ssl key")
fs.StringVar(&cmd.port, "port", "8080", "port for server to run on")
fs.BoolVar(&cmd.once, "once", false, "generate an output once and then exit")
}
type serverCommand struct {
interval time.Duration
registryServer string
clairServer string
once bool
cert string
key string
port string
}
func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
// Create the registry client.
r, err := createRegistryClient(cmd.registryServer)
if err != nil {
return err
}
// Create the registry controller for the handlers.
rc := registryController{
reg: r,
}
// Create a clair client if the user passed in a server address.
if len(cmd.clairServer) < 1 {
rc.cl, err = clair.New(cmd.clairServer, clair.Opt{
Insecure: insecure,
Debug: debug,
Timeout: timeout,
})
if err != nil {
return fmt.Errorf("creation of clair client at %s failed: %v", cmd.clairServer, err)
}
}
// Get the path to the static directory.
wd, err := os.Getwd()
if err != nil {
return err
}
staticDir := filepath.Join(wd, "static")
templateDir := filepath.Join(staticDir, "../templates")
// Make sure all the paths exist.
tmplPaths := []string{
staticDir,
filepath.Join(templateDir, "vulns.html"),
filepath.Join(templateDir, "repositories.html"),
filepath.Join(templateDir, "tags.html"),
}
for _, path := range tmplPaths {
if _, err := os.Stat(path); os.IsNotExist(err) {
return fmt.Errorf("template %s not found", path)
}
}
funcMap := template.FuncMap{
"trim": func(s string) string {
return wordwrap.WrapString(s, 80)
},
"color": func(s string) string {
switch s = strings.ToLower(s); s {
case "high":
return "danger"
case "critical":
return "danger"
case "defcon1":
return "danger"
case "medium":
return "warning"
case "low":
return "info"
case "negligible":
return "info"
case "unknown":
return "default"
default:
return "default"
}
},
}
rc.tmpl = template.Must(template.New("").Funcs(funcMap).ParseGlob(templateDir + "/*.html"))
// Create the initial index.
logrus.Info("creating initial static index")
if err := rc.repositories(staticDir); err != nil {
return fmt.Errorf("creating index failed: %v", err)
}
if cmd.once {
logrus.Info("output generated, exiting...")
return nil
}
ticker := time.NewTicker(cmd.interval)
go func() {
// Create more indexes every X minutes based off interval.
for range ticker.C {
logrus.Info("creating timer based static index")
if err := rc.repositories(staticDir); err != nil {
logrus.Warnf("creating static index failed: %v", err)
}
}
}()
// Create mux server.
mux := mux.NewRouter()
mux.UseEncodedPath()
// Static files handler.
staticHandler := http.FileServer(http.Dir(staticDir))
mux.HandleFunc("/repo/{repo}/tags", rc.tagsHandler)
mux.HandleFunc("/repo/{repo}/tags/", rc.tagsHandler)
mux.HandleFunc("/repo/{repo}/tag/{tag}", rc.vulnerabilitiesHandler)
mux.HandleFunc("/repo/{repo}/tag/{tag}/", rc.vulnerabilitiesHandler)
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns", rc.vulnerabilitiesHandler)
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns/", rc.vulnerabilitiesHandler)
mux.HandleFunc("/repo/{repo}/tag/{tag}/vulns.json", rc.vulnerabilitiesHandler)
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticHandler))
mux.Handle("/", staticHandler)
// Set up the server.
server := &http.Server{
Addr: ":" + cmd.port,
Handler: mux,
}
logrus.Infof("Starting server on port %q", cmd.port)
if len(cmd.cert) > 0 && len(cmd.key) > 0 {
return server.ListenAndServeTLS(cmd.cert, cmd.key)
}
return server.ListenAndServe()
}