From 0ab86b565f437b9dede5d3a7cef56690d8d19bbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Manuel=20R=C3=BCger?= <manuel@rueg.eu>
Date: Tue, 18 Sep 2018 15:42:24 +0200
Subject: [PATCH 2/2] server: Read config from file

---
 README.md          |   4 ++
 config.yml.example |  37 ++++++++++++++++
 server.go          | 107 +++++++++++++++++++++++++++++++--------------
 3 files changed, 114 insertions(+), 34 deletions(-)
 create mode 100644 config.yml.example

diff --git a/README.md b/README.md
index 3b1b661f..5e0f0175 100644
--- a/README.md
+++ b/README.md
@@ -228,6 +228,7 @@ Flags:
   -u, --username       username for the registry (default: <none>)
   --listen-address     address to listen on (default: <none>)
   --asset-path         Path to assets and templates (default: <none>)
+  --config             Path to config file (default: <none>)
   -f, --force-non-ssl  force allow use of non-ssl (default: false)
   --once               generate the templates once and then exit (default: false)
   --skip-ping          skip pinging the registry while establishing connection (default: false)
@@ -243,6 +244,9 @@ Flags:
   -p, --password       password for the registry (default: <none>)
 ```
 
+Alternatively you can provide configuration to `reg server` via a file passed to
+`reg server` via as `--config`. See also config.yml.example in this repository.
+
 **Screenshots:**
 
 ![home.png](server/home.png)
diff --git a/config.yml.example b/config.yml.example
new file mode 100644
index 00000000..9d4be25f
--- /dev/null
+++ b/config.yml.example
@@ -0,0 +1,37 @@
+### Reg Server Settings
+
+## Path to server TLS certificate:
+# cert:
+## Path to server TLS key:
+# key:
+## Address to listen on:
+# listen-address: 0.0.0.0
+## Port to listen on:
+# port: 8080
+## Path Assets are stored under:
+# asset-path: /var/lib/reg
+## Generate static website and exit:
+# once: false
+## Refresh interval:
+# interval: 1h0m0s
+## Debug output
+# debug: false
+## Skip initial ping
+# skip-ping: false
+## Timeout
+# timeout: 1m0s
+
+
+### Registry and Clair Server Settings
+## Registry Server:
+# registry: r.j3ss.co
+## Username to authenticate against registry server
+# username:
+## Password to authenticate against registry server
+# password:
+## If true, do not verify TLS certificates
+# insecure: false
+## Force allow use of non-TLS connections
+# force-nonssl: false
+## Clair Server:
+# clair:
diff --git a/server.go b/server.go
index fcd4cd71..0c84d85d 100644
--- a/server.go
+++ b/server.go
@@ -4,7 +4,9 @@ import (
 	"context"
 	"flag"
 	"fmt"
+	"gopkg.in/yaml.v2"
 	"html/template"
+	"io/ioutil"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -29,39 +31,76 @@ 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.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.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.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.listenAddress, "listen-address", "", "address to listen on")
-	fs.StringVar(&cmd.port, "port", "8080", "port for server to run on")
-	fs.StringVar(&cmd.assetPath, "asset-path", "", "Path to assets and templates")
+	fs.StringVar(&cmd.Cert, "cert", "", "path to ssl cert")
+	fs.StringVar(&cmd.Key, "key", "", "path to ssl key")
+	fs.StringVar(&cmd.ListenAddress, "listen-address", "", "address to listen on")
+	fs.StringVar(&cmd.Port, "port", "8080", "port for server to run on")
 
-	fs.BoolVar(&cmd.generateAndExit, "once", false, "generate the templates once and then exit")
+	fs.StringVar(&cmd.AssetPath, "asset-path", "", "Path to assets and templates")
+	fs.StringVar(&cmd.configPath, "config", "", "Path to config file")
+
+	fs.BoolVar(&cmd.GenerateAndExit, "once", false, "generate the templates once and then exit")
 }
 
 type serverCommand struct {
-	interval       time.Duration
-	registryServer string
-	clairServer    string
-
-	generateAndExit bool
-
-	cert          string
-	key           string
-	listenAddress string
-	port          string
-	assetPath     string
+	Interval        time.Duration `yaml:"interval"`
+	RegistryServer  string        `yaml:"registry"`
+	ClairServer     string        `yaml:"clair"`
+	GenerateAndExit bool          `yaml:"once"`
+	Cert            string        `yaml:"cert"`
+	Key             string        `yaml:"key"`
+	ListenAddress   string        `yaml:"listen-address"`
+	Port            string        `yaml:"port"`
+	AssetPath       string        `yaml:"asset-path"`
+	configPath      string
+
+	Password    string        `yaml:"password"`
+	Username    string        `yaml:"username"`
+	Insecure    bool          `yaml:"insecure"`
+	Debug       bool          `yaml:"debug"`
+	SkipPing    bool          `yaml:"skip-ping"`
+	ForceNonSSL bool          `yaml:"force-nonssl"`
+	Timeout     time.Duration `yaml:"timeout"`
 }
 
 func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
 	// Create the registry client.
-	r, err := createRegistryClient(ctx, cmd.registryServer)
+	if len(cmd.configPath) > 0 {
+		config, err := ioutil.ReadFile(cmd.configPath)
+		if err != nil {
+			return err
+		}
+		yaml.Unmarshal(config, cmd)
+		if err != nil {
+			return err
+		}
+		if len(cmd.Username) > 0 {
+			username = cmd.Username
+		}
+		if len(cmd.Password) > 0 {
+			password = cmd.Password
+		}
+		if cmd.Debug {
+			debug = cmd.Debug
+		}
+		if cmd.Insecure {
+			insecure = cmd.Insecure
+		}
+		if cmd.SkipPing {
+			skipPing = cmd.SkipPing
+		}
+		if cmd.Timeout != 0 {
+			timeout = cmd.Timeout
+		}
+	}
+	r, err := createRegistryClient(ctx, cmd.RegistryServer)
 	if err != nil {
 		return err
 	}
@@ -69,25 +108,25 @@ func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
 	// Create the registry controller for the handlers.
 	rc := registryController{
 		reg:          r,
-		generateOnly: cmd.generateAndExit,
+		generateOnly: cmd.GenerateAndExit,
 	}
 
 	// Create a clair client if the user passed in a server address.
-	if len(cmd.clairServer) > 0 {
-		rc.cl, err = clair.New(cmd.clairServer, clair.Opt{
+	if len(cmd.ClairServer) > 0 {
+		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)
+			return fmt.Errorf("creation of clair client at %s failed: %v", cmd.ClairServer, err)
 		}
 	} else {
 		rc.cl = nil
 	}
 	// Get the path to the asset directory.
-	assetDir := cmd.assetPath
-	if len(cmd.assetPath) <= 0 {
+	assetDir := cmd.AssetPath
+	if len(cmd.AssetPath) <= 0 {
 		assetDir, err = os.Getwd()
 		if err != nil {
 			return err
@@ -131,12 +170,12 @@ func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
 		return fmt.Errorf("creating index failed: %v", err)
 	}
 
-	if cmd.generateAndExit {
+	if cmd.GenerateAndExit {
 		logrus.Info("output generated, exiting...")
 		return nil
 	}
 
-	rc.interval = cmd.interval
+	rc.interval = cmd.Interval
 	ticker := time.NewTicker(rc.interval)
 	go func() {
 		// Create more indexes every X minutes based off interval.
@@ -174,12 +213,12 @@ func (cmd *serverCommand) Run(ctx context.Context, args []string) error {
 
 	// Set up the server.
 	server := &http.Server{
-		Addr:    cmd.listenAddress + ":" + cmd.port,
+		Addr:    cmd.ListenAddress + ":" + 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)
+	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()
 }