Skip to content

Commit

Permalink
Merge pull request #77 from optiopay/config
Browse files Browse the repository at this point in the history
Refactor configuration hadling
  • Loading branch information
hashmap authored Feb 7, 2018
2 parents 3907c03 + 97cd6a3 commit d92a6c3
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 87 deletions.
26 changes: 17 additions & 9 deletions docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,24 @@ type layer struct {
Digest string
}

type Config struct {
ImageName string
User string
Password string
InsecureTLS bool
InsecureRegistry bool
}

const dockerHub = "registry-1.docker.io"

var tokenRe = regexp.MustCompile(`Bearer realm="(.*?)",service="(.*?)",scope="(.*?)"`)

// NewImage parses image name which could be the ful name registry:port/name:tag
// or in any other shorter forms and creates docker image entity without
// information about layers
func NewImage(qname, user, password string, insecureTLS, insecureRegistry bool) (*Image, error) {
func NewImage(conf *Config) (*Image, error) {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureTLS},
TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.InsecureTLS},
}
client := http.Client{
Transport: tr,
Expand All @@ -108,13 +116,13 @@ func NewImage(qname, user, password string, insecureTLS, insecureRegistry bool)
var name, port string
state := stateInitial
start := 0
for i, c := range qname {
if c == ':' || c == '/' || c == '@' || i == len(qname)-1 {
if i == len(qname)-1 {
for i, c := range conf.ImageName {
if c == ':' || c == '/' || c == '@' || i == len(conf.ImageName)-1 {
if i == len(conf.ImageName)-1 {
// ignore a separator, include the last symbol
i += 1
}
part := qname[start:i]
part := conf.ImageName[start:i]
start = i + 1
switch state {
case stateInitial:
Expand Down Expand Up @@ -163,7 +171,7 @@ func NewImage(qname, user, password string, insecureTLS, insecureRegistry bool)
if tag == "" {
tag = strings.Join(tagParts, ":")
}
if insecureRegistry {
if conf.InsecureRegistry {
registry = fmt.Sprintf("http://%s/v2", registry)
} else {
registry = fmt.Sprintf("https://%s/v2", registry)
Expand All @@ -173,8 +181,8 @@ func NewImage(qname, user, password string, insecureTLS, insecureRegistry bool)
Registry: registry,
Name: name,
Tag: tag,
user: user,
password: password,
user: conf.User,
password: conf.Password,
client: client,
}, nil
}
Expand Down
7 changes: 4 additions & 3 deletions docker/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ func TestNewImage(t *testing.T) {
},
}
for name, tc := range tcs {
image, err := NewImage(tc.image, "", "", false, false)

image, err := NewImage(&Config{ImageName: tc.image})
if err != nil {
t.Fatalf("%s: Can't parse image name: %s", name, err)
}
Expand All @@ -105,7 +106,7 @@ func TestPullManifestSchemaV1(t *testing.T) {
}))
defer ts.Close()

image, err := NewImage("docker-registry.domain.com/nginx:1b29e1531c", "", "", false, false)
image, err := NewImage(&Config{ImageName: "docker-registry.domain.com/nginx:1b29e1531ci"})
image.Registry = ts.URL
err = image.Pull()
if err != nil {
Expand All @@ -127,7 +128,7 @@ func TestPullManifestSchemaV2(t *testing.T) {
}))
defer ts.Close()

image, err := NewImage("docker-registry.domain.com/nginx:1b29e1531c", "", "", false, false)
image, err := NewImage(&Config{ImageName: "docker-registry.domain.com/nginx:1b29e1531c"})
image.Registry = ts.URL
err = image.Pull()
if err != nil {
Expand Down
106 changes: 106 additions & 0 deletions klar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"fmt"
"os"
"strconv"
"strings"

"github.com/optiopay/klar/clair"
"github.com/optiopay/klar/docker"
"github.com/optiopay/klar/utils"
)

const (
optionClairOutput = "CLAIR_OUTPUT"
optionClairAddress = "CLAIR_ADDR"
optionKlarTrace = "KLAR_TRACE"
optionClairThreshold = "CLAIR_THRESHOLD"
optionJSONOutput = "JSON_OUTPUT"
optionDockerUser = "DOCKER_USER"
optionDockerPassword = "DOCKER_PASSWORD"
optionDockerInsecure = "DOCKER_INSECURE"
optionRegistryInsecure = "REGISTRY_INSECURE"
)

var priorities = []string{"Unknown", "Negligible", "Low", "Medium", "High", "Critical", "Defcon1"}

func parseOutputPriority() (string, error) {
clairOutput := priorities[0]
outputEnv := os.Getenv(optionClairOutput)
if outputEnv != "" {
output := strings.Title(strings.ToLower(outputEnv))
correct := false
for _, sev := range priorities {
if sev == output {
clairOutput = sev
correct = true
break
}
}

if !correct {
return "", fmt.Errorf("Clair output level %s is not supported, only support %v\n", outputEnv, priorities)
}
}
return clairOutput, nil
}

func parseIntOption(key string) int {
val := 0
valStr := os.Getenv(key)
if valStr != "" {
val, _ = strconv.Atoi(valStr)
}
return val
}

func parseBoolOption(key string) bool {
val := false
if envVal, err := strconv.ParseBool(os.Getenv(key)); err == nil {
val = envVal
}
return val
}

type jsonOutput struct {
LayerCount int
Vulnerabilities map[string][]*clair.Vulnerability
}

type config struct {
ClairAddr string
ClairOutput string
Threshold int
JSONOutput bool
DockerConfig docker.Config
}

func newConfig(args []string) (*config, error) {
clairAddr := os.Getenv(optionClairAddress)
if clairAddr == "" {
return nil, fmt.Errorf("Clair address must be provided\n")
}

if os.Getenv(optionKlarTrace) != "" {
utils.Trace = true
}

clairOutput, err := parseOutputPriority()
if err != nil {
return nil, err
}
return &config{
ClairAddr: clairAddr,
ClairOutput: clairOutput,
Threshold: parseIntOption(optionClairThreshold),
JSONOutput: parseBoolOption(optionJSONOutput),
DockerConfig: docker.Config{
ImageName: args[1],
User: os.Getenv(optionDockerUser),
Password: os.Getenv(optionDockerPassword),
InsecureTLS: parseBoolOption(optionDockerInsecure),
InsecureRegistry: parseBoolOption(optionRegistryInsecure),
},
}, nil
}
98 changes: 98 additions & 0 deletions klar_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"os"
"testing"
)

func TestParseIntOption(t *testing.T) {
cases := []struct {
value string
expected int
}{
{
value: "",
expected: 0,
},
{
value: "0",
expected: 0,
},
{
value: "10",
expected: 10,
},
{
value: "x",
expected: 0,
},
}
for _, tc := range cases {
os.Setenv(optionClairThreshold, tc.value)
if got := parseIntOption(optionClairThreshold); got != tc.expected {
t.Errorf("expected %d got %d", tc.expected, got)
}
}
}

func TestParseBoolOption(t *testing.T) {
cases := []struct {
value string
expected bool
}{
{
value: "",
expected: false,
},
{
value: "x",
expected: false,
},
{
value: "true",
expected: true,
},
{
value: "false",
expected: false,
},
}
for _, tc := range cases {
os.Setenv(optionDockerInsecure, tc.value)
if got := parseBoolOption(optionDockerInsecure); got != tc.expected {
t.Errorf("%q: expected %v got %v", tc.value, tc.expected, got)
}
}
}

func TestParseOutputPriority(t *testing.T) {
cases := []struct {
priority string
expected string
shouldFail bool
}{
{
priority: priorities[1],
expected: priorities[1],
},
{
priority: "",
expected: priorities[0],
},
{
priority: "xxx",
shouldFail: true,
},
}

for _, tc := range cases {
os.Setenv(optionClairOutput, tc.priority)
p, err := parseOutputPriority()
if (err != nil) != tc.shouldFail {
t.Fatalf("expected error: %v, got: %v", tc.expected, err)
}
if p != tc.expected {
t.Fatalf("expected output priority %s, got %s", tc.expected, p)
}
}
}
Loading

0 comments on commit d92a6c3

Please sign in to comment.