2016-05-02 16:23:38 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2016-05-02 18:33:33 +00:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
2016-05-02 18:23:43 +00:00
|
|
|
"errors"
|
2016-05-02 16:23:38 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2016-05-02 18:33:33 +00:00
|
|
|
"io/ioutil"
|
2016-05-02 16:23:38 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/user"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
"github.com/coreos/clair/cmd/clairctl/clair"
|
2016-05-02 18:33:33 +00:00
|
|
|
"github.com/coreos/clair/cmd/clairctl/xstrings"
|
2016-05-02 18:23:43 +00:00
|
|
|
"github.com/spf13/viper"
|
2016-05-02 16:23:38 +00:00
|
|
|
)
|
|
|
|
|
2016-05-02 18:23:43 +00:00
|
|
|
var ErrLoginNotFound = errors.New("user is not log in")
|
|
|
|
|
2016-06-08 16:52:15 +00:00
|
|
|
var IsLocal = false
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
type reportConfig struct {
|
2016-05-02 16:23:38 +00:00
|
|
|
Path, Format string
|
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
type clairConfig struct {
|
2016-05-17 16:36:51 +00:00
|
|
|
URI string
|
2016-05-02 16:23:38 +00:00
|
|
|
Port, HealthPort int
|
2016-05-17 16:31:53 +00:00
|
|
|
Report reportConfig
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
type authConfig struct {
|
2016-05-02 16:23:38 +00:00
|
|
|
InsecureSkipVerify bool
|
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
type clairctlConfig struct {
|
2016-05-02 16:23:38 +00:00
|
|
|
IP, TempFolder string
|
|
|
|
Port int
|
|
|
|
}
|
|
|
|
type config struct {
|
2016-05-17 16:31:53 +00:00
|
|
|
Clair clairConfig
|
|
|
|
Auth authConfig
|
|
|
|
Clairctl clairctlConfig
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Init reads in config file and ENV variables if set.
|
|
|
|
func Init(cfgFile string, logLevel string) {
|
|
|
|
lvl := logrus.WarnLevel
|
|
|
|
if logLevel != "" {
|
|
|
|
var err error
|
|
|
|
lvl, err = logrus.ParseLevel(logLevel)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Warningf("Wrong Log level %v, defaults to [Warning]", logLevel)
|
|
|
|
lvl = logrus.WarnLevel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logrus.SetLevel(lvl)
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
viper.SetEnvPrefix("clairctl")
|
|
|
|
viper.SetConfigName("clairctl") // name of config file (without extension)
|
|
|
|
viper.AddConfigPath("$HOME/.clairctl") // adding home directory as first search path
|
|
|
|
viper.AddConfigPath(".") // adding home directory as first search path
|
|
|
|
viper.AutomaticEnv() // read in environment variables that match
|
2016-05-02 16:23:38 +00:00
|
|
|
if cfgFile != "" {
|
|
|
|
viper.SetConfigFile(cfgFile)
|
|
|
|
}
|
|
|
|
err := viper.ReadInConfig()
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("No config file used")
|
|
|
|
} else {
|
|
|
|
logrus.Debugf("Using config file: %v", viper.ConfigFileUsed())
|
|
|
|
}
|
|
|
|
|
|
|
|
if viper.Get("clair.uri") == nil {
|
|
|
|
viper.Set("clair.uri", "http://localhost")
|
|
|
|
}
|
|
|
|
if viper.Get("clair.port") == nil {
|
|
|
|
viper.Set("clair.port", "6060")
|
|
|
|
}
|
|
|
|
if viper.Get("clair.healthPort") == nil {
|
|
|
|
viper.Set("clair.healthPort", "6061")
|
|
|
|
}
|
2016-05-17 16:36:51 +00:00
|
|
|
|
2016-05-02 16:23:38 +00:00
|
|
|
if viper.Get("clair.report.path") == nil {
|
|
|
|
viper.Set("clair.report.path", "reports")
|
|
|
|
}
|
|
|
|
if viper.Get("clair.report.format") == nil {
|
|
|
|
viper.Set("clair.report.format", "html")
|
|
|
|
}
|
|
|
|
if viper.Get("auth.insecureSkipVerify") == nil {
|
|
|
|
viper.Set("auth.insecureSkipVerify", "true")
|
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
if viper.Get("clairctl.ip") == nil {
|
|
|
|
viper.Set("clairctl.ip", "")
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
if viper.Get("clairctl.port") == nil {
|
|
|
|
viper.Set("clairctl.port", 0)
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
if viper.Get("clairctl.tempFolder") == nil {
|
|
|
|
viper.Set("clairctl.tempFolder", "/tmp/clairctl")
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
|
|
|
clair.Config()
|
|
|
|
}
|
|
|
|
|
2016-06-07 21:30:36 +00:00
|
|
|
func TmpLocal() string {
|
|
|
|
return viper.GetString("clairctl.tempFolder")
|
|
|
|
}
|
|
|
|
|
2016-05-02 16:23:38 +00:00
|
|
|
func values() config {
|
|
|
|
return config{
|
2016-05-17 16:31:53 +00:00
|
|
|
Clair: clairConfig{
|
2016-05-02 16:23:38 +00:00
|
|
|
URI: viper.GetString("clair.uri"),
|
|
|
|
Port: viper.GetInt("clair.port"),
|
|
|
|
HealthPort: viper.GetInt("clair.healthPort"),
|
2016-05-17 16:31:53 +00:00
|
|
|
Report: reportConfig{
|
2016-05-02 16:23:38 +00:00
|
|
|
Path: viper.GetString("clair.report.path"),
|
|
|
|
Format: viper.GetString("clair.report.format"),
|
|
|
|
},
|
|
|
|
},
|
2016-05-17 16:31:53 +00:00
|
|
|
Auth: authConfig{
|
2016-05-02 16:23:38 +00:00
|
|
|
InsecureSkipVerify: viper.GetBool("auth.insecureSkipVerify"),
|
|
|
|
},
|
2016-05-17 16:31:53 +00:00
|
|
|
Clairctl: clairctlConfig{
|
|
|
|
IP: viper.GetString("clairctl.ip"),
|
|
|
|
Port: viper.GetInt("clairctl.port"),
|
|
|
|
TempFolder: viper.GetString("clairctl.tempFolder"),
|
2016-05-02 16:23:38 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Print() {
|
|
|
|
cfg := values()
|
|
|
|
cfgBytes, err := yaml.Marshal(cfg)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatalf("marshalling configuration: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("Configuration")
|
|
|
|
fmt.Printf("%v", string(cfgBytes))
|
|
|
|
}
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
func ClairctlHome() string {
|
2016-05-02 16:23:38 +00:00
|
|
|
usr, err := user.Current()
|
|
|
|
if err != nil {
|
2016-05-02 18:33:33 +00:00
|
|
|
panic(err)
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
2016-05-17 16:31:53 +00:00
|
|
|
p := usr.HomeDir + "/.clairctl"
|
2016-05-02 16:23:38 +00:00
|
|
|
|
|
|
|
if _, err := os.Stat(p); os.IsNotExist(err) {
|
|
|
|
os.Mkdir(p, 0700)
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2016-05-02 18:33:33 +00:00
|
|
|
type Login struct {
|
|
|
|
Username string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
|
|
|
|
type loginMapping map[string]Login
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
func ClairctlConfig() string {
|
|
|
|
return ClairctlHome() + "/config.json"
|
2016-05-02 16:23:38 +00:00
|
|
|
}
|
|
|
|
|
2016-05-02 18:33:33 +00:00
|
|
|
func AddLogin(registry string, login Login) error {
|
|
|
|
var logins loginMapping
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
if err := readConfigFile(&logins, ClairctlConfig()); err != nil {
|
|
|
|
return fmt.Errorf("reading clairctl file: %v", err)
|
2016-05-02 18:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
logins[registry] = login
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
if err := writeConfigFile(logins, ClairctlConfig()); err != nil {
|
2016-05-02 18:33:33 +00:00
|
|
|
return fmt.Errorf("indenting login: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func GetLogin(registry string) (Login, error) {
|
2016-05-17 16:31:53 +00:00
|
|
|
if _, err := os.Stat(ClairctlConfig()); err == nil {
|
2016-05-02 18:33:33 +00:00
|
|
|
var logins loginMapping
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
if err := readConfigFile(&logins, ClairctlConfig()); err != nil {
|
|
|
|
return Login{}, fmt.Errorf("reading clairctl file: %v", err)
|
2016-05-02 18:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if login, present := logins[registry]; present {
|
|
|
|
d, err := base64.StdEncoding.DecodeString(login.Password)
|
|
|
|
if err != nil {
|
|
|
|
return Login{}, fmt.Errorf("decoding password: %v", err)
|
|
|
|
}
|
|
|
|
login.Password = string(d)
|
|
|
|
return login, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Login{}, ErrLoginNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func RemoveLogin(registry string) (bool, error) {
|
2016-05-17 16:31:53 +00:00
|
|
|
if _, err := os.Stat(ClairctlConfig()); err == nil {
|
2016-05-02 18:33:33 +00:00
|
|
|
var logins loginMapping
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
if err := readConfigFile(&logins, ClairctlConfig()); err != nil {
|
|
|
|
return false, fmt.Errorf("reading clairctl file: %v", err)
|
2016-05-02 18:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, present := logins[registry]; present {
|
|
|
|
delete(logins, registry)
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
if err := writeConfigFile(logins, ClairctlConfig()); err != nil {
|
2016-05-02 18:33:33 +00:00
|
|
|
return false, fmt.Errorf("indenting login: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func readConfigFile(logins *loginMapping, file string) error {
|
|
|
|
if _, err := os.Stat(file); err == nil {
|
|
|
|
f, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(f, &logins); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*logins = loginMapping{}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeConfigFile(logins loginMapping, file string) error {
|
|
|
|
s, err := xstrings.ToIndentJSON(logins)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = ioutil.WriteFile(file, s, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-17 16:31:53 +00:00
|
|
|
//LocalServerIP return the local clairctl server IP
|
2016-05-02 16:23:38 +00:00
|
|
|
func LocalServerIP() (string, error) {
|
2016-05-17 16:31:53 +00:00
|
|
|
localPort := viper.GetString("clairctl.port")
|
|
|
|
localIP := viper.GetString("clairctl.ip")
|
2016-05-02 16:23:38 +00:00
|
|
|
if localIP == "" {
|
|
|
|
logrus.Infoln("retrieving docker0 interface as local IP")
|
|
|
|
var err error
|
|
|
|
localIP, err = Docker0InterfaceIP()
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("retrieving docker0 interface ip: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(localIP) + ":" + localPort, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//Docker0InterfaceIP return the docker0 interface ip by running `ip route show | grep docker0 | awk {print $9}`
|
|
|
|
func Docker0InterfaceIP() (string, error) {
|
|
|
|
var localIP bytes.Buffer
|
|
|
|
|
|
|
|
ip := exec.Command("ip", "route", "show")
|
|
|
|
rGrep, wIP := io.Pipe()
|
|
|
|
grep := exec.Command("grep", "docker0")
|
|
|
|
ip.Stdout = wIP
|
|
|
|
grep.Stdin = rGrep
|
|
|
|
awk := exec.Command("awk", "{print $9}")
|
|
|
|
rAwk, wGrep := io.Pipe()
|
|
|
|
grep.Stdout = wGrep
|
|
|
|
awk.Stdin = rAwk
|
|
|
|
awk.Stdout = &localIP
|
|
|
|
err := ip.Start()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = grep.Start()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = awk.Start()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = ip.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = wIP.Close()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = grep.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = wGrep.Close()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
err = awk.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return localIP.String(), nil
|
|
|
|
}
|