From 4a06ae98a032acab96d4ea7e367e259a59d3f7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Wed, 8 May 2019 09:29:03 +0200 Subject: [PATCH 01/19] Replace cli.Context with app.CliContext --- add.go | 44 ++++++-------- cmd/goss/goss.go | 58 ++++++++++++------- go.mod | 4 +- go.sum | 13 +++++ integration/commander.yaml | 2 +- .../resources/package/ubuntu/goss.yaml | 2 +- internal/app/context.go | 56 ++++++++++++++++++ serve.go | 30 +++++----- system/system.go | 17 +++--- validate.go | 34 +++++------ 10 files changed, 169 insertions(+), 91 deletions(-) create mode 100644 internal/app/context.go diff --git a/add.go b/add.go index b7951f6..d6c2f73 100644 --- a/add.go +++ b/add.go @@ -2,29 +2,27 @@ package goss import ( "fmt" + "github.com/SimonBaeumer/goss/internal/app" + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" "os" "strconv" "strings" - "time" - - "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" - "github.com/urfave/cli" ) // AddResources is a sSimple wrapper to add multiple resources -func AddResources(fileName, resourceName string, keys []string, c *cli.Context) error { +func AddResources(fileName, resourceName string, keys []string, ctx app.CliContext) error { OutStoreFormat = getStoreFormatFromFileName(fileName) - header := extractHeaderArgument(c.String("header")) + header := extractHeaderArgument(ctx.Header) config := util.Config{ - IgnoreList: c.GlobalStringSlice("exclude-attr"), - Timeout: int(c.Duration("timeout") / time.Millisecond), - AllowInsecure: c.Bool("insecure"), - NoFollowRedirects: c.Bool("no-follow-redirects"), - Server: c.String("server"), - Username: c.String("username"), - Password: c.String("password"), + IgnoreList: ctx.ExcludeAttr, + Timeout: ctx.Timeout, + AllowInsecure: ctx.AllowInsecure, + NoFollowRedirects: ctx.NoFollowRedirects, + Server: ctx.Server, + Username: ctx.Username, + Password: ctx.Password, Header: header, } @@ -35,10 +33,10 @@ func AddResources(fileName, resourceName string, keys []string, c *cli.Context) gossConfig = *NewGossConfig() } - sys := system.New(c) + sys := system.New(ctx.Package) for _, key := range keys { - if err := AddResource(fileName, gossConfig, resourceName, key, c, config, sys); err != nil { + if err := AddResource(fileName, gossConfig, resourceName, key, config, sys); err != nil { return err } } @@ -58,7 +56,7 @@ func extractHeaderArgument(headerArg string) map[string][]string { } // AddResource adds a resource to the configuration file -func AddResource(fileName string, gossConfig GossConfig, resourceName, key string, c *cli.Context, config util.Config, sys *system.System) error { +func AddResource(fileName string, gossConfig GossConfig, resourceName, key string, config util.Config, sys *system.System) error { // Need to figure out a good way to refactor this switch resourceName { case "Addr": @@ -174,12 +172,8 @@ func AddResource(fileName string, gossConfig GossConfig, resourceName, key strin } // Simple wrapper to add multiple resources -func AutoAddResources(fileName string, keys []string, c *cli.Context) error { +func AutoAddResources(fileName string, keys []string, ctx app.CliContext) error { OutStoreFormat = getStoreFormatFromFileName(fileName) - config := util.Config{ - IgnoreList: c.GlobalStringSlice("exclude-attr"), - Timeout: int(c.Duration("timeout") / time.Millisecond), - } var gossConfig GossConfig if _, err := os.Stat(fileName); err == nil { @@ -188,10 +182,10 @@ func AutoAddResources(fileName string, keys []string, c *cli.Context) error { gossConfig = *NewGossConfig() } - sys := system.New(c) + sys := system.New(ctx.Package) for _, key := range keys { - if err := AutoAddResource(fileName, gossConfig, key, c, config, sys); err != nil { + if err := AutoAddResource(fileName, gossConfig, key, sys); err != nil { return err } } @@ -203,7 +197,7 @@ func AutoAddResources(fileName string, keys []string, c *cli.Context) error { } // Autoadds all resources to the config file -func AutoAddResource(fileName string, gossConfig GossConfig, key string, c *cli.Context, config util.Config, sys *system.System) error { +func AutoAddResource(fileName string, gossConfig GossConfig, key string, sys *system.System) error { // file if strings.Contains(key, "/") { if res, _, ok := gossConfig.Files.AppendSysResourceIfExists(key, sys); ok == true { diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 12cf59a..0162b32 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -2,7 +2,8 @@ package main import ( "fmt" - "log" + app2 "github.com/SimonBaeumer/goss/internal/app" + "log" "os" "time" @@ -15,7 +16,6 @@ import ( var version string func main() { - startTime := time.Now() app := cli.NewApp() app.EnableBashCompletion = true @@ -86,7 +86,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.Validate(c, startTime) + ctx := app2.NewCliContext(c) + goss.Validate(ctx, startTime) return nil }, }, @@ -132,7 +133,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.Serve(c) + ctx := app2.NewCliContext(c) + goss.Serve(ctx) return nil }, }, @@ -156,7 +158,8 @@ func main() { Aliases: []string{"aa"}, Usage: "automatically add all matching resource to the test suite", Action: func(c *cli.Context) error { - goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) return nil }, }, @@ -175,7 +178,8 @@ func main() { Name: "package", Usage: "add new package", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) return nil }, }, @@ -183,7 +187,8 @@ func main() { Name: "file", Usage: "add new file", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) return nil }, }, @@ -197,7 +202,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) return nil }, }, @@ -205,7 +211,8 @@ func main() { Name: "port", Usage: "add new listening [protocol]:port - ex: 80 or udp:123", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) return nil }, }, @@ -213,7 +220,8 @@ func main() { Name: "service", Usage: "add new service", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) return nil }, }, @@ -221,7 +229,8 @@ func main() { Name: "user", Usage: "add new user", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) return nil }, }, @@ -229,7 +238,8 @@ func main() { Name: "group", Usage: "add new group", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) return nil }, }, @@ -243,7 +253,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) return nil }, }, @@ -261,7 +272,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) return nil }, }, @@ -269,7 +281,8 @@ func main() { Name: "process", Usage: "add new process name", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), ctx) return nil }, }, @@ -301,7 +314,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) return nil }, }, @@ -309,7 +323,8 @@ func main() { Name: "goss", Usage: "add new goss file, it will be imported from this one", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) return nil }, }, @@ -317,7 +332,8 @@ func main() { Name: "kernel-param", Usage: "add new goss kernel param", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) return nil }, }, @@ -325,7 +341,8 @@ func main() { Name: "mount", Usage: "add new mount", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) return nil }, }, @@ -333,7 +350,8 @@ func main() { Name: "interface", Usage: "add new interface", Action: func(c *cli.Context) error { - goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), c) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) return nil }, }, diff --git a/go.mod b/go.mod index fc6a596..c17a0de 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,11 @@ require ( github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1 + github.com/alecthomas/chroma v0.6.3 github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b - github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/docker v0.0.0-20161109014415-383a2f046b16 github.com/fatih/color v0.0.0-20161025120501-bf82308e8c85 github.com/golang/mock v1.2.0 - github.com/mattn/go-colorable v0.0.9 // indirect - github.com/mattn/go-isatty v0.0.4 // indirect github.com/miekg/dns v0.0.0-20161018060808-58f52c57ce9d github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f github.com/onsi/gomega v1.5.0 diff --git a/go.sum b/go.sum index 7b8ef2c..3887fe8 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,21 @@ github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 h1:oD15ssIOuF github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08/go.mod h1:FETZSu2VGNDJbGfeRExaz/SNbX0TTaqJEMo1yvsKoZ8= github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1 h1:s4dvLggvQOov0YFdv8XQvX+72TAFzfJg+6SgoXiIaq4= github.com/aelsabbahy/go-ps v0.0.0-20170721000941-443386855ca1/go.mod h1:70tSBushy/POz6cCR294bKno4BNAC7XWVdkkxWQ1N6E= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= +github.com/alecthomas/chroma v0.6.3 h1:8H1D0yddf0mvgvO4JDBKnzLd9ERmzzAijBxnZXGV/FA= +github.com/alecthomas/chroma v0.6.3/go.mod h1:quT2EpvJNqkuPi6DmBHB+E33FXBgBBPzyH5++Dn1LPc= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= +github.com/alecthomas/kong v0.1.15/go.mod h1:0m2VYms8rH0qbCqVB2gvGHk74bqLIq0HXjCs5bNbNQU= +github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b h1:EaV7ZKUbpQK3eErRkV5GKl7s6SZU30dEB6gimH5BLLk= github.com/cheekybits/genny v0.0.0-20160824153601-e8e29e67948b/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= +github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v0.0.0-20161109014415-383a2f046b16 h1:8J7CV9qtX4ygmYnM9uL5Ola9iI9QWzDt0rI3rwKSfSo= github.com/docker/docker v0.0.0-20161109014415-383a2f046b16/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/fatih/color v0.0.0-20161025120501-bf82308e8c85 h1:g7ijd5QIEMWwZNVp/T/6kQ8RSh8rN+YNhghMcrET3qY= @@ -37,7 +47,9 @@ github.com/patrickmn/go-cache v2.0.0+incompatible h1:1G02Ver4lZNbrWBHtot9O0Z2Pik github.com/patrickmn/go-cache v2.0.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 h1:niRuEF0NOlFnqraxzjuvvOdCM6gxmHiaBABjvg3/kDo= @@ -46,6 +58,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5 h1:SlFRMb9PEnqzqnBRCynVOhxv4vHjB2lnIoxK6p5nzFM= golang.org/x/sys v0.0.0-20181210030007-2a47403f2ae5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= diff --git a/integration/commander.yaml b/integration/commander.yaml index 7ebc96a..66a0921 100755 --- a/integration/commander.yaml +++ b/integration/commander.yaml @@ -400,7 +400,7 @@ tests: - |- Package: apt: version: Expected - <[]string | len:1, cap:1>: ["1.6.8"] + <[]string | len:1, cap:1>: ["1.6.10"] to contain element matching : 100.0.0 - |- diff --git a/integration/resources/package/ubuntu/goss.yaml b/integration/resources/package/ubuntu/goss.yaml index 73d4750..f302735 100644 --- a/integration/resources/package/ubuntu/goss.yaml +++ b/integration/resources/package/ubuntu/goss.yaml @@ -2,6 +2,6 @@ package: apt: installed: true versions: - - 1.6.8 + - 1.6.10 no exists: installed: false \ No newline at end of file diff --git a/internal/app/context.go b/internal/app/context.go new file mode 100644 index 0000000..f13430f --- /dev/null +++ b/internal/app/context.go @@ -0,0 +1,56 @@ +package app + +import ( + "github.com/urfave/cli" + "time" +) + +type CliContext struct { + FormatOptions []string + Sleep time.Duration + RetryTimeout time.Duration + MaxConcurrent int + Package string + Vars string + Gossfile string + ExcludeAttr []string + Timeout int + AllowInsecure bool + NoFollowRedirects bool + Server string + Username string + Password string + Header string + Endpoint string + ListenAddr string + Cache time.Duration + Format string + NoColor bool + Color bool +} + +func NewCliContext(c *cli.Context) CliContext { + return CliContext{ + FormatOptions: c.StringSlice("format-options"), + Sleep: c.Duration("sleep"), + RetryTimeout: c.Duration("retry-timeout"), + Package: c.String("package"), + MaxConcurrent: c.Int("max-concurrent"), + Vars: c.GlobalString("vars"), + Gossfile: c.GlobalString("gossfile"), + ExcludeAttr: c.GlobalStringSlice("exclude-attr"), + Timeout: int(c.Duration("timeout") / time.Millisecond), + AllowInsecure: c.Bool("insecure"), + NoFollowRedirects: c.Bool("no-follow-redirects"), + Server: c.String("server"), + Username: c.String("username"), + Password: c.String("password"), + Header: c.String("header"), + Endpoint: c.String("endpoint"), + ListenAddr: c.String("listen-addr"), + Cache: c.Duration("cache"), + Format: c.String("format"), + NoColor: c.Bool("no-color"), + Color: c.Bool("color"), + } +} diff --git a/serve.go b/serve.go index f89435b..080a48e 100644 --- a/serve.go +++ b/serve.go @@ -2,6 +2,7 @@ package goss import ( "bytes" + "github.com/SimonBaeumer/goss/internal/app" "log" "net/http" "sync" @@ -12,30 +13,29 @@ import ( "github.com/SimonBaeumer/goss/util" "github.com/fatih/color" "github.com/patrickmn/go-cache" - "github.com/urfave/cli" ) -func Serve(c *cli.Context) { - endpoint := c.String("endpoint") +func Serve(ctx app.CliContext) { + endpoint := ctx.Endpoint color.NoColor = true - cache := cache.New(c.Duration("cache"), 30*time.Second) + cache := cache.New(ctx.Cache, 30*time.Second) health := healthHandler{ - c: c, - gossConfig: getGossConfig(c), - sys: system.New(c), - outputer: getOutputer(c), + c: ctx, + gossConfig: getGossConfig(ctx), + sys: system.New(ctx.Package), + outputer: getOutputer(ctx), cache: cache, gossMu: &sync.Mutex{}, - maxConcurrent: c.Int("max-concurrent"), + maxConcurrent: ctx.MaxConcurrent, } - if c.String("format") == "json" { + if ctx.Format == "json" { health.contentType = "application/json" } http.Handle(endpoint, health) - listenAddr := c.String("listen-addr") + listenAddr := ctx.ListenAddr log.Printf("Starting to listen on: %s", listenAddr) - log.Fatal(http.ListenAndServe(c.String("listen-addr"), nil)) + log.Fatal(http.ListenAndServe(ctx.ListenAddr, nil)) } type res struct { @@ -43,7 +43,7 @@ type res struct { b bytes.Buffer } type healthHandler struct { - c *cli.Context + c app.CliContext gossConfig GossConfig sys *system.System outputer outputs.Outputer @@ -56,7 +56,7 @@ type healthHandler struct { func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { outputConfig := util.OutputConfig{ - FormatOptions: h.c.StringSlice("format-options"), + FormatOptions: h.c.FormatOptions, } log.Printf("%v: requesting health probe", r.RemoteAddr) @@ -71,7 +71,7 @@ func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if found { resp = tmp.(res) } else { - h.sys = system.New(h.c) + h.sys = system.New(h.c.Package) log.Printf("%v: Stale cache, running tests", r.RemoteAddr) iStartTime := time.Now() out := validate(h.sys, h.gossConfig, h.maxConcurrent) diff --git a/system/system.go b/system/system.go index 7276ba2..f569235 100644 --- a/system/system.go +++ b/system/system.go @@ -8,10 +8,9 @@ import ( "sync" "github.com/aelsabbahy/GOnetstat" + util2 "github.com/SimonBaeumer/goss/util" // This needs a better name "github.com/aelsabbahy/go-ps" - util2 "github.com/SimonBaeumer/goss/util" - "github.com/urfave/cli" ) type Resource interface { @@ -55,7 +54,8 @@ func (s *System) ProcMap() map[string][]ps.Process { return s.procMap } -func New(c *cli.Context) *System { +//New creates the system object which holds all constructors for the system packages +func New(packageManger string) *System { sys := &System{ NewFile: NewDefFile, NewAddr: NewDefAddr, @@ -72,17 +72,16 @@ func New(c *cli.Context) *System { NewHTTP: NewDefHTTP, } sys.detectService() - sys.detectPackage(c) + sys.detectPackage(packageManger) return sys } // DetectPackage adds the correct package creation function to a System struct -func (sys *System) detectPackage(c *cli.Context) { - p := c.GlobalString("package") - if p != "deb" && p != "apk" && p != "pacman" && p != "rpm" { - p = DetectPackageManager() +func (sys *System) detectPackage(pkgManager string) { + if pkgManager != "deb" && pkgManager != "apk" && pkgManager != "pacman" && pkgManager != "rpm" { + pkgManager = DetectPackageManager() } - switch p { + switch pkgManager { case "deb": sys.NewPackage = NewDebPackage case "apk": diff --git a/validate.go b/validate.go index 19f40b4..c61633a 100644 --- a/validate.go +++ b/validate.go @@ -2,6 +2,7 @@ package goss import ( "fmt" + "github.com/SimonBaeumer/goss/internal/app" "io/ioutil" "os" "path/filepath" @@ -14,16 +15,15 @@ import ( "github.com/SimonBaeumer/goss/system" "github.com/SimonBaeumer/goss/util" "github.com/fatih/color" - "github.com/urfave/cli" ) -func getGossConfig(c *cli.Context) GossConfig { +func getGossConfig(ctx app.CliContext) GossConfig { // handle stdin var fh *os.File var path, source string var gossConfig GossConfig - TemplateFilter = NewTemplateFilter(c.GlobalString("vars")) - specFile := c.GlobalString("gossfile") + TemplateFilter = NewTemplateFilter(ctx.Vars) + specFile := ctx.Gossfile if specFile == "-" { source = "STDIN" fh = os.Stdin @@ -50,33 +50,33 @@ func getGossConfig(c *cli.Context) GossConfig { return gossConfig } -func getOutputer(c *cli.Context) outputs.Outputer { - if c.Bool("no-color") { +func getOutputer(ctx app.CliContext) outputs.Outputer { + if ctx.NoColor { color.NoColor = true } - if c.Bool("color") { + if ctx.Color { color.NoColor = false } - return outputs.GetOutputer(c.String("format")) + return outputs.GetOutputer(ctx.Format) } // Validate validation runtime -func Validate(c *cli.Context, startTime time.Time) { +func Validate(ctx app.CliContext, startTime time.Time) { outputConfig := util.OutputConfig{ - FormatOptions: c.StringSlice("format-options"), + FormatOptions: ctx.FormatOptions, } - gossConfig := getGossConfig(c) - sys := system.New(c) - outputer := getOutputer(c) + gossConfig := getGossConfig(ctx) + sys := system.New(ctx.Package) + outputer := getOutputer(ctx) - sleep := c.Duration("sleep") - retryTimeout := c.Duration("retry-timeout") + sleep := ctx.Sleep + retryTimeout := ctx.RetryTimeout i := 1 for { iStartTime := time.Now() - out := validate(sys, gossConfig, c.Int("max-concurrent")) + out := validate(sys, gossConfig, ctx.MaxConcurrent) exitCode := outputer.Output(os.Stdout, out, iStartTime, outputConfig) if retryTimeout == 0 || exitCode == 0 { os.Exit(exitCode) @@ -88,7 +88,7 @@ func Validate(c *cli.Context, startTime time.Time) { } color.Red("Retrying in %s (elapsed/timeout time: %.3fs/%s)\n\n\n", sleep, elapsed.Seconds(), retryTimeout) // Reset cache - sys = system.New(c) + sys = system.New(ctx.Package) time.Sleep(sleep) i++ fmt.Printf("Attempt #%d:\n", i) From a113c8fc31fb2e9944f0968784b90b4b648e14e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Sun, 12 May 2019 21:08:32 +0200 Subject: [PATCH 02/19] Remove more cli.Context references and separate structs --- app.go | 62 +++++++++++++++++++++++ cmd/goss/goss.go | 127 ++++++++++++++++++++++++++++++++--------------- serve.go | 74 ++++++++++++--------------- store.go | 2 +- validate.go | 105 +++++++++++++-------------------------- 5 files changed, 217 insertions(+), 153 deletions(-) create mode 100644 app.go diff --git a/app.go b/app.go new file mode 100644 index 0000000..62f2d4e --- /dev/null +++ b/app.go @@ -0,0 +1,62 @@ +package goss + +import ( + "fmt" + "github.com/SimonBaeumer/goss/internal/app" + "io/ioutil" + "os" + "path/filepath" +) + +// GossRunTime represents the global runtime configs which can be set in goss +type GossRunTime struct { + //Gossfile which should holds the test config + Gossfile string + //Vars file which holds the variabesl + Vars string + //Package defines which package manager you want to use, i.e. yum, apt, ... + Package string //this does not belong here imho +} + +func NewGossRunTime(ctx app.CliContext) *GossRunTime { + return &GossRunTime{} +} + +func (g *GossRunTime) Serve() { + //Serve() +} + + +// GetGossConfig returns the goss configuration +func (g *GossRunTime) GetGossConfig() GossConfig { + // handle stdin + var fh *os.File + var path, source string + var gossConfig GossConfig + TemplateFilter = NewTemplateFilter(g.Vars) + specFile := g.Gossfile + if specFile == "-" { + source = "STDIN" + fh = os.Stdin + data, err := ioutil.ReadAll(fh) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + OutStoreFormat = getStoreFormatFromData(data) + gossConfig = ReadJSONData(data, true) + } else { + source = specFile + path = filepath.Dir(specFile) + OutStoreFormat = getStoreFormatFromFileName(specFile) + gossConfig = ReadJSON(specFile) + } + + gossConfig = mergeJSONData(gossConfig, 0, path) + + if len(gossConfig.Resources()) == 0 { + fmt.Printf("Error: found 0 tests, source: %v\n", source) + os.Exit(1) + } + return gossConfig +} \ No newline at end of file diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 0162b32..ca4a18e 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -3,8 +3,12 @@ package main import ( "fmt" app2 "github.com/SimonBaeumer/goss/internal/app" - "log" + "github.com/SimonBaeumer/goss/system" + "github.com/fatih/color" + "github.com/patrickmn/go-cache" + "log" "os" + "sync" "time" "github.com/SimonBaeumer/goss" @@ -26,7 +30,7 @@ func main() { cli.StringFlag{ Name: "gossfile, g", Value: "./goss.yaml", - Usage: "Goss file to read from / write to", + Usage: "GossRunTime file to read from / write to", EnvVar: "GOSS_FILE", }, cli.StringFlag{ @@ -87,7 +91,26 @@ func main() { }, Action: func(c *cli.Context) error { ctx := app2.NewCliContext(c) - goss.Validate(ctx, startTime) + + runtime := getGossRunTime(ctx) + + v := &goss.Validator{ + MaxConcurrent: ctx.MaxConcurrent, + Package: ctx.Package, + Outputer: outputs.GetOutputer(ctx.Format), + FormatOptions: ctx.FormatOptions, + GossConfig: runtime.GetGossConfig(), + } + + //TODO: ugly shit to set the color here, tmp fix for the moment! + if ctx.NoColor { + color.NoColor = true + } + if ctx.Color { + color.NoColor = false + } + + v.Validate(startTime) return nil }, }, @@ -133,8 +156,25 @@ func main() { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.Serve(ctx) + ctx := app2.NewCliContext(c) + + gossRunTime := getGossRunTime(ctx) + + h := &goss.HealthHandler{ + Cache: cache.New(ctx.Cache, 30*time.Second), + ListenAddr: ctx.ListenAddr, + Outputer: outputs.GetOutputer(ctx.Format), + Sys: system.New(ctx.Package), + GossMu: &sync.Mutex{}, + MaxConcurrent: ctx.MaxConcurrent, + GossConfig: gossRunTime.GetGossConfig(), + } + + if ctx.Format == "json" { + h.ContentType = "application/json" + } + + h.Serve(ctx.Endpoint) return nil }, }, @@ -158,8 +198,8 @@ func main() { Aliases: []string{"aa"}, Usage: "automatically add all matching resource to the test suite", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) return nil }, }, @@ -178,8 +218,8 @@ func main() { Name: "package", Usage: "add new package", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) return nil }, }, @@ -187,8 +227,8 @@ func main() { Name: "file", Usage: "add new file", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) return nil }, }, @@ -202,8 +242,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) return nil }, }, @@ -211,8 +251,8 @@ func main() { Name: "port", Usage: "add new listening [protocol]:port - ex: 80 or udp:123", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) return nil }, }, @@ -220,8 +260,8 @@ func main() { Name: "service", Usage: "add new service", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) return nil }, }, @@ -229,8 +269,8 @@ func main() { Name: "user", Usage: "add new user", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) return nil }, }, @@ -238,8 +278,8 @@ func main() { Name: "group", Usage: "add new group", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) return nil }, }, @@ -253,8 +293,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) return nil }, }, @@ -272,8 +312,8 @@ func main() { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) return nil }, }, @@ -281,7 +321,7 @@ func main() { Name: "process", Usage: "add new process name", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) + ctx := app2.NewCliContext(c) goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), ctx) return nil }, @@ -301,21 +341,21 @@ func main() { Value: 5 * time.Second, }, cli.StringFlag{ - Name: "username, u", + Name: "username, u", Usage: "Username for basic auth", }, cli.StringFlag{ - Name: "password, p", + Name: "password, p", Usage: "Password for basic auth", }, cli.StringFlag{ - Name: "header", + Name: "header", Usage: "Set-Cookie: Value", }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) return nil }, }, @@ -323,8 +363,8 @@ func main() { Name: "goss", Usage: "add new goss file, it will be imported from this one", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) return nil }, }, @@ -332,8 +372,8 @@ func main() { Name: "kernel-param", Usage: "add new goss kernel param", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) return nil }, }, @@ -341,8 +381,8 @@ func main() { Name: "mount", Usage: "add new mount", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) return nil }, }, @@ -350,8 +390,8 @@ func main() { Name: "interface", Usage: "add new interface", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) return nil }, }, @@ -364,3 +404,12 @@ func main() { log.Fatal(err) } } + +func getGossRunTime(ctx app2.CliContext) goss.GossRunTime { + runtime := goss.GossRunTime{ + Gossfile: ctx.Gossfile, + Vars: ctx.Vars, + Package: ctx.Package, + } + return runtime +} diff --git a/serve.go b/serve.go index 080a48e..e0b4e05 100644 --- a/serve.go +++ b/serve.go @@ -15,74 +15,64 @@ import ( "github.com/patrickmn/go-cache" ) -func Serve(ctx app.CliContext) { - endpoint := ctx.Endpoint +//TODO: Maybe seperating handler and server? +type HealthHandler struct { + RunTimeConfig GossRunTime + C app.CliContext + GossConfig GossConfig + Sys *system.System + Outputer outputs.Outputer + Cache *cache.Cache + GossMu *sync.Mutex + ContentType string + MaxConcurrent int + ListenAddr string +} + +func (h *HealthHandler) Serve(endpoint string) { color.NoColor = true - cache := cache.New(ctx.Cache, 30*time.Second) - health := healthHandler{ - c: ctx, - gossConfig: getGossConfig(ctx), - sys: system.New(ctx.Package), - outputer: getOutputer(ctx), - cache: cache, - gossMu: &sync.Mutex{}, - maxConcurrent: ctx.MaxConcurrent, - } - if ctx.Format == "json" { - health.contentType = "application/json" - } - http.Handle(endpoint, health) - listenAddr := ctx.ListenAddr - log.Printf("Starting to listen on: %s", listenAddr) - log.Fatal(http.ListenAndServe(ctx.ListenAddr, nil)) + http.Handle(endpoint, h) + log.Printf("Starting to listen on: %s", h.ListenAddr) + log.Fatal(http.ListenAndServe(h.ListenAddr, nil)) } type res struct { exitCode int b bytes.Buffer } -type healthHandler struct { - c app.CliContext - gossConfig GossConfig - sys *system.System - outputer outputs.Outputer - cache *cache.Cache - gossMu *sync.Mutex - contentType string - maxConcurrent int -} -func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (h HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { outputConfig := util.OutputConfig{ - FormatOptions: h.c.FormatOptions, + FormatOptions: h.C.FormatOptions, } + log.Printf("%v: requesting health probe", r.RemoteAddr) var resp res - tmp, found := h.cache.Get("res") + tmp, found := h.Cache.Get("res") if found { resp = tmp.(res) } else { - h.gossMu.Lock() - defer h.gossMu.Unlock() - tmp, found := h.cache.Get("res") + h.GossMu.Lock() + defer h.GossMu.Unlock() + tmp, found := h.Cache.Get("res") if found { resp = tmp.(res) } else { - h.sys = system.New(h.c.Package) - log.Printf("%v: Stale cache, running tests", r.RemoteAddr) + h.Sys = system.New(h.C.Package) + log.Printf("%v: Stale Cache, running tests", r.RemoteAddr) iStartTime := time.Now() - out := validate(h.sys, h.gossConfig, h.maxConcurrent) + out := validate(h.Sys, h.GossConfig, h.MaxConcurrent) var b bytes.Buffer - exitCode := h.outputer.Output(&b, out, iStartTime, outputConfig) + exitCode := h.Outputer.Output(&b, out, iStartTime, outputConfig) resp = res{exitCode: exitCode, b: b} - h.cache.Set("res", resp, cache.DefaultExpiration) + h.Cache.Set("res", resp, cache.DefaultExpiration) } } - if h.contentType != "" { - w.Header().Set("Content-Type", h.contentType) + if h.ContentType != "" { + w.Header().Set("Content-Type", h.ContentType) } if resp.exitCode == 0 { resp.b.WriteTo(w) diff --git a/store.go b/store.go index cec7266..e2aa366 100644 --- a/store.go +++ b/store.go @@ -138,7 +138,7 @@ func mergeJSONData(gossConfig GossConfig, depth int, path string) GossConfig { fmt.Println("Error: Max depth of 50 reached, possibly due to dependency loop in goss file") os.Exit(1) } - // Our return gossConfig + // Our return GossConfig ret := *NewGossConfig() ret = mergeGoss(ret, gossConfig) diff --git a/validate.go b/validate.go index c61633a..a6b7e5a 100644 --- a/validate.go +++ b/validate.go @@ -1,95 +1,58 @@ package goss import ( - "fmt" - "github.com/SimonBaeumer/goss/internal/app" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "sync" - "time" - - "github.com/SimonBaeumer/goss/outputs" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" - "github.com/fatih/color" + "fmt" + "os" + "runtime" + "sync" + "time" + + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" + "github.com/fatih/color" ) -func getGossConfig(ctx app.CliContext) GossConfig { - // handle stdin - var fh *os.File - var path, source string - var gossConfig GossConfig - TemplateFilter = NewTemplateFilter(ctx.Vars) - specFile := ctx.Gossfile - if specFile == "-" { - source = "STDIN" - fh = os.Stdin - data, err := ioutil.ReadAll(fh) - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - OutStoreFormat = getStoreFormatFromData(data) - gossConfig = ReadJSONData(data, true) - } else { - source = specFile - path = filepath.Dir(specFile) - OutStoreFormat = getStoreFormatFromFileName(specFile) - gossConfig = ReadJSON(specFile) - } - - gossConfig = mergeJSONData(gossConfig, 0, path) - - if len(gossConfig.Resources()) == 0 { - fmt.Printf("Error: found 0 tests, source: %v\n", source) - os.Exit(1) - } - return gossConfig -} - -func getOutputer(ctx app.CliContext) outputs.Outputer { - if ctx.NoColor { - color.NoColor = true - } - if ctx.Color { - color.NoColor = false - } - return outputs.GetOutputer(ctx.Format) +type Validator struct { + GossConfig GossConfig + RetryTimeout time.Duration + Sleep time.Duration + FormatOptions []string + Outputer outputs.Outputer + Package string //Should be in the package resource config + MaxConcurrent int //Separating concurrency and validation, irritating atm... } // Validate validation runtime -func Validate(ctx app.CliContext, startTime time.Time) { +func (v *Validator) Validate(startTime time.Time) { outputConfig := util.OutputConfig{ - FormatOptions: ctx.FormatOptions, + FormatOptions: v.FormatOptions, } - gossConfig := getGossConfig(ctx) - sys := system.New(ctx.Package) - outputer := getOutputer(ctx) + sys := system.New(v.Package) - sleep := ctx.Sleep - retryTimeout := ctx.RetryTimeout i := 1 for { iStartTime := time.Now() - out := validate(sys, gossConfig, ctx.MaxConcurrent) - exitCode := outputer.Output(os.Stdout, out, iStartTime, outputConfig) - if retryTimeout == 0 || exitCode == 0 { + + out := validate(sys, v.GossConfig, v.MaxConcurrent) + exitCode := v.Outputer.Output(os.Stdout, out, iStartTime, outputConfig) + if v.RetryTimeout == 0 || exitCode == 0 { os.Exit(exitCode) } + elapsed := time.Since(startTime) - if elapsed+sleep > retryTimeout { - color.Red("\nERROR: Timeout of %s reached before tests entered a passing state", retryTimeout) + if elapsed + v.Sleep > v.RetryTimeout { + color.Red("\nERROR: Timeout of %s reached before tests entered a passing state", v.RetryTimeout) os.Exit(3) } - color.Red("Retrying in %s (elapsed/timeout time: %.3fs/%s)\n\n\n", sleep, elapsed.Seconds(), retryTimeout) - // Reset cache - sys = system.New(ctx.Package) - time.Sleep(sleep) + color.Red("Retrying in %s (elapsed/timeout time: %.3fs/%s)\n\n\n", v.Sleep, elapsed.Seconds(), v.RetryTimeout) + + // Reset Cache + sys = system.New(v.Package) + time.Sleep(v.Sleep) i++ fmt.Printf("Attempt #%d:\n", i) } From 68fa13078cf6a419e5486fb7d34dc7e917401b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 13 May 2019 20:30:58 +0200 Subject: [PATCH 03/19] Add healthcheck unit test --- serve.go | 4 ++-- serve_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 serve_test.go diff --git a/serve.go b/serve.go index e0b4e05..9b54bdd 100644 --- a/serve.go +++ b/serve.go @@ -42,13 +42,13 @@ type res struct { b bytes.Buffer } +//ServeHTTP fulfills the handler interface and is called as a handler on the +//health check request. func (h HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - outputConfig := util.OutputConfig{ FormatOptions: h.C.FormatOptions, } - log.Printf("%v: requesting health probe", r.RemoteAddr) var resp res tmp, found := h.Cache.Get("res") diff --git a/serve_test.go b/serve_test.go new file mode 100644 index 0000000..1af652e --- /dev/null +++ b/serve_test.go @@ -0,0 +1,51 @@ +package goss + +import ( + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" +) + +func TestHealthHandler_Serve(t *testing.T) { + req, err := http.NewRequest("GET", "/healthz", nil) + if err != nil { + t.Fatal(err) + } + + //var cmdResource resource.Resource + cmdResource := &resource.Command{ + Command: "echo hello", + Title: "echo hello", + ExitStatus: 0, + } + + h := HealthHandler{ + Cache: cache.New(time.Duration(50), time.Duration(50)), + Outputer: outputs.GetOutputer("documentation"), + MaxConcurrent: 1, + ListenAddr: "9999", + ContentType: "application/json", + GossMu: &sync.Mutex{}, + GossConfig: GossConfig{ + Commands: resource.CommandMap{"echo hello": cmdResource}, + }, + } + rr := httptest.NewRecorder() + + h.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("Health check failed!") + } + + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "Title: echo hello") + assert.Contains(t, rr.Body.String(), "Command: echo hello: exit-status: matches expectation: [0]") + assert.Contains(t, rr.Body.String(), "Count: 1, Failed: 0, Skipped: 0") +} From 8ae054b43067051298fb9ed57d8aa8c1757b21bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 13 May 2019 21:07:29 +0200 Subject: [PATCH 04/19] Add Validator.Validate test --- cmd/goss/goss.go | 2 +- serve_test.go | 1 - validate.go | 33 +++++++++++++++++++-------------- validate_test.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 validate_test.go diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index ca4a18e..16d862d 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -110,7 +110,7 @@ func main() { color.NoColor = false } - v.Validate(startTime) + os.Exit(v.Validate(startTime)) return nil }, }, diff --git a/serve_test.go b/serve_test.go index 1af652e..c191389 100644 --- a/serve_test.go +++ b/serve_test.go @@ -18,7 +18,6 @@ func TestHealthHandler_Serve(t *testing.T) { t.Fatal(err) } - //var cmdResource resource.Resource cmdResource := &resource.Command{ Command: "echo hello", Title: "echo hello", diff --git a/validate.go b/validate.go index a6b7e5a..21d6eeb 100644 --- a/validate.go +++ b/validate.go @@ -1,17 +1,18 @@ package goss import ( - "fmt" + "fmt" + "io" "os" - "runtime" - "sync" - "time" - - "github.com/SimonBaeumer/goss/outputs" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" - "github.com/fatih/color" + "runtime" + "sync" + "time" + + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" + "github.com/fatih/color" ) type Validator struct { @@ -22,10 +23,14 @@ type Validator struct { Outputer outputs.Outputer Package string //Should be in the package resource config MaxConcurrent int //Separating concurrency and validation, irritating atm... + OutputWriter io.Writer } // Validate validation runtime -func (v *Validator) Validate(startTime time.Time) { +func (v *Validator) Validate(startTime time.Time) int { + if v.OutputWriter == nil { + v.OutputWriter = os.Stdout + } outputConfig := util.OutputConfig{ FormatOptions: v.FormatOptions, @@ -38,15 +43,15 @@ func (v *Validator) Validate(startTime time.Time) { iStartTime := time.Now() out := validate(sys, v.GossConfig, v.MaxConcurrent) - exitCode := v.Outputer.Output(os.Stdout, out, iStartTime, outputConfig) + exitCode := v.Outputer.Output(v.OutputWriter, out, iStartTime, outputConfig) if v.RetryTimeout == 0 || exitCode == 0 { - os.Exit(exitCode) + return exitCode } elapsed := time.Since(startTime) if elapsed + v.Sleep > v.RetryTimeout { color.Red("\nERROR: Timeout of %s reached before tests entered a passing state", v.RetryTimeout) - os.Exit(3) + return exitCode } color.Red("Retrying in %s (elapsed/timeout time: %.3fs/%s)\n\n\n", v.Sleep, elapsed.Seconds(), v.RetryTimeout) diff --git a/validate_test.go b/validate_test.go new file mode 100644 index 0000000..aed9790 --- /dev/null +++ b/validate_test.go @@ -0,0 +1,31 @@ +package goss + +import ( + "bytes" + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestValidator_Validate(t *testing.T) { + cmdResource := &resource.Command{Title: "echo hello", Command: "echo hello", ExitStatus: 0} + + w := &bytes.Buffer{} + v := Validator{ + GossConfig: GossConfig{ + Commands: resource.CommandMap{"echo hello": cmdResource}, + }, + MaxConcurrent: 1, + Outputer: outputs.GetOutputer("documentation"), + OutputWriter: w, + } + + r := v.Validate(time.Now()) + + assert.Equal(t, 0, r) + assert.Contains(t, w.String(), "Title: echo hello") + assert.Contains(t, w.String(), "Command: echo hello: exit-status: matches expectation: [0]") + assert.Contains(t, w.String(), "Count: 1, Failed: 0, Skipped: 0") +} \ No newline at end of file From a6935bdbfecac516a4a2698d4fc3da4875d21eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 13 May 2019 21:22:21 +0200 Subject: [PATCH 05/19] Add more resource tests --- resource/addr.go | 1 + validate_test.go | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/resource/addr.go b/resource/addr.go index 476ec3a..0ddd69e 100644 --- a/resource/addr.go +++ b/resource/addr.go @@ -7,6 +7,7 @@ import ( const DefaultTimeoutMS = 500 +// Addr resource validates a addr, i.e. tcp://127.0.0.1:80 type Addr struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` diff --git a/validate_test.go b/validate_test.go index aed9790..62451db 100644 --- a/validate_test.go +++ b/validate_test.go @@ -10,12 +10,24 @@ import ( ) func TestValidator_Validate(t *testing.T) { - cmdResource := &resource.Command{Title: "echo hello", Command: "echo hello", ExitStatus: 0} + cmdRes := &resource.Command{Title: "echo hello", Command: "echo hello", ExitStatus: 0} + fileRes := &resource.File{Title: "/tmp", Path: "/tmp", Filetype: "directory", Exists: true} + addrRes := &resource.Addr{Title: "tcp://google.com:443", Address: "tcp://google.com:443", Reachable: true} + httpRes := &resource.HTTP{Title: "https://google.com", HTTP: "https://google.com", Status: 200} + userRes := &resource.User{Title: "root", Username: "root", Exists: true} + groupRes := &resource.Group{Title: "root", Groupname: "root", Exists: true} + dnsRes := &resource.DNS{Title: "A:google.com", Host: "A:google.com", Resolvable: true} w := &bytes.Buffer{} v := Validator{ GossConfig: GossConfig{ - Commands: resource.CommandMap{"echo hello": cmdResource}, + Commands: resource.CommandMap{"echo hello": cmdRes}, + Files: resource.FileMap{"/tmp": fileRes}, + Addrs: resource.AddrMap{"127.0.0.1": addrRes}, + HTTPs: resource.HTTPMap{"https://google.com": httpRes}, + Users: resource.UserMap{"root": userRes}, + Groups: resource.GroupMap{"root": groupRes}, + DNS: resource.DNSMap{"A:https://google.com": dnsRes}, }, MaxConcurrent: 1, Outputer: outputs.GetOutputer("documentation"), @@ -25,7 +37,5 @@ func TestValidator_Validate(t *testing.T) { r := v.Validate(time.Now()) assert.Equal(t, 0, r) - assert.Contains(t, w.String(), "Title: echo hello") - assert.Contains(t, w.String(), "Command: echo hello: exit-status: matches expectation: [0]") - assert.Contains(t, w.String(), "Count: 1, Failed: 0, Skipped: 0") + assert.Contains(t, w.String(), "Count: 8, Failed: 0, Skipped: 0") } \ No newline at end of file From 219e55ca768bd42797b89116cda7176a20ac02a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 13 May 2019 21:42:14 +0200 Subject: [PATCH 06/19] Ignore development dir in test coverage --- novendor.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/novendor.sh b/novendor.sh index 2b57337..dad96c8 100755 --- a/novendor.sh +++ b/novendor.sh @@ -9,6 +9,11 @@ DIRS=$(ls -ld */ . | awk {'print $9'} | grep -v vendor) for DIR in ${DIRS}; do GOFILES=$(git ls-files ${DIR} | grep ".*\.go$") || true + # ignore dev directory... + if [[ ${DIR} == "development/" ]]; then + continue + fi + if [[ ${DIR} == "." ]]; then echo "." continue From f971032fa5f684fe573f39d8362cf92fb0ece22b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Thu, 16 May 2019 11:29:04 +0200 Subject: [PATCH 07/19] Add output tests --- outputs/documentation.go | 15 ++++++-- outputs/documentation_test.go | 46 ++++++++++++++++++++++++ outputs/json.go | 14 +++++++- outputs/json_oneline.go | 5 +++ outputs/json_test.go | 67 +++++++++++++++++++++++++++++++++++ outputs/junit.go | 5 +++ outputs/nagios.go | 5 +++ outputs/outputs.go | 9 +++-- outputs/rspecish.go | 16 +++++++-- outputs/rspecish_test.go | 45 +++++++++++++++++++++++ outputs/silent.go | 5 +++ outputs/tap.go | 5 +++ util/goss_testing/helper.go | 21 +++++++++++ validate.go | 1 - 14 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 outputs/documentation_test.go create mode 100644 outputs/json_test.go create mode 100644 outputs/rspecish_test.go create mode 100644 util/goss_testing/helper.go diff --git a/outputs/documentation.go b/outputs/documentation.go index f4d65b7..6fd8c36 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -9,7 +9,14 @@ import ( "github.com/SimonBaeumer/goss/util" ) -type Documentation struct{} +// Documentation represents the documentation output type +type Documentation struct{ + //FakeDuration will only be used for testing purposes + FakeDuration time.Duration +} + +// Name returns the name +func (r Documentation) Name() string { return "documentation" } func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { @@ -47,7 +54,11 @@ func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, "\n\n") fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped)) - fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + duration := time.Since(startTime) + if r.FakeDuration != 0 { + duration = r.FakeDuration + } + fmt.Fprint(w, summary(duration.Seconds(), testCount, failed, skipped)) if failed > 0 { return 1 } diff --git a/outputs/documentation_test.go b/outputs/documentation_test.go new file mode 100644 index 0000000..71f56c4 --- /dev/null +++ b/outputs/documentation_test.go @@ -0,0 +1,46 @@ +package outputs + +import ( + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" +) + +func TestDocumentation_Name(t *testing.T) { + j := Documentation{} + assert.Equal(t, "documentation", j.Name()) +} + +func TestDocumentation_Output(t *testing.T) { + var wg sync.WaitGroup + b := &bytes.Buffer{} + d, _ := time.ParseDuration("2s") + j := Documentation{FakeDuration: d} + out := make(chan []resource.TestResult) + r := 1 + + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() + + out <- goss_testing.GetExampleTestResult() + + close(out) + wg.Wait() + expectedJson := `Title: my title +resource type: my resource id: a property: matches expectation: [expected] + + +Total Duration: 2.000s +Count: 1, Failed: 0, Skipped: 0 +` + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) +} diff --git a/outputs/json.go b/outputs/json.go index babcf8f..343c979 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -11,8 +11,16 @@ import ( "github.com/fatih/color" ) -type Json struct{} +// Json represents the json output type +type Json struct{ + // FakeDuration will only be used for testing purposes + FakeDuration time.Duration +} + +// Name returns the name +func (r Json) Name() string { return "json" } +// Output writes the actual output func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { @@ -35,6 +43,10 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, summary := make(map[string]interface{}) duration := time.Since(startTime) + if r.FakeDuration != 0 { + duration = r.FakeDuration + } + summary["test-count"] = testCount summary["failed-count"] = failed summary["total-duration"] = duration diff --git a/outputs/json_oneline.go b/outputs/json_oneline.go index 8453e8d..65a355f 100644 --- a/outputs/json_oneline.go +++ b/outputs/json_oneline.go @@ -11,8 +11,13 @@ import ( "github.com/fatih/color" ) +// JsonOneline represents the JsonOneline output type type JsonOneline struct{} +// Name returns the name +func (r JsonOneline) Name() string { return "json_oneline" } + +// Output writes the actual output func (r JsonOneline) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { diff --git a/outputs/json_test.go b/outputs/json_test.go new file mode 100644 index 0000000..8b7b38d --- /dev/null +++ b/outputs/json_test.go @@ -0,0 +1,67 @@ +package outputs + +import ( + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" +) + +func TestJson_Name(t *testing.T) { + j := Json{} + assert.Equal(t, "json", j.Name()) +} + +func TestJson_Output(t *testing.T) { + var wg sync.WaitGroup + b := &bytes.Buffer{} + j := Json{FakeDuration: 1000} + out := make(chan []resource.TestResult) + r := 1 + + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() + + out <- goss_testing.GetExampleTestResult() + + close(out) + wg.Wait() + expectedJson := `{ + "results": [ + { + "duration": 500, + "err": null, + "expected": [ + "expected" + ], + "found": null, + "human": "", + "meta": null, + "property": "a property", + "resource-id": "my resource id", + "resource-type": "resource type", + "result": 0, + "successful": true, + "summary-line": "resource type: my resource id: a property: matches expectation: [expected]", + "test-type": 0, + "title": "my title" + } + ], + "summary": { + "failed-count": 0, + "summary-line": "Count: 1, Failed: 0, Duration: 0.000s", + "test-count": 1, + "total-duration": 1000 + } +} +` + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) +} diff --git a/outputs/junit.go b/outputs/junit.go index 480a90b..12c5b2b 100644 --- a/outputs/junit.go +++ b/outputs/junit.go @@ -13,8 +13,13 @@ import ( "github.com/fatih/color" ) +// JUnit represents the junit output type type JUnit struct{} +// Name returns the name +func (r JUnit) Name() string { return "junit" } + +// Output writes the actual output func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { diff --git a/outputs/nagios.go b/outputs/nagios.go index d7a080e..eee3722 100644 --- a/outputs/nagios.go +++ b/outputs/nagios.go @@ -10,8 +10,13 @@ import ( "github.com/SimonBaeumer/goss/util" ) +// Nagios represents the nagios output type type Nagios struct{} +// Name returns the name +func (r Nagios) Name() string { return "nagios" } + +// Output writes the actual output func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { diff --git a/outputs/outputs.go b/outputs/outputs.go index 22f1c22..994e11d 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -14,8 +14,10 @@ import ( "github.com/fatih/color" ) +// Outputer is the interface which is used for the generation of the view type Outputer interface { Output(io.Writer, <-chan []resource.TestResult, time.Time, util.OutputConfig) int + Name() string } var green = color.New(color.FgGreen).SprintfFunc() @@ -84,6 +86,7 @@ var ( outputerFormatOptions = make(map[string][]string) ) +// RegisterOutputer registers a new outputer in the registry func RegisterOutputer(name string, outputer Outputer, formatOptions []string) { outputersMu.Lock() defer outputersMu.Unlock() @@ -125,6 +128,7 @@ func FormatOptions() []string { return list } +//GetOutputer returns an outputer by name func GetOutputer(name string) Outputer { if _, ok := outputers[name]; !ok { fmt.Println("goss: Bad output format: " + name) @@ -171,9 +175,9 @@ func header(t resource.TestResult) string { return out } -func summary(startTime time.Time, count, failed, skipped int) string { +func summary(duration float64, count, failed, skipped int) string { var s string - s += fmt.Sprintf("Total Duration: %.3fs\n", time.Since(startTime).Seconds()) + s += fmt.Sprintf("Total Duration: %.3fs\n", duration) f := green if failed > 0 { f = red @@ -181,6 +185,7 @@ func summary(startTime time.Time, count, failed, skipped int) string { s += f("Count: %d, Failed: %d, Skipped: %d\n", count, failed, skipped) return s } + func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult) string { var s string if len(failedOrSkipped) > 0 { diff --git a/outputs/rspecish.go b/outputs/rspecish.go index 2d72026..fdb16d8 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -9,8 +9,16 @@ import ( "github.com/SimonBaeumer/goss/util" ) -type Rspecish struct{} +// Rspecish represents the rspecish output type +type Rspecish struct{ + //FakeDuration will only be needed for testing purposes + FakeDuration time.Duration +} + +// Name returns the name +func (r Rspecish) Name() string { return "rspecish" } +// Output writes the actual output func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { @@ -42,7 +50,11 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, "\n\n") fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped)) - fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + duration := time.Since(startTime) + if r.FakeDuration != 0 { + duration = r.FakeDuration + } + fmt.Fprint(w, summary(duration.Seconds(), testCount, failed, skipped)) if failed > 0 { return 1 } diff --git a/outputs/rspecish_test.go b/outputs/rspecish_test.go new file mode 100644 index 0000000..3310ef8 --- /dev/null +++ b/outputs/rspecish_test.go @@ -0,0 +1,45 @@ +package outputs + +import ( + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" +) + +func TestRspecish_Name(t *testing.T) { + j := Rspecish{} + assert.Equal(t, "rspecish", j.Name()) +} + +func TestRspecish_Output(t *testing.T) { + var wg sync.WaitGroup + b := &bytes.Buffer{} + d, _ := time.ParseDuration("2s") + j := Rspecish{FakeDuration: d} + out := make(chan []resource.TestResult) + r := 1 + + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() + + out <- goss_testing.GetExampleTestResult() + + close(out) + wg.Wait() + expectedJson := `. + +Total Duration: 2.000s +Count: 1, Failed: 0, Skipped: 0 +` + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) +} + diff --git a/outputs/silent.go b/outputs/silent.go index 353ae4c..a925a3b 100644 --- a/outputs/silent.go +++ b/outputs/silent.go @@ -8,8 +8,13 @@ import ( "github.com/SimonBaeumer/goss/util" ) +// Silent represents the silent output type type Silent struct{} +// Name returns the name +func (r Silent) Name() string { return "silent" } + +// Output writes the actual output func (r Silent) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { diff --git a/outputs/tap.go b/outputs/tap.go index 4b6563b..d77a59f 100644 --- a/outputs/tap.go +++ b/outputs/tap.go @@ -10,8 +10,13 @@ import ( "github.com/SimonBaeumer/goss/util" ) +// Tap represents the tap output type type Tap struct{} +// Name returns the name +func (r Tap) Name() string { return "tap" } + +// Output writes the actual output func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { diff --git a/util/goss_testing/helper.go b/util/goss_testing/helper.go new file mode 100644 index 0000000..e31e31d --- /dev/null +++ b/util/goss_testing/helper.go @@ -0,0 +1,21 @@ +package goss_testing + +import ( + "github.com/SimonBaeumer/goss/resource" + "time" +) + +func GetExampleTestResult() []resource.TestResult { + return []resource.TestResult{ + { + Title: "my title", + Duration: time.Duration(500), + Successful: true, + ResourceType: "resource type", + ResourceId: "my resource id", + Property: "a property", + Expected: []string{"expected"}, + }, + } + +} \ No newline at end of file diff --git a/validate.go b/validate.go index 21d6eeb..f502b64 100644 --- a/validate.go +++ b/validate.go @@ -88,7 +88,6 @@ func validate(sys *system.System, gossConfig GossConfig, maxConcurrent int) <-ch for res := range in { out <- res.Validate(sys) } - }() } From 623e87d4f454a68edc7e2b498342c4a083a6864d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Thu, 16 May 2019 12:54:51 +0200 Subject: [PATCH 08/19] Remove global package option, instead add package-manager property --- CHANGELOG.md | 1 + add.go | 4 ++-- cmd/goss/goss.go | 11 +-------- docs/manual.md | 3 +-- outputs/rspecish_test.go | 1 - resource/package.go | 13 ++++++----- resource/resource_list.go | 4 ++-- serve.go | 2 +- system/package.go | 43 +++++++++++++++++++++++++++++++++++ system/package_alpine.go | 2 +- system/package_deb.go | 2 +- system/package_pacman.go | 8 ++++++- system/package_rpm.go | 2 +- system/package_test.go | 25 ++++++++++++++++++++ system/system.go | 48 +++------------------------------------ validate.go | 4 ++-- 16 files changed, 98 insertions(+), 75 deletions(-) create mode 100644 system/package_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb1bfb..498057e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # v0.6.0 - Fixed a bug where DNS record lookups never were fired wihout a nameserver + - Removed global `--package` option, added `package-manager` property to `package` resource # v0.5.0 diff --git a/add.go b/add.go index d6c2f73..f088090 100644 --- a/add.go +++ b/add.go @@ -33,7 +33,7 @@ func AddResources(fileName, resourceName string, keys []string, ctx app.CliConte gossConfig = *NewGossConfig() } - sys := system.New(ctx.Package) + sys := system.New() for _, key := range keys { if err := AddResource(fileName, gossConfig, resourceName, key, config, sys); err != nil { @@ -182,7 +182,7 @@ func AutoAddResources(fileName string, keys []string, ctx app.CliContext) error gossConfig = *NewGossConfig() } - sys := system.New(ctx.Package) + sys := system.New() for _, key := range keys { if err := AutoAddResource(fileName, gossConfig, key, sys); err != nil { diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 16d862d..0499edf 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -164,7 +164,7 @@ func main() { Cache: cache.New(ctx.Cache, 30*time.Second), ListenAddr: ctx.ListenAddr, Outputer: outputs.GetOutputer(ctx.Format), - Sys: system.New(ctx.Package), + Sys: system.New(), GossMu: &sync.Mutex{}, MaxConcurrent: ctx.MaxConcurrent, GossConfig: gossRunTime.GetGossConfig(), @@ -214,15 +214,6 @@ func main() { }, }, Subcommands: []cli.Command{ - { - Name: "package", - Usage: "add new package", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) - return nil - }, - }, { Name: "file", Usage: "add new file", diff --git a/docs/manual.md b/docs/manual.md index 705b624..a54a4d3 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -702,12 +702,11 @@ package: # required attributes installed: true # optional attributes + package: pacman #set a specific package manager versions: - 2.2.15 ``` -**NOTE:** this check uses the `--package ` parameter passed on the command line. - ### port Validates the state of a local port. diff --git a/outputs/rspecish_test.go b/outputs/rspecish_test.go index 3310ef8..1ef456d 100644 --- a/outputs/rspecish_test.go +++ b/outputs/rspecish_test.go @@ -42,4 +42,3 @@ Count: 1, Failed: 0, Skipped: 0 assert.Equal(t, expectedJson, b.String()) assert.Equal(t, 0, r) } - diff --git a/resource/package.go b/resource/package.go index c60f397..c10386a 100644 --- a/resource/package.go +++ b/resource/package.go @@ -6,11 +6,12 @@ import ( ) type Package struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Name string `json:"-" yaml:"-"` - Installed matcher `json:"installed" yaml:"installed"` - Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Name string `json:"-" yaml:"-"` + Installed matcher `json:"installed" yaml:"installed"` + Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` + PackageManager string `json:"package-manager,omitempty" yaml:"package-manager,omitempty"` } func (p *Package) ID() string { return p.Name } @@ -21,7 +22,7 @@ func (p *Package) GetMeta() meta { return p.Meta } func (p *Package) Validate(sys *system.System) []TestResult { skip := false - sysPkg := sys.NewPackage(p.Name, sys, util.Config{}) + sysPkg := sys.NewPackage(p.Name, p.PackageManager) var results []TestResult results = append(results, ValidateValue(p, "installed", p.Installed, sysPkg.Installed, skip)) diff --git a/resource/resource_list.go b/resource/resource_list.go index 01f0869..aeecd83 100644 --- a/resource/resource_list.go +++ b/resource/resource_list.go @@ -621,7 +621,7 @@ func (ret *GroupMap) UnmarshalYAML(unmarshal func(v interface{}) error) error { type PackageMap map[string]*Package func (r PackageMap) AppendSysResource(sr string, sys *system.System, config util.Config) (*Package, error) { - sysres := sys.NewPackage(sr, sys, config) + sysres := sys.NewPackage(sr, "") res, err := NewPackage(sysres, config) if err != nil { return nil, err @@ -635,7 +635,7 @@ func (r PackageMap) AppendSysResource(sr string, sys *system.System, config util } func (r PackageMap) AppendSysResourceIfExists(sr string, sys *system.System) (*Package, system.Package, bool) { - sysres := sys.NewPackage(sr, sys, util.Config{}) + sysres := sys.NewPackage(sr, "") // FIXME: Do we want to be silent about errors? res, _ := NewPackage(sysres, util.Config{}) if e, _ := sysres.Exists(); e != true { diff --git a/serve.go b/serve.go index 9b54bdd..6d7a45e 100644 --- a/serve.go +++ b/serve.go @@ -61,7 +61,7 @@ func (h HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if found { resp = tmp.(res) } else { - h.Sys = system.New(h.C.Package) + h.Sys = system.New() log.Printf("%v: Stale Cache, running tests", r.RemoteAddr) iStartTime := time.Now() out := validate(h.Sys, h.GossConfig, h.MaxConcurrent) diff --git a/system/package.go b/system/package.go index c232290..db551ac 100644 --- a/system/package.go +++ b/system/package.go @@ -34,3 +34,46 @@ func (p *NullPackage) Installed() (bool, error) { func (p *NullPackage) Versions() ([]string, error) { return nil, ErrNullPackage } + +// DetectPackageManager attempts to detect whether or not the system is using +// "deb", "rpm", "apk", or "pacman" package managers. It first attempts to +// detect the distro. If that fails, it falls back to finding package manager +// executables. If that fails, it returns the empty string. +func DetectPackageManager() string { + switch DetectDistro() { + case "ubuntu": + return "deb" + case "redhat": + return "rpm" + case "alpine": + return "apk" + case "arch": + return "pacman" + case "debian": + return "deb" + } + for _, manager := range []string{"deb", "rpm", "apk", "pacman"} { + if HasCommand(manager) { + return manager + } + } + return "" +} + +// NewPackage is the constructor method which creates the correct package manager +// If pkgManager is empty the package manager will be automatically detected +func NewPackage(name string, pkgManager string) Package { + if pkgManager != "deb" && pkgManager != "apk" && pkgManager != "pacman" && pkgManager != "rpm" { + pkgManager = DetectPackageManager() + } + switch pkgManager { + case "deb": + return NewDebPackage(name) + case "apk": + return NewAlpinePackage(name) + case "pacman": + return NewPacmanPackage(name) + default: + return NewRpmPackage(name) + } +} diff --git a/system/package_alpine.go b/system/package_alpine.go index ff000b9..d231297 100644 --- a/system/package_alpine.go +++ b/system/package_alpine.go @@ -14,7 +14,7 @@ type AlpinePackage struct { installed bool } -func NewAlpinePackage(name string, system *System, config util.Config) Package { +func NewAlpinePackage(name string) Package { return &AlpinePackage{name: name} } diff --git a/system/package_deb.go b/system/package_deb.go index c229373..61dd6cb 100644 --- a/system/package_deb.go +++ b/system/package_deb.go @@ -14,7 +14,7 @@ type DebPackage struct { installed bool } -func NewDebPackage(name string, system *System, config util.Config) Package { +func NewDebPackage(name string) Package { return &DebPackage{name: name} } diff --git a/system/package_pacman.go b/system/package_pacman.go index 37f5138..58ed57f 100644 --- a/system/package_pacman.go +++ b/system/package_pacman.go @@ -7,6 +7,7 @@ import ( "github.com/SimonBaeumer/goss/util" ) +//PackmanPackage represents a package inside the pacman manager type PacmanPackage struct { name string versions []string @@ -14,7 +15,8 @@ type PacmanPackage struct { installed bool } -func NewPacmanPackage(name string, system *System, config util.Config) Package { +//NewPacmanPackage creates a new pacman manager +func NewPacmanPackage(name string) Package { return &PacmanPackage{name: name} } @@ -34,18 +36,22 @@ func (p *PacmanPackage) setup() { p.versions = []string{strings.Fields(cmd.Stdout.String())[1]} } +// Name returns the name of the package func (p *PacmanPackage) Name() string { return p.name } +// Exists returns if the package is installed func (p *PacmanPackage) Exists() (bool, error) { return p.Installed() } +// Installed will check and returns if the package is installed func (p *PacmanPackage) Installed() (bool, error) { p.setup() return p.installed, nil } +// Versions returns all installed versions of the package func (p *PacmanPackage) Versions() ([]string, error) { p.setup() if len(p.versions) == 0 { diff --git a/system/package_rpm.go b/system/package_rpm.go index 8a3faf4..0a9d034 100644 --- a/system/package_rpm.go +++ b/system/package_rpm.go @@ -14,7 +14,7 @@ type RpmPackage struct { installed bool } -func NewRpmPackage(name string, system *System, config util.Config) Package { +func NewRpmPackage(name string) Package { return &RpmPackage{name: name} } diff --git a/system/package_test.go b/system/package_test.go new file mode 100644 index 0000000..2393112 --- /dev/null +++ b/system/package_test.go @@ -0,0 +1,25 @@ +package system + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewPackage(t *testing.T) { + deb := NewPackage("package", "deb") + rpm := NewPackage("package", "rpm") + pac := NewPackage("package", "pacman") + apk := NewPackage("package", "apk") + + assert.Implements(t, new(Package), deb) + assert.IsType(t, &DebPackage{}, deb) + + assert.Implements(t, new(Package), rpm) + assert.IsType(t, &RpmPackage{}, rpm) + + assert.Implements(t, new(Package), pac) + assert.IsType(t, &PacmanPackage{}, pac) + + assert.Implements(t, new(Package), apk) + assert.IsType(t, &AlpinePackage{}, apk) +} diff --git a/system/system.go b/system/system.go index f569235..2a65c67 100644 --- a/system/system.go +++ b/system/system.go @@ -19,7 +19,7 @@ type Resource interface { // System holds all constructor functions for each type System struct { - NewPackage func(string, *System, util2.Config) Package + NewPackage func(string, string) Package NewFile func(string, *System, util2.Config) File NewAddr func(string, *System, util2.Config) Addr NewPort func(string, *System, util2.Config) Port @@ -55,8 +55,9 @@ func (s *System) ProcMap() map[string][]ps.Process { } //New creates the system object which holds all constructors for the system packages -func New(packageManger string) *System { +func New() *System { sys := &System{ + NewPackage: NewPackage, NewFile: NewDefFile, NewAddr: NewDefAddr, NewPort: NewDefPort, @@ -72,27 +73,9 @@ func New(packageManger string) *System { NewHTTP: NewDefHTTP, } sys.detectService() - sys.detectPackage(packageManger) return sys } -// DetectPackage adds the correct package creation function to a System struct -func (sys *System) detectPackage(pkgManager string) { - if pkgManager != "deb" && pkgManager != "apk" && pkgManager != "pacman" && pkgManager != "rpm" { - pkgManager = DetectPackageManager() - } - switch pkgManager { - case "deb": - sys.NewPackage = NewDebPackage - case "apk": - sys.NewPackage = NewAlpinePackage - case "pacman": - sys.NewPackage = NewPacmanPackage - default: - sys.NewPackage = NewRpmPackage - } -} - // DetectService adds the correct service creation function to a System struct func (sys *System) detectService() { switch DetectService() { @@ -107,31 +90,6 @@ func (sys *System) detectService() { } } -// DetectPackageManager attempts to detect whether or not the system is using -// "deb", "rpm", "apk", or "pacman" package managers. It first attempts to -// detect the distro. If that fails, it falls back to finding package manager -// executables. If that fails, it returns the empty string. -func DetectPackageManager() string { - switch DetectDistro() { - case "ubuntu": - return "deb" - case "redhat": - return "rpm" - case "alpine": - return "apk" - case "arch": - return "pacman" - case "debian": - return "deb" - } - for _, manager := range []string{"deb", "rpm", "apk", "pacman"} { - if HasCommand(manager) { - return manager - } - } - return "" -} - // DetectService attempts to detect what kind of service management the system // is using, "systemd", "upstart", "alpineinit", or "init". It looks for systemctl // command to detect systemd, and falls back on DetectDistro otherwise. If it can't diff --git a/validate.go b/validate.go index f502b64..d8836ba 100644 --- a/validate.go +++ b/validate.go @@ -36,7 +36,7 @@ func (v *Validator) Validate(startTime time.Time) int { FormatOptions: v.FormatOptions, } - sys := system.New(v.Package) + sys := system.New() i := 1 for { @@ -56,7 +56,7 @@ func (v *Validator) Validate(startTime time.Time) int { color.Red("Retrying in %s (elapsed/timeout time: %.3fs/%s)\n\n\n", v.Sleep, elapsed.Seconds(), v.RetryTimeout) // Reset Cache - sys = system.New(v.Package) + sys = system.New() time.Sleep(v.Sleep) i++ fmt.Printf("Attempt #%d:\n", i) From 74e862e38874ec61ae61ac9140f63bbf620fbfc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Thu, 16 May 2019 13:10:51 +0200 Subject: [PATCH 09/19] Add package resource tests --- cmd/goss/goss.go | 14 ++++++---- internal/app/context.go | 2 -- outputs/documentation_test.go | 3 +-- outputs/json_test.go | 3 +-- outputs/rspecish_test.go | 3 +-- outputs/test_helper.go | 20 ++++++++++++++ resource/dns_test.go | 16 +++--------- resource/package_test.go | 49 +++++++++++++++++++++++++++++++++++ util/goss_testing/helper.go | 24 +++++------------ 9 files changed, 91 insertions(+), 43 deletions(-) create mode 100644 outputs/test_helper.go create mode 100644 resource/package_test.go diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 0499edf..c3f4c11 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -38,10 +38,6 @@ func main() { Usage: "json/yaml file containing variables for template", EnvVar: "GOSS_VARS", }, - cli.StringFlag{ - Name: "package", - Usage: "Package type to use [rpm, deb, apk, pacman]", - }, } app.Commands = []cli.Command{ { @@ -214,6 +210,15 @@ func main() { }, }, Subcommands: []cli.Command{ + { + Name: "package", + Usage: "add new package", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) + return nil + }, + }, { Name: "file", Usage: "add new file", @@ -400,7 +405,6 @@ func getGossRunTime(ctx app2.CliContext) goss.GossRunTime { runtime := goss.GossRunTime{ Gossfile: ctx.Gossfile, Vars: ctx.Vars, - Package: ctx.Package, } return runtime } diff --git a/internal/app/context.go b/internal/app/context.go index f13430f..e10f451 100644 --- a/internal/app/context.go +++ b/internal/app/context.go @@ -10,7 +10,6 @@ type CliContext struct { Sleep time.Duration RetryTimeout time.Duration MaxConcurrent int - Package string Vars string Gossfile string ExcludeAttr []string @@ -34,7 +33,6 @@ func NewCliContext(c *cli.Context) CliContext { FormatOptions: c.StringSlice("format-options"), Sleep: c.Duration("sleep"), RetryTimeout: c.Duration("retry-timeout"), - Package: c.String("package"), MaxConcurrent: c.Int("max-concurrent"), Vars: c.GlobalString("vars"), Gossfile: c.GlobalString("gossfile"), diff --git a/outputs/documentation_test.go b/outputs/documentation_test.go index 71f56c4..9761874 100644 --- a/outputs/documentation_test.go +++ b/outputs/documentation_test.go @@ -4,7 +4,6 @@ import ( "bytes" "github.com/SimonBaeumer/goss/resource" "github.com/SimonBaeumer/goss/util" - "github.com/SimonBaeumer/goss/util/goss_testing" "github.com/stretchr/testify/assert" "sync" "testing" @@ -30,7 +29,7 @@ func TestDocumentation_Output(t *testing.T) { r = j.Output(b, out, time.Now(), util.OutputConfig{}) }() - out <- goss_testing.GetExampleTestResult() + out <- GetExampleTestResult() close(out) wg.Wait() diff --git a/outputs/json_test.go b/outputs/json_test.go index 8b7b38d..3b1411c 100644 --- a/outputs/json_test.go +++ b/outputs/json_test.go @@ -4,7 +4,6 @@ import ( "bytes" "github.com/SimonBaeumer/goss/resource" "github.com/SimonBaeumer/goss/util" - "github.com/SimonBaeumer/goss/util/goss_testing" "github.com/stretchr/testify/assert" "sync" "testing" @@ -29,7 +28,7 @@ func TestJson_Output(t *testing.T) { r = j.Output(b, out, time.Now(), util.OutputConfig{}) }() - out <- goss_testing.GetExampleTestResult() + out <- GetExampleTestResult() close(out) wg.Wait() diff --git a/outputs/rspecish_test.go b/outputs/rspecish_test.go index 1ef456d..6abd46f 100644 --- a/outputs/rspecish_test.go +++ b/outputs/rspecish_test.go @@ -4,7 +4,6 @@ import ( "bytes" "github.com/SimonBaeumer/goss/resource" "github.com/SimonBaeumer/goss/util" - "github.com/SimonBaeumer/goss/util/goss_testing" "github.com/stretchr/testify/assert" "sync" "testing" @@ -30,7 +29,7 @@ func TestRspecish_Output(t *testing.T) { r = j.Output(b, out, time.Now(), util.OutputConfig{}) }() - out <- goss_testing.GetExampleTestResult() + out <- GetExampleTestResult() close(out) wg.Wait() diff --git a/outputs/test_helper.go b/outputs/test_helper.go new file mode 100644 index 0000000..b078c81 --- /dev/null +++ b/outputs/test_helper.go @@ -0,0 +1,20 @@ +package outputs + +import ( + "github.com/SimonBaeumer/goss/resource" + "time" +) + +func GetExampleTestResult() []resource.TestResult { + return []resource.TestResult{ + { + Title: "my title", + Duration: time.Duration(500), + Successful: true, + ResourceType: "resource type", + ResourceId: "my resource id", + Property: "a property", + Expected: []string{"expected"}, + }, + } +} diff --git a/resource/dns_test.go b/resource/dns_test.go index 77fbead..fe2c413 100644 --- a/resource/dns_test.go +++ b/resource/dns_test.go @@ -3,6 +3,7 @@ package resource import ( "github.com/SimonBaeumer/goss/system" "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" "github.com/stretchr/testify/assert" "testing" ) @@ -35,7 +36,7 @@ func TestNewDNS_WithoutQType(t *testing.T) { } func TestDNS_Validate(t *testing.T) { - addrs := convertToInterfaceSlice() + addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) mockDns := MockSysDNS{} dns, _ := NewDNS(mockDns, conf) @@ -62,7 +63,7 @@ func TestDNS_Validate(t *testing.T) { } func TestDNS_ValidateFail(t *testing.T) { - addrs := convertToInterfaceSlice() + addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) mockDns := MockSysDNS{} dns, _ := NewDNS(mockDns, conf) @@ -91,17 +92,6 @@ to contain element matching assert.Equal(t, expectedHuman, r[1].Human) } -func convertToInterfaceSlice() []interface{} { - // Create expected addrs as interface{} slice - // It is necessary to allocate the memory before, because []interface{} is of an unknown size - var expect = []string{"localhost:53"} - var addrs = make([]interface{}, len(expect)) - for i, char := range expect { - addrs[i] = char - } - return addrs -} - //MockSysDNS mocks the DNS system interface type MockSysDNS struct { Addr []string diff --git a/resource/package_test.go b/resource/package_test.go new file mode 100644 index 0000000..a4fa82d --- /dev/null +++ b/resource/package_test.go @@ -0,0 +1,49 @@ +package resource + +import ( + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewPackage(t *testing.T) { + pkg, _ := NewPackage(TestPackage{}, util.Config{}) + + assert.Equal(t, "test-pkg-manager", pkg.Name) + assert.True(t, pkg.Installed.(bool)) +} + +func TestPackage_Validate(t *testing.T) { + p := Package{ + Title: "vim", + Name: "vim", + Installed: false, + Versions: goss_testing.ConvertStringSliceToInterfaceSlice([]string{"1.0.0"}), + PackageManager: "deb", + } + + sys := &system.System{NewPackage: func(name string, pkg string) system.Package { + return TestPackage{} + }} + + r := p.Validate(sys) + + assert.False(t, r[0].Successful) + assert.Equal(t, "installed", r[0].Property) + + assert.True(t, r[1].Successful) + assert.Equal(t, "version", r[1].Property) +} + + +type TestPackage struct {} + +func (p TestPackage) Name() string { return "test-pkg-manager" } + +func (p TestPackage) Exists() (bool, error) { return true, nil } + +func (p TestPackage) Installed() (bool, error) { return true, nil } + +func (p TestPackage) Versions() ([]string, error) { return []string{"1.0.0"}, nil } \ No newline at end of file diff --git a/util/goss_testing/helper.go b/util/goss_testing/helper.go index e31e31d..94f8b0b 100644 --- a/util/goss_testing/helper.go +++ b/util/goss_testing/helper.go @@ -1,21 +1,11 @@ package goss_testing -import ( - "github.com/SimonBaeumer/goss/resource" - "time" -) - -func GetExampleTestResult() []resource.TestResult { - return []resource.TestResult{ - { - Title: "my title", - Duration: time.Duration(500), - Successful: true, - ResourceType: "resource type", - ResourceId: "my resource id", - Property: "a property", - Expected: []string{"expected"}, - }, +//ConvertStringSliceToInterfaceSlice is a helper function to match +// system interfaces +func ConvertStringSliceToInterfaceSlice(strings []string) []interface{} { + var iStrings = make([]interface{}, len(strings)) + for i, char := range strings { + iStrings[i] = char } - + return iStrings } \ No newline at end of file From 8b6ad7642b661175b01b3863cb02c031a7845049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Thu, 16 May 2019 13:30:14 +0200 Subject: [PATCH 10/19] Add automated tests --- cmd/goss/goss.go | 1 - util/command.go | 3 +++ util/command_test.go | 22 ++++++++++++++++++++++ validate.go | 1 - 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 util/command_test.go diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index c3f4c11..aaf4cf0 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -92,7 +92,6 @@ func main() { v := &goss.Validator{ MaxConcurrent: ctx.MaxConcurrent, - Package: ctx.Package, Outputer: outputs.GetOutputer(ctx.Format), FormatOptions: ctx.FormatOptions, GossConfig: runtime.GetGossConfig(), diff --git a/util/command.go b/util/command.go index 92a03e8..3f9d543 100644 --- a/util/command.go +++ b/util/command.go @@ -7,6 +7,7 @@ import ( "syscall" ) +// Command represents a command which can be executed type Command struct { name string Cmd *exec.Cmd @@ -15,6 +16,7 @@ type Command struct { Status int } +// NewCommand creates a command func NewCommand(name string, arg ...string) *Command { //fmt.Println(arg) command := new(Command) @@ -23,6 +25,7 @@ func NewCommand(name string, arg ...string) *Command { return command } +// Run executes the command and writes the results to its properties func (c *Command) Run() error { c.Cmd.Stdout = &c.Stdout c.Cmd.Stderr = &c.Stderr diff --git a/util/command_test.go b/util/command_test.go new file mode 100644 index 0000000..9c22dcd --- /dev/null +++ b/util/command_test.go @@ -0,0 +1,22 @@ +package util + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewCommand(t *testing.T) { + cmd := NewCommand("/bin/sh") + assert.Equal(t, "/bin/sh", cmd.name) + assert.Equal(t, "", cmd.Stdout.String()) +} + +func TestCommand_Run(t *testing.T) { + cmd := NewCommand("/bin/sh", "-c", "echo test") + err := cmd.Run() + + assert.Nil(t, err) + assert.Equal(t, "test\n", cmd.Stdout.String()) + assert.Equal(t, "", cmd.Stderr.String()) + assert.Equal(t, 0, cmd.Status) +} \ No newline at end of file diff --git a/validate.go b/validate.go index d8836ba..29c1d0c 100644 --- a/validate.go +++ b/validate.go @@ -21,7 +21,6 @@ type Validator struct { Sleep time.Duration FormatOptions []string Outputer outputs.Outputer - Package string //Should be in the package resource config MaxConcurrent int //Separating concurrency and validation, irritating atm... OutputWriter io.Writer } From f6952ce0cb26f3c3b2d14369fd9691ecca61b706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Thu, 16 May 2019 15:09:45 +0200 Subject: [PATCH 11/19] Add validate app test --- app.go | 10 + cmd/goss/goss.go | 789 ++++++++++++++++++++++-------------------- cmd/goss/goss_test.go | 18 + 3 files changed, 434 insertions(+), 383 deletions(-) create mode 100644 cmd/goss/goss_test.go diff --git a/app.go b/app.go index 62f2d4e..14d0328 100644 --- a/app.go +++ b/app.go @@ -45,6 +45,16 @@ func (g *GossRunTime) GetGossConfig() GossConfig { } OutStoreFormat = getStoreFormatFromData(data) gossConfig = ReadJSONData(data, true) + } else if specFile == "testing" { + json := []byte(` +command: + echo hello: + exit-status: 0 + stdout: + - hello + timeout: 10000 +`) + gossConfig = ReadJSONData(json, true) } else { source = specFile path = filepath.Dir(specFile) diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index aaf4cf0..d3f787f 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -1,409 +1,432 @@ package main import ( - "fmt" + "fmt" app2 "github.com/SimonBaeumer/goss/internal/app" - "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/system" "github.com/fatih/color" "github.com/patrickmn/go-cache" - "log" - "os" - "sync" - "time" + "log" + "os" + "sync" + "time" - "github.com/SimonBaeumer/goss" - "github.com/SimonBaeumer/goss/outputs" - "github.com/urfave/cli" - //"time" + "github.com/SimonBaeumer/goss" + "github.com/SimonBaeumer/goss/outputs" + "github.com/urfave/cli" + //"time" ) var version string func main() { - startTime := time.Now() - app := cli.NewApp() - app.EnableBashCompletion = true - app.Version = version - app.Name = "goss" - app.Usage = "Quick and Easy server validation" - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "gossfile, g", - Value: "./goss.yaml", - Usage: "GossRunTime file to read from / write to", - EnvVar: "GOSS_FILE", - }, - cli.StringFlag{ - Name: "vars", - Usage: "json/yaml file containing variables for template", - EnvVar: "GOSS_VARS", - }, - } - app.Commands = []cli.Command{ - { - Name: "validate", - Aliases: []string{"v"}, - Usage: "Validate system", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "format, f", - Value: "rspecish", - Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), - EnvVar: "GOSS_FMT", - }, - cli.StringSliceFlag{ - Name: "format-options, o", - Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), - EnvVar: "GOSS_FMT_OPTIONS", - }, - cli.BoolFlag{ - Name: "color", - Usage: "Force color on", - EnvVar: "GOSS_COLOR", - }, - cli.BoolFlag{ - Name: "no-color", - Usage: "Force color off", - EnvVar: "GOSS_NOCOLOR", - }, - cli.DurationFlag{ - Name: "sleep,s", - Usage: "Time to sleep between retries, only active when -r is set", - Value: 1 * time.Second, - EnvVar: "GOSS_SLEEP", - }, - cli.DurationFlag{ - Name: "retry-timeout,r", - Usage: "Retry on failure so long as elapsed + sleep time is less than this", - Value: 0, - EnvVar: "GOSS_RETRY_TIMEOUT", - }, - cli.IntFlag{ - Name: "max-concurrent", - Usage: "Max number of tests to run concurrently", - Value: 50, - EnvVar: "GOSS_MAX_CONCURRENT", - }, - }, - Action: func(c *cli.Context) error { + app := createApp() + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} + +func createApp() *cli.App { + app := cli.NewApp() + app.EnableBashCompletion = true + app.Version = version + app.Name = "goss" + app.Usage = "Quick and Easy server validation" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "gossfile, g", + Value: "./goss.yaml", + Usage: "GossRunTime file to read from / write to", + EnvVar: "GOSS_FILE", + }, + cli.StringFlag{ + Name: "vars", + Usage: "json/yaml file containing variables for template", + EnvVar: "GOSS_VARS", + }, + } + app.Commands = []cli.Command{ + createValidateCommand(app), + createServeCommand(), + createAddCommand(), + { + Name: "render", + Aliases: []string{"r"}, + Usage: "render gossfile after imports", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "debug, d", + Usage: fmt.Sprintf("Print debugging info when rendering"), + }, + }, + Action: func(c *cli.Context) error { + fmt.Print(goss.RenderJSON(c)) + return nil + }, + }, + { + Name: "autoadd", + Aliases: []string{"aa"}, + Usage: "automatically add all matching resource to the test suite", + Action: func(c *cli.Context) error { ctx := app2.NewCliContext(c) + goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) + return nil + }, + }, + } + return app +} + +func createServeCommand() cli.Command { + return cli.Command{ + Name: "serve", + Aliases: []string{"s"}, + Usage: "Serve a health endpoint", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "rspecish", + Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), + EnvVar: "GOSS_FMT", + }, + cli.StringSliceFlag{ + Name: "format-options, o", + Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), + EnvVar: "GOSS_FMT_OPTIONS", + }, + cli.DurationFlag{ + Name: "cache,c", + Usage: "Time to cache the results", + Value: 5 * time.Second, + EnvVar: "GOSS_CACHE", + }, + cli.StringFlag{ + Name: "listen-addr,l", + Value: ":8080", + Usage: "Address to listen on [ip]:port", + EnvVar: "GOSS_LISTEN", + }, + cli.StringFlag{ + Name: "endpoint,e", + Value: "/healthz", + Usage: "Endpoint to expose", + EnvVar: "GOSS_ENDPOINT", + }, + cli.IntFlag{ + Name: "max-concurrent", + Usage: "Max number of tests to run concurrently", + Value: 50, + EnvVar: "GOSS_MAX_CONCURRENT", + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) - runtime := getGossRunTime(ctx) + gossRunTime := getGossRunTime(ctx) - v := &goss.Validator{ - MaxConcurrent: ctx.MaxConcurrent, - Outputer: outputs.GetOutputer(ctx.Format), - FormatOptions: ctx.FormatOptions, - GossConfig: runtime.GetGossConfig(), - } + h := &goss.HealthHandler{ + Cache: cache.New(ctx.Cache, 30*time.Second), + Outputer: outputs.GetOutputer(ctx.Format), + Sys: system.New(), + GossMu: &sync.Mutex{}, + MaxConcurrent: ctx.MaxConcurrent, + GossConfig: gossRunTime.GetGossConfig(), + } + + if ctx.Format == "json" { + h.ContentType = "application/json" + } + + h.Serve(ctx.Endpoint) + return nil + }, + } +} + +func createAddCommand() cli.Command { + return cli.Command{ + Name: "add", + Aliases: []string{"a"}, + Usage: "add a resource to the test suite", + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "exclude-attr", + Usage: "Exclude the following attributes when adding a new resource", + }, + }, + Subcommands: []cli.Command{ + { + Name: "package", + Usage: "add new package", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) + return nil + }, + }, + { + Name: "file", + Usage: "add new file", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) + return nil + }, + }, + { + Name: "addr", + Usage: "add new remote address:port - ex: google.com:80", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 500 * time.Millisecond, + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) + return nil + }, + }, + { + Name: "port", + Usage: "add new listening [protocol]:port - ex: 80 or udp:123", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) + return nil + }, + }, + { + Name: "service", + Usage: "add new service", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) + return nil + }, + }, + { + Name: "user", + Usage: "add new user", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) + return nil + }, + }, + { + Name: "group", + Usage: "add new group", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) + return nil + }, + }, + { + Name: "command", + Usage: "add new command", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 10 * time.Second, + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) + return nil + }, + }, + { + Name: "dns", + Usage: "add new dns", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 500 * time.Millisecond, + }, + cli.StringFlag{ + Name: "server", + Usage: "The IP address of a DNS server to query", + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) + return nil + }, + }, + { + Name: "process", + Usage: "add new process name", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), ctx) + return nil + }, + }, + { + Name: "http", + Usage: "add new http", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "insecure, k", + }, + cli.BoolFlag{ + Name: "no-follow-redirects, r", + }, + cli.DurationFlag{ + Name: "timeout", + Value: 5 * time.Second, + }, + cli.StringFlag{ + Name: "username, u", + Usage: "Username for basic auth", + }, + cli.StringFlag{ + Name: "password, p", + Usage: "Password for basic auth", + }, + cli.StringFlag{ + Name: "header", + Usage: "Set-Cookie: Value", + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) + return nil + }, + }, + { + Name: "goss", + Usage: "add new goss file, it will be imported from this one", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) + return nil + }, + }, + { + Name: "kernel-param", + Usage: "add new goss kernel param", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) + return nil + }, + }, + { + Name: "mount", + Usage: "add new mount", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) + return nil + }, + }, + { + Name: "interface", + Usage: "add new interface", + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) + goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) + return nil + }, + }, + }, + } +} - //TODO: ugly shit to set the color here, tmp fix for the moment! - if ctx.NoColor { - color.NoColor = true - } - if ctx.Color { - color.NoColor = false - } +func createValidateCommand(app *cli.App) cli.Command { + startTime := time.Now() - os.Exit(v.Validate(startTime)) - return nil - }, - }, - { - Name: "serve", - Aliases: []string{"s"}, - Usage: "Serve a health endpoint", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "format, f", - Value: "rspecish", - Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), - EnvVar: "GOSS_FMT", - }, - cli.StringSliceFlag{ - Name: "format-options, o", - Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), - EnvVar: "GOSS_FMT_OPTIONS", - }, - cli.DurationFlag{ - Name: "cache,c", - Usage: "Time to cache the results", - Value: 5 * time.Second, - EnvVar: "GOSS_CACHE", - }, - cli.StringFlag{ - Name: "listen-addr,l", - Value: ":8080", - Usage: "Address to listen on [ip]:port", - EnvVar: "GOSS_LISTEN", - }, - cli.StringFlag{ - Name: "endpoint,e", - Value: "/healthz", - Usage: "Endpoint to expose", - EnvVar: "GOSS_ENDPOINT", - }, - cli.IntFlag{ - Name: "max-concurrent", - Usage: "Max number of tests to run concurrently", - Value: 50, - EnvVar: "GOSS_MAX_CONCURRENT", - }, - }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) + return cli.Command{ + Name: "validate", + Aliases: []string{"v"}, + Usage: "Validate system", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "rspecish", + Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), + EnvVar: "GOSS_FMT", + }, + cli.StringSliceFlag{ + Name: "format-options, o", + Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), + EnvVar: "GOSS_FMT_OPTIONS", + }, + cli.BoolFlag{ + Name: "color", + Usage: "Force color on", + EnvVar: "GOSS_COLOR", + }, + cli.BoolFlag{ + Name: "no-color", + Usage: "Force color off", + EnvVar: "GOSS_NOCOLOR", + }, + cli.DurationFlag{ + Name: "sleep,s", + Usage: "Time to sleep between retries, only active when -r is set", + Value: 1 * time.Second, + EnvVar: "GOSS_SLEEP", + }, + cli.DurationFlag{ + Name: "retry-timeout,r", + Usage: "Retry on failure so long as elapsed + sleep time is less than this", + Value: 0, + EnvVar: "GOSS_RETRY_TIMEOUT", + }, + cli.IntFlag{ + Name: "max-concurrent", + Usage: "Max number of tests to run concurrently", + Value: 50, + EnvVar: "GOSS_MAX_CONCURRENT", + }, + }, + Action: func(c *cli.Context) error { + ctx := app2.NewCliContext(c) - gossRunTime := getGossRunTime(ctx) + runtime := getGossRunTime(ctx) - h := &goss.HealthHandler{ - Cache: cache.New(ctx.Cache, 30*time.Second), - ListenAddr: ctx.ListenAddr, - Outputer: outputs.GetOutputer(ctx.Format), - Sys: system.New(), - GossMu: &sync.Mutex{}, - MaxConcurrent: ctx.MaxConcurrent, - GossConfig: gossRunTime.GetGossConfig(), - } + v := &goss.Validator{ + OutputWriter: app.Writer, + MaxConcurrent: ctx.MaxConcurrent, + Outputer: outputs.GetOutputer(ctx.Format), + FormatOptions: ctx.FormatOptions, + GossConfig: runtime.GetGossConfig(), + } - if ctx.Format == "json" { - h.ContentType = "application/json" - } + //TODO: ugly shit to set the color here, tmp fix for the moment! + if ctx.NoColor { + color.NoColor = true + } + if ctx.Color { + color.NoColor = false + } - h.Serve(ctx.Endpoint) - return nil - }, - }, - { - Name: "render", - Aliases: []string{"r"}, - Usage: "render gossfile after imports", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "debug, d", - Usage: fmt.Sprintf("Print debugging info when rendering"), - }, - }, - Action: func(c *cli.Context) error { - fmt.Print(goss.RenderJSON(c)) - return nil - }, - }, - { - Name: "autoadd", - Aliases: []string{"aa"}, - Usage: "automatically add all matching resource to the test suite", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) - return nil - }, - }, - { - Name: "add", - Aliases: []string{"a"}, - Usage: "add a resource to the test suite", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "exclude-attr", - Usage: "Exclude the following attributes when adding a new resource", - }, - }, - Subcommands: []cli.Command{ - { - Name: "package", - Usage: "add new package", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) - return nil - }, - }, - { - Name: "file", - Usage: "add new file", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) - return nil - }, - }, - { - Name: "addr", - Usage: "add new remote address:port - ex: google.com:80", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 500 * time.Millisecond, - }, - }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) - return nil - }, - }, - { - Name: "port", - Usage: "add new listening [protocol]:port - ex: 80 or udp:123", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) - return nil - }, - }, - { - Name: "service", - Usage: "add new service", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) - return nil - }, - }, - { - Name: "user", - Usage: "add new user", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) - return nil - }, - }, - { - Name: "group", - Usage: "add new group", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) - return nil - }, - }, - { - Name: "command", - Usage: "add new command", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 10 * time.Second, - }, - }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) - return nil - }, - }, - { - Name: "dns", - Usage: "add new dns", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 500 * time.Millisecond, - }, - cli.StringFlag{ - Name: "server", - Usage: "The IP address of a DNS server to query", - }, - }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) - return nil - }, - }, - { - Name: "process", - Usage: "add new process name", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), ctx) - return nil - }, - }, - { - Name: "http", - Usage: "add new http", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "insecure, k", - }, - cli.BoolFlag{ - Name: "no-follow-redirects, r", - }, - cli.DurationFlag{ - Name: "timeout", - Value: 5 * time.Second, - }, - cli.StringFlag{ - Name: "username, u", - Usage: "Username for basic auth", - }, - cli.StringFlag{ - Name: "password, p", - Usage: "Password for basic auth", - }, - cli.StringFlag{ - Name: "header", - Usage: "Set-Cookie: Value", - }, - }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) - return nil - }, - }, - { - Name: "goss", - Usage: "add new goss file, it will be imported from this one", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) - return nil - }, - }, - { - Name: "kernel-param", - Usage: "add new goss kernel param", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) - return nil - }, - }, - { - Name: "mount", - Usage: "add new mount", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) - return nil - }, - }, - { - Name: "interface", - Usage: "add new interface", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) - return nil - }, - }, - }, - }, - } + if ctx.Gossfile == "testing" { + v.Validate(startTime) + return nil + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + os.Exit(v.Validate(startTime)) + return nil + }, + } } func getGossRunTime(ctx app2.CliContext) goss.GossRunTime { - runtime := goss.GossRunTime{ - Gossfile: ctx.Gossfile, - Vars: ctx.Vars, - } - return runtime + runtime := goss.GossRunTime{ + Gossfile: ctx.Gossfile, + Vars: ctx.Vars, + } + return runtime } diff --git a/cmd/goss/goss_test.go b/cmd/goss/goss_test.go new file mode 100644 index 0000000..78057b9 --- /dev/null +++ b/cmd/goss/goss_test.go @@ -0,0 +1,18 @@ +package main + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestApp_Validate(t *testing.T) { + b := &bytes.Buffer{} + app := createApp() + app.Writer = b + + r := app.Run([]string{"", "--gossfile", "testing", "validate"}) + + assert.Nil(t, r) + assert.Contains(t, b.String(), "Count: 2, Failed: 0, Skipped: 0") +} From 8c9651b6b510541e9a3f6f639e9134341be9b08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Fri, 17 May 2019 09:04:01 +0200 Subject: [PATCH 12/19] Add add command test --- add.go | 173 +++++++++++++++++++++++------------------- cmd/goss/goss.go | 118 +++++++++------------------- cmd/goss/goss_test.go | 28 +++++++ store.go | 12 --- 4 files changed, 159 insertions(+), 172 deletions(-) diff --git a/add.go b/add.go index f088090..a2fec7a 100644 --- a/add.go +++ b/add.go @@ -3,26 +3,35 @@ package goss import ( "fmt" "github.com/SimonBaeumer/goss/internal/app" - "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/system" "github.com/SimonBaeumer/goss/util" + "io" "os" - "strconv" + "reflect" + "strconv" "strings" ) +type Add struct { + Writer io.Writer + Ctx app.CliContext + Sys *system.System +} + // AddResources is a sSimple wrapper to add multiple resources -func AddResources(fileName, resourceName string, keys []string, ctx app.CliContext) error { +func (a *Add) AddResources(fileName, resourceName string, keys []string) error { OutStoreFormat = getStoreFormatFromFileName(fileName) - header := extractHeaderArgument(ctx.Header) + header := extractHeaderArgument(a.Ctx.Header) config := util.Config{ - IgnoreList: ctx.ExcludeAttr, - Timeout: ctx.Timeout, - AllowInsecure: ctx.AllowInsecure, - NoFollowRedirects: ctx.NoFollowRedirects, - Server: ctx.Server, - Username: ctx.Username, - Password: ctx.Password, + IgnoreList: a.Ctx.ExcludeAttr, + Timeout: a.Ctx.Timeout, + AllowInsecure: a.Ctx.AllowInsecure, + NoFollowRedirects: a.Ctx.NoFollowRedirects, + Server: a.Ctx.Server, + Username: a.Ctx.Username, + Password: a.Ctx.Password, Header: header, } @@ -33,10 +42,8 @@ func AddResources(fileName, resourceName string, keys []string, ctx app.CliConte gossConfig = *NewGossConfig() } - sys := system.New() - for _, key := range keys { - if err := AddResource(fileName, gossConfig, resourceName, key, config, sys); err != nil { + if err := a.AddResource(fileName, gossConfig, resourceName, key, config); err != nil { return err } } @@ -56,114 +63,114 @@ func extractHeaderArgument(headerArg string) map[string][]string { } // AddResource adds a resource to the configuration file -func AddResource(fileName string, gossConfig GossConfig, resourceName, key string, config util.Config, sys *system.System) error { +func (a *Add) AddResource(fileName string, gossConfig GossConfig, resourceName, key string, config util.Config) error { // Need to figure out a good way to refactor this switch resourceName { - case "Addr": - res, err := gossConfig.Addrs.AppendSysResource(key, sys, config) + case "addr": + res, err := gossConfig.Addrs.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Command": - res, err := gossConfig.Commands.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "command": + res, err := gossConfig.Commands.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "DNS": - res, err := gossConfig.DNS.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "dns": + res, err := gossConfig.DNS.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "File": - res, err := gossConfig.Files.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "file": + res, err := gossConfig.Files.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "Group": - res, err := gossConfig.Groups.AppendSysResource(key, sys, config) + res, err := gossConfig.Groups.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Package": - res, err := gossConfig.Packages.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "package": + res, err := gossConfig.Packages.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Port": - res, err := gossConfig.Ports.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "port": + res, err := gossConfig.Ports.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Process": - res, err := gossConfig.Processes.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "process": + res, err := gossConfig.Processes.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Service": - res, err := gossConfig.Services.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "service": + res, err := gossConfig.Services.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "User": - res, err := gossConfig.Users.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "user": + res, err := gossConfig.Users.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Gossfile": - res, err := gossConfig.Gossfiles.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "gossfile": + res, err := gossConfig.Gossfiles.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "KernelParam": - res, err := gossConfig.KernelParams.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "kernel-param": + res, err := gossConfig.KernelParams.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "Mount": - res, err := gossConfig.Mounts.AppendSysResource(key, sys, config) + res, err := gossConfig.Mounts.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "Interface": - res, err := gossConfig.Interfaces.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "interface": + res, err := gossConfig.Interfaces.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) - case "HTTP": - res, err := gossConfig.HTTPs.AppendSysResource(key, sys, config) + a.resourcePrint(fileName, res) + case "http": + res, err := gossConfig.HTTPs.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - resourcePrint(fileName, res) + a.resourcePrint(fileName, res) default: panic("Undefined resource name: " + resourceName) } @@ -172,7 +179,7 @@ func AddResource(fileName string, gossConfig GossConfig, resourceName, key strin } // Simple wrapper to add multiple resources -func AutoAddResources(fileName string, keys []string, ctx app.CliContext) error { +func (a *Add) AutoAddResources(fileName string, keys []string) error { OutStoreFormat = getStoreFormatFromFileName(fileName) var gossConfig GossConfig @@ -182,10 +189,8 @@ func AutoAddResources(fileName string, keys []string, ctx app.CliContext) error gossConfig = *NewGossConfig() } - sys := system.New() - for _, key := range keys { - if err := AutoAddResource(fileName, gossConfig, key, sys); err != nil { + if err := a.AutoAddResource(fileName, gossConfig, key); err != nil { return err } } @@ -197,32 +202,32 @@ func AutoAddResources(fileName string, keys []string, ctx app.CliContext) error } // Autoadds all resources to the config file -func AutoAddResource(fileName string, gossConfig GossConfig, key string, sys *system.System) error { +func (a *Add) AutoAddResource(fileName string, gossConfig GossConfig, key string) error { // file if strings.Contains(key, "/") { - if res, _, ok := gossConfig.Files.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Files.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } } // group - if res, _, ok := gossConfig.Groups.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Groups.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } // package - if res, _, ok := gossConfig.Packages.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Packages.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } // port - if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } // process - if res, sysres, ok := gossConfig.Processes.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, sysres, ok := gossConfig.Processes.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) ports := system.GetPorts(true) pids, _ := sysres.Pids() for _, pid := range pids { @@ -231,8 +236,8 @@ func AutoAddResource(fileName string, gossConfig GossConfig, key string, sys *sy for _, entry := range entries { if entry.Pid == pidS { // port - if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(port, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(port, a.Sys); ok == true { + a.resourcePrint(fileName, res) } } } @@ -241,14 +246,24 @@ func AutoAddResource(fileName string, gossConfig GossConfig, key string, sys *sy } // Service - if res, _, ok := gossConfig.Services.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Services.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } // user - if res, _, ok := gossConfig.Users.AppendSysResourceIfExists(key, sys); ok == true { - resourcePrint(fileName, res) + if res, _, ok := gossConfig.Users.AppendSysResourceIfExists(key, a.Sys); ok == true { + a.resourcePrint(fileName, res) } return nil } + +func (a *Add) resourcePrint(fileName string, res resource.ResourceRead) { + resMap := map[string]resource.ResourceRead{res.ID(): res} + + oj, _ := marshal(resMap) + typ := reflect.TypeOf(res) + typs := strings.Split(typ.String(), ".")[1] + + fmt.Fprintf(a.Writer, "Adding %s to '%s':\n\n%s\n\n", typs, fileName, string(oj)) +} \ No newline at end of file diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index d3f787f..523dbc9 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -50,7 +50,7 @@ func createApp() *cli.App { app.Commands = []cli.Command{ createValidateCommand(app), createServeCommand(), - createAddCommand(), + createAddCommand(app), { Name: "render", Aliases: []string{"r"}, @@ -71,9 +71,12 @@ func createApp() *cli.App { Aliases: []string{"aa"}, Usage: "automatically add all matching resource to the test suite", Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AutoAddResources(c.GlobalString("gossfile"), c.Args(), ctx) - return nil + a := goss.Add{ + Ctx: app2.NewCliContext(c), + Writer: app.Writer, + Sys: system.New(), + } + return a.AutoAddResources(c.GlobalString("gossfile"), c.Args()) }, }, } @@ -146,7 +149,20 @@ func createServeCommand() cli.Command { } } -func createAddCommand() cli.Command { +func createAddHandler(app *cli.App, resourceName string) (func(c *cli.Context) error) { + return func(c *cli.Context) error { + a := goss.Add{ + Sys: system.New(), + Writer: app.Writer, + } + + a.Ctx = app2.NewCliContext(c) + a.AddResources(c.GlobalString("gossfile"), resourceName, c.Args()) + return nil + } +} + +func createAddCommand(app *cli.App) cli.Command { return cli.Command{ Name: "add", Aliases: []string{"a"}, @@ -159,22 +175,14 @@ func createAddCommand() cli.Command { }, Subcommands: []cli.Command{ { - Name: "package", + Name: "Package", Usage: "add new package", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Package", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "package"), }, { Name: "file", Usage: "add new file", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "File", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "file"), }, { Name: "addr", @@ -185,47 +193,27 @@ func createAddCommand() cli.Command { Value: 500 * time.Millisecond, }, }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Addr", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "addr"), }, { Name: "port", Usage: "add new listening [protocol]:port - ex: 80 or udp:123", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Port", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "port"), }, { Name: "service", Usage: "add new service", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Service", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "service"), }, { Name: "user", Usage: "add new user", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "User", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "user"), }, { Name: "group", Usage: "add new group", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Group", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "group"), }, { Name: "command", @@ -236,11 +224,7 @@ func createAddCommand() cli.Command { Value: 10 * time.Second, }, }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Command", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "command"), }, { Name: "dns", @@ -255,20 +239,12 @@ func createAddCommand() cli.Command { Usage: "The IP address of a DNS server to query", }, }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "DNS", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "dns"), }, { Name: "process", Usage: "add new process name", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Process", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "process"), }, { Name: "http", @@ -297,47 +273,27 @@ func createAddCommand() cli.Command { Usage: "Set-Cookie: Value", }, }, - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "HTTP", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "http"), }, { Name: "goss", Usage: "add new goss file, it will be imported from this one", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Gossfile", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "goss"), }, { Name: "kernel-param", Usage: "add new goss kernel param", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "KernelParam", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "kernel-param"), }, { Name: "mount", Usage: "add new mount", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Mount", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "mount"), }, { Name: "interface", Usage: "add new interface", - Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) - goss.AddResources(c.GlobalString("gossfile"), "Interface", c.Args(), ctx) - return nil - }, + Action: createAddHandler(app, "interface"), }, }, } diff --git a/cmd/goss/goss_test.go b/cmd/goss/goss_test.go index 78057b9..38b1670 100644 --- a/cmd/goss/goss_test.go +++ b/cmd/goss/goss_test.go @@ -3,6 +3,7 @@ package main import ( "bytes" "github.com/stretchr/testify/assert" + "io/ioutil" "testing" ) @@ -16,3 +17,30 @@ func TestApp_Validate(t *testing.T) { assert.Nil(t, r) assert.Contains(t, b.String(), "Count: 2, Failed: 0, Skipped: 0") } + +func TestApp_Add(t *testing.T) { + b := &bytes.Buffer{} + app := createApp() + app.Writer = b + + file, err := ioutil.TempFile("/tmp", "testing_goss_*.yaml") + if err != nil { + panic(err.Error()) + } + defer file.Close() + + r := app.Run([]string{"", "--gossfile", file.Name(), "add", "http", "http://google.com"}) + + assert.Nil(t, r) + assert.Contains(t, b.String(), getAddResult()) + assert.Contains(t, b.String(), "Adding HTTP to '/tmp/testing_goss_") +} + +func getAddResult() string { + return `http://google.com: + status: 200 + allow-insecure: false + no-follow-redirects: false + timeout: 5000 + body: []` +} \ No newline at end of file diff --git a/store.go b/store.go index e2aa366..4ced2c7 100644 --- a/store.go +++ b/store.go @@ -7,13 +7,11 @@ import ( "log" "os" "path/filepath" - "reflect" "sort" "strings" "gopkg.in/yaml.v2" - "github.com/SimonBaeumer/goss/resource" "github.com/urfave/cli" ) @@ -201,16 +199,6 @@ func WriteJSON(filePath string, gossConfig GossConfig) error { return nil } -func resourcePrint(fileName string, res resource.ResourceRead) { - resMap := map[string]resource.ResourceRead{res.ID(): res} - - oj, _ := marshal(resMap) - typ := reflect.TypeOf(res) - typs := strings.Split(typ.String(), ".")[1] - - fmt.Printf("Adding %s to '%s':\n\n%s\n\n", typs, fileName, string(oj)) -} - func marshal(gossConfig interface{}) ([]byte, error) { switch OutStoreFormat { case JSON: From 70b9ec0b6bb81abce8a43b5302a53f291ef37f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 10:44:03 +0200 Subject: [PATCH 13/19] Remove internal cli context and add CliConf in cmd --- add.go | 38 +++++++------ app.go | 5 -- cmd/goss/goss.go | 116 ++++++++++++++++++++++++++++++++-------- integration/TODO.md | 2 +- internal/app/context.go | 54 ------------------- serve.go | 7 ++- 6 files changed, 120 insertions(+), 102 deletions(-) delete mode 100644 internal/app/context.go diff --git a/add.go b/add.go index a2fec7a..82c2af4 100644 --- a/add.go +++ b/add.go @@ -2,36 +2,42 @@ package goss import ( "fmt" - "github.com/SimonBaeumer/goss/internal/app" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/system" "github.com/SimonBaeumer/goss/util" "io" "os" - "reflect" - "strconv" + "reflect" + "strconv" "strings" ) type Add struct { - Writer io.Writer - Ctx app.CliContext - Sys *system.System + Writer io.Writer + ExcludeAttr []string + Timeout int + AllowInsecure bool + NoFollowRedirects bool + Server string + Username string + Password string + Header string + Sys *system.System } // AddResources is a sSimple wrapper to add multiple resources func (a *Add) AddResources(fileName, resourceName string, keys []string) error { OutStoreFormat = getStoreFormatFromFileName(fileName) - header := extractHeaderArgument(a.Ctx.Header) + header := extractHeaderArgument(a.Header) config := util.Config{ - IgnoreList: a.Ctx.ExcludeAttr, - Timeout: a.Ctx.Timeout, - AllowInsecure: a.Ctx.AllowInsecure, - NoFollowRedirects: a.Ctx.NoFollowRedirects, - Server: a.Ctx.Server, - Username: a.Ctx.Username, - Password: a.Ctx.Password, + IgnoreList: a.ExcludeAttr, + Timeout: a.Timeout, + AllowInsecure: a.AllowInsecure, + NoFollowRedirects: a.NoFollowRedirects, + Server: a.Server, + Username: a.Username, + Password: a.Password, Header: header, } diff --git a/app.go b/app.go index 14d0328..0611665 100644 --- a/app.go +++ b/app.go @@ -2,7 +2,6 @@ package goss import ( "fmt" - "github.com/SimonBaeumer/goss/internal/app" "io/ioutil" "os" "path/filepath" @@ -18,10 +17,6 @@ type GossRunTime struct { Package string //this does not belong here imho } -func NewGossRunTime(ctx app.CliContext) *GossRunTime { - return &GossRunTime{} -} - func (g *GossRunTime) Serve() { //Serve() } diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 523dbc9..d202d23 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -2,7 +2,6 @@ package main import ( "fmt" - app2 "github.com/SimonBaeumer/goss/internal/app" "github.com/SimonBaeumer/goss/system" "github.com/fatih/color" "github.com/patrickmn/go-cache" @@ -14,7 +13,6 @@ import ( "github.com/SimonBaeumer/goss" "github.com/SimonBaeumer/goss/outputs" "github.com/urfave/cli" - //"time" ) var version string @@ -28,6 +26,58 @@ func main() { } } +// CliConfig represents all configurations passed by the cli app +type CliConfig struct { + FormatOptions []string + Sleep time.Duration + RetryTimeout time.Duration + MaxConcurrent int + Vars string + Gossfile string + ExcludeAttr []string + Timeout int + AllowInsecure bool + NoFollowRedirects bool + Server string + Username string + Password string + Header string + Endpoint string + ListenAddr string + Cache time.Duration + Format string + NoColor bool + Color bool +} + +// NewCliConfig creates an object from the cli.Context. It is used as a constructor and will do simple type conversions +// and validations +func NewCliConfig(c *cli.Context) CliConfig { + return CliConfig{ + FormatOptions: c.StringSlice("format-options"), + Sleep: c.Duration("sleep"), + RetryTimeout: c.Duration("retry-timeout"), + MaxConcurrent: c.Int("max-concurrent"), + Vars: c.GlobalString("vars"), + Gossfile: c.GlobalString("gossfile"), + ExcludeAttr: c.GlobalStringSlice("exclude-attr"), + Timeout: int(c.Duration("timeout") / time.Millisecond), + AllowInsecure: c.Bool("insecure"), + NoFollowRedirects: c.Bool("no-follow-redirects"), + Server: c.String("server"), + Username: c.String("username"), + Password: c.String("password"), + Header: c.String("header"), + Endpoint: c.String("endpoint"), + ListenAddr: c.String("listen-addr"), + Cache: c.Duration("cache"), + Format: c.String("format"), + NoColor: c.Bool("no-color"), + Color: c.Bool("color"), + } +} + +// Create the cli.App object func createApp() *cli.App { app := cli.NewApp() app.EnableBashCompletion = true @@ -71,10 +121,19 @@ func createApp() *cli.App { Aliases: []string{"aa"}, Usage: "automatically add all matching resource to the test suite", Action: func(c *cli.Context) error { + conf := NewCliConfig(c) + a := goss.Add{ - Ctx: app2.NewCliContext(c), Writer: app.Writer, Sys: system.New(), + Username: conf.Username, + Password: conf.Password, + Server: conf.Server, + Timeout: conf.Timeout, + NoFollowRedirects: conf.NoFollowRedirects, + AllowInsecure: conf.AllowInsecure, + ExcludeAttr: conf.ExcludeAttr, + Header: conf.Header, } return a.AutoAddResources(c.GlobalString("gossfile"), c.Args()) }, @@ -126,24 +185,28 @@ func createServeCommand() cli.Command { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) + conf := NewCliConfig(c) - gossRunTime := getGossRunTime(ctx) + gossRunTime := goss.GossRunTime{ + Gossfile: c.GlobalString("gossfile"), + Vars: c.GlobalString("vars"), + } h := &goss.HealthHandler{ - Cache: cache.New(ctx.Cache, 30*time.Second), - Outputer: outputs.GetOutputer(ctx.Format), + Cache: cache.New(conf.Cache, 30 * time.Second), + Outputer: outputs.GetOutputer(conf.Format), Sys: system.New(), GossMu: &sync.Mutex{}, - MaxConcurrent: ctx.MaxConcurrent, + MaxConcurrent: conf.MaxConcurrent, GossConfig: gossRunTime.GetGossConfig(), + FormatOptions: conf.FormatOptions, } - if ctx.Format == "json" { + if conf.Format == "json" { h.ContentType = "application/json" } - h.Serve(ctx.Endpoint) + h.Serve(conf.Endpoint) return nil }, } @@ -151,12 +214,21 @@ func createServeCommand() cli.Command { func createAddHandler(app *cli.App, resourceName string) (func(c *cli.Context) error) { return func(c *cli.Context) error { + conf := NewCliConfig(c) + a := goss.Add{ Sys: system.New(), Writer: app.Writer, + Username: conf.Username, + Password: conf.Password, + Server: conf.Server, + Timeout: conf.Timeout, + NoFollowRedirects: conf.NoFollowRedirects, + AllowInsecure: conf.AllowInsecure, + ExcludeAttr: conf.ExcludeAttr, + Header: conf.Header, } - a.Ctx = app2.NewCliContext(c) a.AddResources(c.GlobalString("gossfile"), resourceName, c.Args()) return nil } @@ -348,27 +420,27 @@ func createValidateCommand(app *cli.App) cli.Command { }, }, Action: func(c *cli.Context) error { - ctx := app2.NewCliContext(c) + conf := NewCliConfig(c) - runtime := getGossRunTime(ctx) + runtime := getGossRunTime(conf.Gossfile, conf.Vars) v := &goss.Validator{ OutputWriter: app.Writer, - MaxConcurrent: ctx.MaxConcurrent, - Outputer: outputs.GetOutputer(ctx.Format), - FormatOptions: ctx.FormatOptions, + MaxConcurrent: conf.MaxConcurrent, + Outputer: outputs.GetOutputer(conf.Format), + FormatOptions: conf.FormatOptions, GossConfig: runtime.GetGossConfig(), } //TODO: ugly shit to set the color here, tmp fix for the moment! - if ctx.NoColor { + if conf.NoColor { color.NoColor = true } - if ctx.Color { + if conf.Color { color.NoColor = false } - if ctx.Gossfile == "testing" { + if conf.Gossfile == "testing" { v.Validate(startTime) return nil } @@ -379,10 +451,10 @@ func createValidateCommand(app *cli.App) cli.Command { } } -func getGossRunTime(ctx app2.CliContext) goss.GossRunTime { +func getGossRunTime(gossfile string, vars string) goss.GossRunTime { runtime := goss.GossRunTime{ - Gossfile: ctx.Gossfile, - Vars: ctx.Vars, + Gossfile: gossfile, + Vars: vars, } return runtime } diff --git a/integration/TODO.md b/integration/TODO.md index 1b96ed7..c429998 100644 --- a/integration/TODO.md +++ b/integration/TODO.md @@ -5,7 +5,7 @@ Fail and Success case for every resource. - [x] addr - [x] command - - [ ] dns + - [x] dns - [x] file - [x] gossfile - [x] group diff --git a/internal/app/context.go b/internal/app/context.go deleted file mode 100644 index e10f451..0000000 --- a/internal/app/context.go +++ /dev/null @@ -1,54 +0,0 @@ -package app - -import ( - "github.com/urfave/cli" - "time" -) - -type CliContext struct { - FormatOptions []string - Sleep time.Duration - RetryTimeout time.Duration - MaxConcurrent int - Vars string - Gossfile string - ExcludeAttr []string - Timeout int - AllowInsecure bool - NoFollowRedirects bool - Server string - Username string - Password string - Header string - Endpoint string - ListenAddr string - Cache time.Duration - Format string - NoColor bool - Color bool -} - -func NewCliContext(c *cli.Context) CliContext { - return CliContext{ - FormatOptions: c.StringSlice("format-options"), - Sleep: c.Duration("sleep"), - RetryTimeout: c.Duration("retry-timeout"), - MaxConcurrent: c.Int("max-concurrent"), - Vars: c.GlobalString("vars"), - Gossfile: c.GlobalString("gossfile"), - ExcludeAttr: c.GlobalStringSlice("exclude-attr"), - Timeout: int(c.Duration("timeout") / time.Millisecond), - AllowInsecure: c.Bool("insecure"), - NoFollowRedirects: c.Bool("no-follow-redirects"), - Server: c.String("server"), - Username: c.String("username"), - Password: c.String("password"), - Header: c.String("header"), - Endpoint: c.String("endpoint"), - ListenAddr: c.String("listen-addr"), - Cache: c.Duration("cache"), - Format: c.String("format"), - NoColor: c.Bool("no-color"), - Color: c.Bool("color"), - } -} diff --git a/serve.go b/serve.go index 6d7a45e..1ece34b 100644 --- a/serve.go +++ b/serve.go @@ -2,7 +2,6 @@ package goss import ( "bytes" - "github.com/SimonBaeumer/goss/internal/app" "log" "net/http" "sync" @@ -15,10 +14,9 @@ import ( "github.com/patrickmn/go-cache" ) -//TODO: Maybe seperating handler and server? +//TODO: Maybe separating handler and server? type HealthHandler struct { RunTimeConfig GossRunTime - C app.CliContext GossConfig GossConfig Sys *system.System Outputer outputs.Outputer @@ -27,6 +25,7 @@ type HealthHandler struct { ContentType string MaxConcurrent int ListenAddr string + FormatOptions []string } func (h *HealthHandler) Serve(endpoint string) { @@ -46,7 +45,7 @@ type res struct { //health check request. func (h HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { outputConfig := util.OutputConfig{ - FormatOptions: h.C.FormatOptions, + FormatOptions: h.FormatOptions, } log.Printf("%v: requesting health probe", r.RemoteAddr) From 0ba098d25a8f61d2a9f6bfd30ba15acb1bc72f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 11:49:43 +0200 Subject: [PATCH 14/19] Fix version assertion on apt package manager --- integration/resources/package/ubuntu/goss.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/resources/package/ubuntu/goss.yaml b/integration/resources/package/ubuntu/goss.yaml index f302735..8026b13 100644 --- a/integration/resources/package/ubuntu/goss.yaml +++ b/integration/resources/package/ubuntu/goss.yaml @@ -2,6 +2,6 @@ package: apt: installed: true versions: - - 1.6.10 + - 1.6.11 no exists: installed: false \ No newline at end of file From 991d9d49868b7ae0bc989c650cb4aa264673cbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 12:02:03 +0200 Subject: [PATCH 15/19] Fix apt version to 1.6.11 in commander test --- integration/commander.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/commander.yaml b/integration/commander.yaml index 66a0921..ba8bebc 100755 --- a/integration/commander.yaml +++ b/integration/commander.yaml @@ -400,7 +400,7 @@ tests: - |- Package: apt: version: Expected - <[]string | len:1, cap:1>: ["1.6.10"] + <[]string | len:1, cap:1>: ["1.6.11"] to contain element matching : 100.0.0 - |- From 8382cca0137cead6943f0775942e0e83b8c78fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 15:33:29 +0200 Subject: [PATCH 16/19] Add template render tests --- app.go | 29 ++++++++++- cmd/goss/goss.go | 12 +++-- serve.go | 2 + store.go | 19 +++---- store_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++++ system/dns_test.go | 2 +- template.go | 3 ++ template_test.go | 86 +++++++++++++++++++++++++++++++ 8 files changed, 258 insertions(+), 18 deletions(-) create mode 100644 store_test.go create mode 100644 template_test.go diff --git a/app.go b/app.go index 0611665..0ead873 100644 --- a/app.go +++ b/app.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "time" ) // GossRunTime represents the global runtime configs which can be set in goss @@ -15,12 +16,36 @@ type GossRunTime struct { Vars string //Package defines which package manager you want to use, i.e. yum, apt, ... Package string //this does not belong here imho + //Debug on true will create a more verbose output + Debug bool } -func (g *GossRunTime) Serve() { - //Serve() +// Serve serves a new health endpoint +func (g *GossRunTime) Serve(endpoint string, handler *HealthHandler) { + handler.Serve(endpoint) } +// Validate starts the validation process +func (g *GossRunTime) Validate(v *Validator) int { + return v.Validate(time.Now()) +} + +// Render renders a template file +func (g *GossRunTime) Render() (string, error) { + goss, err := os.Open(g.Gossfile) + if err != nil { + return "", err + } + defer goss.Close() + + vars, err := os.Open(g.Vars) + if err != nil { + return "", err + } + defer vars.Close() + + return RenderJSON(goss, vars), nil +} // GetGossConfig returns the goss configuration func (g *GossRunTime) GetGossConfig() GossConfig { diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index d202d23..56a6019 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -112,7 +112,13 @@ func createApp() *cli.App { }, }, Action: func(c *cli.Context) error { - fmt.Print(goss.RenderJSON(c)) + conf := NewCliConfig(c) + runtime := goss.GossRunTime{ + Vars: conf.Vars, + Gossfile: conf.Gossfile, + } + + fmt.Print(runtime.Render()) return nil }, }, @@ -206,7 +212,7 @@ func createServeCommand() cli.Command { h.ContentType = "application/json" } - h.Serve(conf.Endpoint) + gossRunTime.Serve(conf.Endpoint, h) return nil }, } @@ -445,7 +451,7 @@ func createValidateCommand(app *cli.App) cli.Command { return nil } - os.Exit(v.Validate(startTime)) + os.Exit(runtime.Validate(v)) return nil }, } diff --git a/serve.go b/serve.go index 1ece34b..f2b97b3 100644 --- a/serve.go +++ b/serve.go @@ -15,6 +15,7 @@ import ( ) //TODO: Maybe separating handler and server? +// HealthHandler creates a new handler for the health endpoint type HealthHandler struct { RunTimeConfig GossRunTime GossConfig GossConfig @@ -28,6 +29,7 @@ type HealthHandler struct { FormatOptions []string } +// Serve creates a new endpoint and starts the http server func (h *HealthHandler) Serve(endpoint string) { color.NoColor = true diff --git a/store.go b/store.go index 4ced2c7..a0df6bb 100644 --- a/store.go +++ b/store.go @@ -11,8 +11,6 @@ import ( "strings" "gopkg.in/yaml.v2" - - "github.com/urfave/cli" ) const ( @@ -90,7 +88,7 @@ func varsFromFile(varsFile string) (map[string]interface{}, error) { return vars, nil } -// Reads json byte array returning GossConfig +// ReadJSONData reads json byte array returning GossConfig func ReadJSONData(data []byte, detectFormat bool) GossConfig { if TemplateFilter != nil { data = TemplateFilter(data) @@ -113,15 +111,12 @@ func ReadJSONData(data []byte, detectFormat bool) GossConfig { return *gossConfig } -// Reads json file recursively returning string -func RenderJSON(c *cli.Context) string { - filePath := c.GlobalString("gossfile") - varsFile := c.GlobalString("vars") - debug = c.Bool("debug") - TemplateFilter = NewTemplateFilter(varsFile) - path := filepath.Dir(filePath) - OutStoreFormat = getStoreFormatFromFileName(filePath) - gossConfig := mergeJSONData(ReadJSON(filePath), 0, path) +// RenderJSON reads json file recursively returning string +func RenderJSON(gossfile *os.File, vars *os.File) string { + TemplateFilter = NewTemplateFilter(vars.Name()) + path := filepath.Dir(gossfile.Name()) + OutStoreFormat = getStoreFormatFromFileName(gossfile.Name()) + gossConfig := mergeJSONData(ReadJSON(gossfile.Name()), 0, path) b, err := marshal(gossConfig) if err != nil { diff --git a/store_test.go b/store_test.go new file mode 100644 index 0000000..d442146 --- /dev/null +++ b/store_test.go @@ -0,0 +1,123 @@ +package goss + +import ( + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" +) + +const GossTestingEnvOS = "GOSS_TESTING_OS" + +func Test_RenderJSON(t *testing.T) { + err := os.Setenv(GossTestingEnvOS, "centos") + if err != nil { + panic(err.Error()) + } + defer os.Unsetenv(GossTestingEnvOS) + + tmpVars, err := ioutil.TempFile("", "example_tmp_vars_*.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpVars.Name()) + + _, err = tmpVars.WriteString(getExampleVars()) + if err != nil { + panic(err.Error()) + } + + tmpGossfile, err := ioutil.TempFile("", "example_tmp_gossfile_*.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpGossfile.Name()) + + _, err = tmpGossfile.WriteString(getExampleTemplate()) + if err != nil { + panic(err.Error()) + } + + result := RenderJSON(tmpGossfile, tmpVars) + + assert.Equal(t, getExpecetd(), result) +} + +func getExampleVars() string { + return ` + centos: + packages: + kernel: + - "4.9.11-centos" + - "4.9.11-centos2" + debian: + packages: + kernel: + - "4.9.11-debian" + - "4.9.11-debian2" + users: + - user1 + - user2 + ` +} + +func getExampleTemplate() string { + return ` + package: + # Looping over a variables defined in a vars.yaml using $OS environment variable as a lookup key + {{range $name, $vers := index .Vars .Env.GOSS_TESTING_OS "packages"}} + {{$name}}: + installed: true + versions: + {{range $vers}} + - {{.}} + {{end}} + {{end}} + + # This test is only when the OS environment variable matches the pattern + {{if .Env.GOSS_TESTING_OS | regexMatch "[Cc]ent(OS|os)"}} + libselinux: + installed: true + {{end}} + + # Loop over users + user: + {{range .Vars.users}} + {{.}}: + exists: true + groups: + - {{.}} + home: /home/{{.}} + shell: /bin/bash + {{end}} + + + package: + {{if eq .Env.GOSS_TESTING_OS "centos"}} + # This test is only when $OS environment variable is set to "centos" + libselinux: + installed: true + {{end}} + ` +} + +func getExpecetd() string { + expected := `package: + libselinux: + installed: true +user: + user1: + exists: true + groups: + - user1 + home: /home/user1 + shell: /bin/bash + user2: + exists: true + groups: + - user2 + home: /home/user2 + shell: /bin/bash +` + return expected +} diff --git a/system/dns_test.go b/system/dns_test.go index 2252e0b..fdbd77b 100644 --- a/system/dns_test.go +++ b/system/dns_test.go @@ -22,7 +22,7 @@ func TestNewDefDNS_WithQueryType(t *testing.T) { } func TestAddr(t *testing.T) { - dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 50}) + dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 200}) r, err := dns.Resolvable() assert.Nil(t, err) diff --git a/template.go b/template.go index 97837b3..6d02bf8 100644 --- a/template.go +++ b/template.go @@ -11,10 +11,12 @@ import ( "text/template" ) +// mkSlice is able to create loops in templates func mkSlice(args ...interface{}) []interface{} { return args } +// readFile reads a file from inside a template func readFile(f string) (string, error) { b, err := ioutil.ReadFile(f) if err != nil { @@ -49,6 +51,7 @@ var funcMap = map[string]interface{}{ "regexMatch": regexMatch, } +// NewTemplateFilter creates a new filter with template extensions func NewTemplateFilter(varsFile string) func([]byte) []byte { vars, err := varsFromFile(varsFile) if err != nil { diff --git a/template_test.go b/template_test.go new file mode 100644 index 0000000..358bf4f --- /dev/null +++ b/template_test.go @@ -0,0 +1,86 @@ +package goss + +import ( + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" +) + +func Test_NewTemplateFilter_Variable(t *testing.T) { + vars, err := ioutil.TempFile("", "*_vars.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(vars.Name()) + + _, err = vars.WriteString("test: testing") + if err != nil { + panic(err.Error()) + } + + content := []byte(`variable: {{.Vars.test}}`) + + filter := NewTemplateFilter(vars.Name()) + result := filter(content) + + assert.Equal(t, "variable: testing", string(result)) +} + +func Test_NewTemplateFilter_Env(t *testing.T) { + err := os.Setenv("GOSS_TEST_ENV", "env testing") + if err != nil{ + panic(err.Error()) + } + defer os.Unsetenv("template_goss_test_env") + + content := []byte(`environment: {{.Env.GOSS_TEST_ENV}}`) + + filter := NewTemplateFilter("") + result := filter(content) + + assert.Equal(t, "environment: env testing", string(result)) +} + +func Test_NewTemplateFilter_mkSlice(t *testing.T) { + content := []byte(`{{- range mkSlice "test1" "test2" "test3"}}{{.}}{{end}}`) + + filter := NewTemplateFilter("") + result := filter(content) + + assert.Equal(t, "test1test2test3", string(result)) +} + +func Test_NewTemplateFilter_readFile(t *testing.T) { + tmpFile, err := ioutil.TempFile("", "read_file_temp") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpFile.Name()) + tmpFile.WriteString("test read file from template") + + content := []byte(`{{readFile "` + tmpFile.Name() + `"}}`) + + filter := NewTemplateFilter("") + result := filter(content) + + assert.Equal(t, "test read file from template", string(result)) +} + +func Test_NewTemplateFilter_regexMatch(t *testing.T) { + content := []byte(`{{if "centos" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{end}}`) + + filter := NewTemplateFilter("") + result := filter(content) + + assert.Equal(t, "detected regex", string(result)) +} + +func Test_NewTemplateFilter_regexMatch_fail(t *testing.T) { + content := []byte(`{{if "ubuntu" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{else}}no match{{end}}`) + + filter := NewTemplateFilter("") + result := filter(content) + + assert.Equal(t, "no match", string(result)) +} \ No newline at end of file From 1791d2d52c46044b7350799dc3f4f4447419197b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 15:42:35 +0200 Subject: [PATCH 17/19] Add app render tests --- app.go | 4 ++-- app_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 app_test.go diff --git a/app.go b/app.go index 0ead873..603759d 100644 --- a/app.go +++ b/app.go @@ -34,13 +34,13 @@ func (g *GossRunTime) Validate(v *Validator) int { func (g *GossRunTime) Render() (string, error) { goss, err := os.Open(g.Gossfile) if err != nil { - return "", err + return "", fmt.Errorf("Could not open gossfile with error: %s", err.Error()) } defer goss.Close() vars, err := os.Open(g.Vars) if err != nil { - return "", err + return "", fmt.Errorf("Could not open varsfile with error: %s", err.Error()) } defer vars.Close() diff --git a/app_test.go b/app_test.go new file mode 100644 index 0000000..01f5b02 --- /dev/null +++ b/app_test.go @@ -0,0 +1,34 @@ +package goss + +import ( + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" +) + +func Test_Render_WithoutGossfile(t *testing.T) { + runtime := GossRunTime{} + result, err := runtime.Render() + + assert.NotNil(t, err) + assert.Equal(t, "Could not open gossfile with error: open : no such file or directory", err.Error()) + assert.Empty(t, result) +} + + +func Test_Render_WithoutVarsfile(t *testing.T) { + file, err := ioutil.TempFile("", "tmp_gossfile_*.yaml") + defer os.Remove(file.Name()) + + runtime := GossRunTime{ + Gossfile: file.Name(), + Vars: "/invalidpath", + } + result, err := runtime.Render() + + assert.NotNil(t, err) + assert.Equal(t, "Could not open varsfile with error: open /invalidpath: no such file or directory", err.Error()) + assert.Empty(t, result) +} + From d87322f95926146d242dd33303e69b7ae1df71d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 15:57:39 +0200 Subject: [PATCH 18/19] Run go fmt --- add.go | 50 +- add_test.go | 4 +- app.go | 116 ++-- app_test.go | 44 +- cmd/goss/goss.go | 828 +++++++++++++-------------- cmd/goss/goss_test.go | 48 +- development/http/http_test_server.go | 2 +- development/ssl/https_server.go | 60 +- outputs/documentation.go | 2 +- outputs/documentation_test.go | 58 +- outputs/json.go | 2 +- outputs/json_test.go | 50 +- outputs/rspecish.go | 10 +- outputs/rspecish_test.go | 52 +- outputs/test_helper.go | 26 +- resource/addr_test.go | 4 +- resource/dns.go | 6 +- resource/dns_test.go | 130 ++--- resource/http.go | 45 +- resource/http_test.go | 2 +- resource/package.go | 12 +- resource/package_test.go | 51 +- resource/resource_list.go | 1 + resource/resource_list_test.go | 1 - resource/validate_test.go | 8 +- serve_test.go | 76 +-- store_test.go | 66 +-- system/dns.go | 1 + system/dns_test.go | 76 +-- system/http.go | 13 +- system/kernel_param.go | 2 +- system/package_test.go | 28 +- system/port.go | 2 +- system/process.go | 2 +- system/system.go | 2 +- template_test.go | 98 ++-- util/command_test.go | 24 +- util/config.go | 1 - util/goss_testing/helper.go | 12 +- validate.go | 16 +- validate_test.go | 64 +-- 41 files changed, 1046 insertions(+), 1049 deletions(-) diff --git a/add.go b/add.go index 82c2af4..641144e 100644 --- a/add.go +++ b/add.go @@ -85,21 +85,21 @@ func (a *Add) AddResource(fileName string, gossConfig GossConfig, resourceName, fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "dns": res, err := gossConfig.DNS.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "file": res, err := gossConfig.Files.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "Group": res, err := gossConfig.Groups.AppendSysResource(key, a.Sys, config) if err != nil { @@ -113,56 +113,56 @@ func (a *Add) AddResource(fileName string, gossConfig GossConfig, resourceName, fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "port": res, err := gossConfig.Ports.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "process": res, err := gossConfig.Processes.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "service": res, err := gossConfig.Services.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "user": res, err := gossConfig.Users.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "gossfile": res, err := gossConfig.Gossfiles.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "kernel-param": res, err := gossConfig.KernelParams.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "Mount": res, err := gossConfig.Mounts.AppendSysResource(key, a.Sys, config) if err != nil { fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) case "interface": res, err := gossConfig.Interfaces.AppendSysResource(key, a.Sys, config) if err != nil { @@ -176,7 +176,7 @@ func (a *Add) AddResource(fileName string, gossConfig GossConfig, resourceName, fmt.Println(err) os.Exit(1) } - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) default: panic("Undefined resource name: " + resourceName) } @@ -212,28 +212,28 @@ func (a *Add) AutoAddResource(fileName string, gossConfig GossConfig, key string // file if strings.Contains(key, "/") { if res, _, ok := gossConfig.Files.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } } // group if res, _, ok := gossConfig.Groups.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } // package if res, _, ok := gossConfig.Packages.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } // port if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } // process if res, sysres, ok := gossConfig.Processes.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) ports := system.GetPorts(true) pids, _ := sysres.Pids() for _, pid := range pids { @@ -243,7 +243,7 @@ func (a *Add) AutoAddResource(fileName string, gossConfig GossConfig, key string if entry.Pid == pidS { // port if res, _, ok := gossConfig.Ports.AppendSysResourceIfExists(port, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } } } @@ -253,23 +253,23 @@ func (a *Add) AutoAddResource(fileName string, gossConfig GossConfig, key string // Service if res, _, ok := gossConfig.Services.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } // user if res, _, ok := gossConfig.Users.AppendSysResourceIfExists(key, a.Sys); ok == true { - a.resourcePrint(fileName, res) + a.resourcePrint(fileName, res) } return nil } func (a *Add) resourcePrint(fileName string, res resource.ResourceRead) { - resMap := map[string]resource.ResourceRead{res.ID(): res} + resMap := map[string]resource.ResourceRead{res.ID(): res} - oj, _ := marshal(resMap) - typ := reflect.TypeOf(res) - typs := strings.Split(typ.String(), ".")[1] + oj, _ := marshal(resMap) + typ := reflect.TypeOf(res) + typs := strings.Split(typ.String(), ".")[1] fmt.Fprintf(a.Writer, "Adding %s to '%s':\n\n%s\n\n", typs, fileName, string(oj)) -} \ No newline at end of file +} diff --git a/add_test.go b/add_test.go index a479cea..83f4089 100644 --- a/add_test.go +++ b/add_test.go @@ -1,8 +1,8 @@ package goss import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func Test_ExtractHeaderArgument(t *testing.T) { diff --git a/app.go b/app.go index 603759d..0ef1efa 100644 --- a/app.go +++ b/app.go @@ -1,72 +1,72 @@ package goss import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "time" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" ) // GossRunTime represents the global runtime configs which can be set in goss type GossRunTime struct { - //Gossfile which should holds the test config - Gossfile string - //Vars file which holds the variabesl - Vars string - //Package defines which package manager you want to use, i.e. yum, apt, ... - Package string //this does not belong here imho - //Debug on true will create a more verbose output - Debug bool + //Gossfile which should holds the test config + Gossfile string + //Vars file which holds the variabesl + Vars string + //Package defines which package manager you want to use, i.e. yum, apt, ... + Package string //this does not belong here imho + //Debug on true will create a more verbose output + Debug bool } // Serve serves a new health endpoint func (g *GossRunTime) Serve(endpoint string, handler *HealthHandler) { - handler.Serve(endpoint) + handler.Serve(endpoint) } // Validate starts the validation process func (g *GossRunTime) Validate(v *Validator) int { - return v.Validate(time.Now()) + return v.Validate(time.Now()) } // Render renders a template file func (g *GossRunTime) Render() (string, error) { - goss, err := os.Open(g.Gossfile) - if err != nil { - return "", fmt.Errorf("Could not open gossfile with error: %s", err.Error()) - } - defer goss.Close() + goss, err := os.Open(g.Gossfile) + if err != nil { + return "", fmt.Errorf("Could not open gossfile with error: %s", err.Error()) + } + defer goss.Close() - vars, err := os.Open(g.Vars) - if err != nil { - return "", fmt.Errorf("Could not open varsfile with error: %s", err.Error()) - } - defer vars.Close() + vars, err := os.Open(g.Vars) + if err != nil { + return "", fmt.Errorf("Could not open varsfile with error: %s", err.Error()) + } + defer vars.Close() - return RenderJSON(goss, vars), nil + return RenderJSON(goss, vars), nil } // GetGossConfig returns the goss configuration func (g *GossRunTime) GetGossConfig() GossConfig { - // handle stdin - var fh *os.File - var path, source string - var gossConfig GossConfig - TemplateFilter = NewTemplateFilter(g.Vars) - specFile := g.Gossfile - if specFile == "-" { - source = "STDIN" - fh = os.Stdin - data, err := ioutil.ReadAll(fh) - if err != nil { - fmt.Printf("Error: %v\n", err) - os.Exit(1) - } - OutStoreFormat = getStoreFormatFromData(data) - gossConfig = ReadJSONData(data, true) - } else if specFile == "testing" { - json := []byte(` + // handle stdin + var fh *os.File + var path, source string + var gossConfig GossConfig + TemplateFilter = NewTemplateFilter(g.Vars) + specFile := g.Gossfile + if specFile == "-" { + source = "STDIN" + fh = os.Stdin + data, err := ioutil.ReadAll(fh) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + OutStoreFormat = getStoreFormatFromData(data) + gossConfig = ReadJSONData(data, true) + } else if specFile == "testing" { + json := []byte(` command: echo hello: exit-status: 0 @@ -74,19 +74,19 @@ command: - hello timeout: 10000 `) - gossConfig = ReadJSONData(json, true) - } else { - source = specFile - path = filepath.Dir(specFile) - OutStoreFormat = getStoreFormatFromFileName(specFile) - gossConfig = ReadJSON(specFile) - } + gossConfig = ReadJSONData(json, true) + } else { + source = specFile + path = filepath.Dir(specFile) + OutStoreFormat = getStoreFormatFromFileName(specFile) + gossConfig = ReadJSON(specFile) + } - gossConfig = mergeJSONData(gossConfig, 0, path) + gossConfig = mergeJSONData(gossConfig, 0, path) - if len(gossConfig.Resources()) == 0 { - fmt.Printf("Error: found 0 tests, source: %v\n", source) - os.Exit(1) - } - return gossConfig -} \ No newline at end of file + if len(gossConfig.Resources()) == 0 { + fmt.Printf("Error: found 0 tests, source: %v\n", source) + os.Exit(1) + } + return gossConfig +} diff --git a/app_test.go b/app_test.go index 01f5b02..aada672 100644 --- a/app_test.go +++ b/app_test.go @@ -1,34 +1,32 @@ package goss import ( - "github.com/stretchr/testify/assert" - "io/ioutil" - "os" - "testing" + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" ) func Test_Render_WithoutGossfile(t *testing.T) { - runtime := GossRunTime{} - result, err := runtime.Render() + runtime := GossRunTime{} + result, err := runtime.Render() - assert.NotNil(t, err) - assert.Equal(t, "Could not open gossfile with error: open : no such file or directory", err.Error()) - assert.Empty(t, result) + assert.NotNil(t, err) + assert.Equal(t, "Could not open gossfile with error: open : no such file or directory", err.Error()) + assert.Empty(t, result) } - func Test_Render_WithoutVarsfile(t *testing.T) { - file, err := ioutil.TempFile("", "tmp_gossfile_*.yaml") - defer os.Remove(file.Name()) - - runtime := GossRunTime{ - Gossfile: file.Name(), - Vars: "/invalidpath", - } - result, err := runtime.Render() - - assert.NotNil(t, err) - assert.Equal(t, "Could not open varsfile with error: open /invalidpath: no such file or directory", err.Error()) - assert.Empty(t, result) + file, err := ioutil.TempFile("", "tmp_gossfile_*.yaml") + defer os.Remove(file.Name()) + + runtime := GossRunTime{ + Gossfile: file.Name(), + Vars: "/invalidpath", + } + result, err := runtime.Render() + + assert.NotNil(t, err) + assert.Equal(t, "Could not open varsfile with error: open /invalidpath: no such file or directory", err.Error()) + assert.Empty(t, result) } - diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 56a6019..8fa8de5 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -1,466 +1,466 @@ package main import ( - "fmt" - "github.com/SimonBaeumer/goss/system" - "github.com/fatih/color" - "github.com/patrickmn/go-cache" - "log" - "os" - "sync" - "time" + "fmt" + "github.com/SimonBaeumer/goss/system" + "github.com/fatih/color" + "github.com/patrickmn/go-cache" + "log" + "os" + "sync" + "time" - "github.com/SimonBaeumer/goss" - "github.com/SimonBaeumer/goss/outputs" - "github.com/urfave/cli" + "github.com/SimonBaeumer/goss" + "github.com/SimonBaeumer/goss/outputs" + "github.com/urfave/cli" ) var version string func main() { - app := createApp() + app := createApp() - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } } // CliConfig represents all configurations passed by the cli app type CliConfig struct { - FormatOptions []string - Sleep time.Duration - RetryTimeout time.Duration - MaxConcurrent int - Vars string - Gossfile string - ExcludeAttr []string - Timeout int - AllowInsecure bool - NoFollowRedirects bool - Server string - Username string - Password string - Header string - Endpoint string - ListenAddr string - Cache time.Duration - Format string - NoColor bool - Color bool + FormatOptions []string + Sleep time.Duration + RetryTimeout time.Duration + MaxConcurrent int + Vars string + Gossfile string + ExcludeAttr []string + Timeout int + AllowInsecure bool + NoFollowRedirects bool + Server string + Username string + Password string + Header string + Endpoint string + ListenAddr string + Cache time.Duration + Format string + NoColor bool + Color bool } // NewCliConfig creates an object from the cli.Context. It is used as a constructor and will do simple type conversions // and validations func NewCliConfig(c *cli.Context) CliConfig { - return CliConfig{ - FormatOptions: c.StringSlice("format-options"), - Sleep: c.Duration("sleep"), - RetryTimeout: c.Duration("retry-timeout"), - MaxConcurrent: c.Int("max-concurrent"), - Vars: c.GlobalString("vars"), - Gossfile: c.GlobalString("gossfile"), - ExcludeAttr: c.GlobalStringSlice("exclude-attr"), - Timeout: int(c.Duration("timeout") / time.Millisecond), - AllowInsecure: c.Bool("insecure"), - NoFollowRedirects: c.Bool("no-follow-redirects"), - Server: c.String("server"), - Username: c.String("username"), - Password: c.String("password"), - Header: c.String("header"), - Endpoint: c.String("endpoint"), - ListenAddr: c.String("listen-addr"), - Cache: c.Duration("cache"), - Format: c.String("format"), - NoColor: c.Bool("no-color"), - Color: c.Bool("color"), - } + return CliConfig{ + FormatOptions: c.StringSlice("format-options"), + Sleep: c.Duration("sleep"), + RetryTimeout: c.Duration("retry-timeout"), + MaxConcurrent: c.Int("max-concurrent"), + Vars: c.GlobalString("vars"), + Gossfile: c.GlobalString("gossfile"), + ExcludeAttr: c.GlobalStringSlice("exclude-attr"), + Timeout: int(c.Duration("timeout") / time.Millisecond), + AllowInsecure: c.Bool("insecure"), + NoFollowRedirects: c.Bool("no-follow-redirects"), + Server: c.String("server"), + Username: c.String("username"), + Password: c.String("password"), + Header: c.String("header"), + Endpoint: c.String("endpoint"), + ListenAddr: c.String("listen-addr"), + Cache: c.Duration("cache"), + Format: c.String("format"), + NoColor: c.Bool("no-color"), + Color: c.Bool("color"), + } } // Create the cli.App object func createApp() *cli.App { - app := cli.NewApp() - app.EnableBashCompletion = true - app.Version = version - app.Name = "goss" - app.Usage = "Quick and Easy server validation" - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "gossfile, g", - Value: "./goss.yaml", - Usage: "GossRunTime file to read from / write to", - EnvVar: "GOSS_FILE", - }, - cli.StringFlag{ - Name: "vars", - Usage: "json/yaml file containing variables for template", - EnvVar: "GOSS_VARS", - }, - } - app.Commands = []cli.Command{ - createValidateCommand(app), - createServeCommand(), - createAddCommand(app), - { - Name: "render", - Aliases: []string{"r"}, - Usage: "render gossfile after imports", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "debug, d", - Usage: fmt.Sprintf("Print debugging info when rendering"), - }, - }, - Action: func(c *cli.Context) error { - conf := NewCliConfig(c) - runtime := goss.GossRunTime{ - Vars: conf.Vars, - Gossfile: conf.Gossfile, - } + app := cli.NewApp() + app.EnableBashCompletion = true + app.Version = version + app.Name = "goss" + app.Usage = "Quick and Easy server validation" + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "gossfile, g", + Value: "./goss.yaml", + Usage: "GossRunTime file to read from / write to", + EnvVar: "GOSS_FILE", + }, + cli.StringFlag{ + Name: "vars", + Usage: "json/yaml file containing variables for template", + EnvVar: "GOSS_VARS", + }, + } + app.Commands = []cli.Command{ + createValidateCommand(app), + createServeCommand(), + createAddCommand(app), + { + Name: "render", + Aliases: []string{"r"}, + Usage: "render gossfile after imports", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "debug, d", + Usage: fmt.Sprintf("Print debugging info when rendering"), + }, + }, + Action: func(c *cli.Context) error { + conf := NewCliConfig(c) + runtime := goss.GossRunTime{ + Vars: conf.Vars, + Gossfile: conf.Gossfile, + } - fmt.Print(runtime.Render()) - return nil - }, - }, - { - Name: "autoadd", - Aliases: []string{"aa"}, - Usage: "automatically add all matching resource to the test suite", - Action: func(c *cli.Context) error { - conf := NewCliConfig(c) + fmt.Print(runtime.Render()) + return nil + }, + }, + { + Name: "autoadd", + Aliases: []string{"aa"}, + Usage: "automatically add all matching resource to the test suite", + Action: func(c *cli.Context) error { + conf := NewCliConfig(c) - a := goss.Add{ - Writer: app.Writer, - Sys: system.New(), - Username: conf.Username, - Password: conf.Password, - Server: conf.Server, - Timeout: conf.Timeout, - NoFollowRedirects: conf.NoFollowRedirects, - AllowInsecure: conf.AllowInsecure, - ExcludeAttr: conf.ExcludeAttr, - Header: conf.Header, - } - return a.AutoAddResources(c.GlobalString("gossfile"), c.Args()) - }, - }, - } - return app + a := goss.Add{ + Writer: app.Writer, + Sys: system.New(), + Username: conf.Username, + Password: conf.Password, + Server: conf.Server, + Timeout: conf.Timeout, + NoFollowRedirects: conf.NoFollowRedirects, + AllowInsecure: conf.AllowInsecure, + ExcludeAttr: conf.ExcludeAttr, + Header: conf.Header, + } + return a.AutoAddResources(c.GlobalString("gossfile"), c.Args()) + }, + }, + } + return app } func createServeCommand() cli.Command { - return cli.Command{ - Name: "serve", - Aliases: []string{"s"}, - Usage: "Serve a health endpoint", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "format, f", - Value: "rspecish", - Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), - EnvVar: "GOSS_FMT", - }, - cli.StringSliceFlag{ - Name: "format-options, o", - Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), - EnvVar: "GOSS_FMT_OPTIONS", - }, - cli.DurationFlag{ - Name: "cache,c", - Usage: "Time to cache the results", - Value: 5 * time.Second, - EnvVar: "GOSS_CACHE", - }, - cli.StringFlag{ - Name: "listen-addr,l", - Value: ":8080", - Usage: "Address to listen on [ip]:port", - EnvVar: "GOSS_LISTEN", - }, - cli.StringFlag{ - Name: "endpoint,e", - Value: "/healthz", - Usage: "Endpoint to expose", - EnvVar: "GOSS_ENDPOINT", - }, - cli.IntFlag{ - Name: "max-concurrent", - Usage: "Max number of tests to run concurrently", - Value: 50, - EnvVar: "GOSS_MAX_CONCURRENT", - }, - }, - Action: func(c *cli.Context) error { - conf := NewCliConfig(c) + return cli.Command{ + Name: "serve", + Aliases: []string{"s"}, + Usage: "Serve a health endpoint", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "rspecish", + Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), + EnvVar: "GOSS_FMT", + }, + cli.StringSliceFlag{ + Name: "format-options, o", + Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), + EnvVar: "GOSS_FMT_OPTIONS", + }, + cli.DurationFlag{ + Name: "cache,c", + Usage: "Time to cache the results", + Value: 5 * time.Second, + EnvVar: "GOSS_CACHE", + }, + cli.StringFlag{ + Name: "listen-addr,l", + Value: ":8080", + Usage: "Address to listen on [ip]:port", + EnvVar: "GOSS_LISTEN", + }, + cli.StringFlag{ + Name: "endpoint,e", + Value: "/healthz", + Usage: "Endpoint to expose", + EnvVar: "GOSS_ENDPOINT", + }, + cli.IntFlag{ + Name: "max-concurrent", + Usage: "Max number of tests to run concurrently", + Value: 50, + EnvVar: "GOSS_MAX_CONCURRENT", + }, + }, + Action: func(c *cli.Context) error { + conf := NewCliConfig(c) - gossRunTime := goss.GossRunTime{ - Gossfile: c.GlobalString("gossfile"), - Vars: c.GlobalString("vars"), - } + gossRunTime := goss.GossRunTime{ + Gossfile: c.GlobalString("gossfile"), + Vars: c.GlobalString("vars"), + } - h := &goss.HealthHandler{ - Cache: cache.New(conf.Cache, 30 * time.Second), - Outputer: outputs.GetOutputer(conf.Format), - Sys: system.New(), - GossMu: &sync.Mutex{}, - MaxConcurrent: conf.MaxConcurrent, - GossConfig: gossRunTime.GetGossConfig(), - FormatOptions: conf.FormatOptions, - } + h := &goss.HealthHandler{ + Cache: cache.New(conf.Cache, 30*time.Second), + Outputer: outputs.GetOutputer(conf.Format), + Sys: system.New(), + GossMu: &sync.Mutex{}, + MaxConcurrent: conf.MaxConcurrent, + GossConfig: gossRunTime.GetGossConfig(), + FormatOptions: conf.FormatOptions, + } - if conf.Format == "json" { - h.ContentType = "application/json" - } + if conf.Format == "json" { + h.ContentType = "application/json" + } - gossRunTime.Serve(conf.Endpoint, h) - return nil - }, - } + gossRunTime.Serve(conf.Endpoint, h) + return nil + }, + } } -func createAddHandler(app *cli.App, resourceName string) (func(c *cli.Context) error) { - return func(c *cli.Context) error { - conf := NewCliConfig(c) +func createAddHandler(app *cli.App, resourceName string) func(c *cli.Context) error { + return func(c *cli.Context) error { + conf := NewCliConfig(c) - a := goss.Add{ - Sys: system.New(), - Writer: app.Writer, - Username: conf.Username, - Password: conf.Password, - Server: conf.Server, - Timeout: conf.Timeout, - NoFollowRedirects: conf.NoFollowRedirects, - AllowInsecure: conf.AllowInsecure, - ExcludeAttr: conf.ExcludeAttr, - Header: conf.Header, - } + a := goss.Add{ + Sys: system.New(), + Writer: app.Writer, + Username: conf.Username, + Password: conf.Password, + Server: conf.Server, + Timeout: conf.Timeout, + NoFollowRedirects: conf.NoFollowRedirects, + AllowInsecure: conf.AllowInsecure, + ExcludeAttr: conf.ExcludeAttr, + Header: conf.Header, + } - a.AddResources(c.GlobalString("gossfile"), resourceName, c.Args()) - return nil - } + a.AddResources(c.GlobalString("gossfile"), resourceName, c.Args()) + return nil + } } func createAddCommand(app *cli.App) cli.Command { - return cli.Command{ - Name: "add", - Aliases: []string{"a"}, - Usage: "add a resource to the test suite", - Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "exclude-attr", - Usage: "Exclude the following attributes when adding a new resource", - }, - }, - Subcommands: []cli.Command{ - { - Name: "Package", - Usage: "add new package", - Action: createAddHandler(app, "package"), - }, - { - Name: "file", - Usage: "add new file", - Action: createAddHandler(app, "file"), - }, - { - Name: "addr", - Usage: "add new remote address:port - ex: google.com:80", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 500 * time.Millisecond, - }, - }, - Action: createAddHandler(app, "addr"), - }, - { - Name: "port", - Usage: "add new listening [protocol]:port - ex: 80 or udp:123", - Action: createAddHandler(app, "port"), - }, - { - Name: "service", - Usage: "add new service", - Action: createAddHandler(app, "service"), - }, - { - Name: "user", - Usage: "add new user", - Action: createAddHandler(app, "user"), - }, - { - Name: "group", - Usage: "add new group", - Action: createAddHandler(app, "group"), - }, - { - Name: "command", - Usage: "add new command", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 10 * time.Second, - }, - }, - Action: createAddHandler(app, "command"), - }, - { - Name: "dns", - Usage: "add new dns", - Flags: []cli.Flag{ - cli.DurationFlag{ - Name: "timeout", - Value: 500 * time.Millisecond, - }, - cli.StringFlag{ - Name: "server", - Usage: "The IP address of a DNS server to query", - }, - }, - Action: createAddHandler(app, "dns"), - }, - { - Name: "process", - Usage: "add new process name", - Action: createAddHandler(app, "process"), - }, - { - Name: "http", - Usage: "add new http", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "insecure, k", - }, - cli.BoolFlag{ - Name: "no-follow-redirects, r", - }, - cli.DurationFlag{ - Name: "timeout", - Value: 5 * time.Second, - }, - cli.StringFlag{ - Name: "username, u", - Usage: "Username for basic auth", - }, - cli.StringFlag{ - Name: "password, p", - Usage: "Password for basic auth", - }, - cli.StringFlag{ - Name: "header", - Usage: "Set-Cookie: Value", - }, - }, - Action: createAddHandler(app, "http"), - }, - { - Name: "goss", - Usage: "add new goss file, it will be imported from this one", - Action: createAddHandler(app, "goss"), - }, - { - Name: "kernel-param", - Usage: "add new goss kernel param", - Action: createAddHandler(app, "kernel-param"), - }, - { - Name: "mount", - Usage: "add new mount", - Action: createAddHandler(app, "mount"), - }, - { - Name: "interface", - Usage: "add new interface", - Action: createAddHandler(app, "interface"), - }, - }, - } + return cli.Command{ + Name: "add", + Aliases: []string{"a"}, + Usage: "add a resource to the test suite", + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "exclude-attr", + Usage: "Exclude the following attributes when adding a new resource", + }, + }, + Subcommands: []cli.Command{ + { + Name: "Package", + Usage: "add new package", + Action: createAddHandler(app, "package"), + }, + { + Name: "file", + Usage: "add new file", + Action: createAddHandler(app, "file"), + }, + { + Name: "addr", + Usage: "add new remote address:port - ex: google.com:80", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 500 * time.Millisecond, + }, + }, + Action: createAddHandler(app, "addr"), + }, + { + Name: "port", + Usage: "add new listening [protocol]:port - ex: 80 or udp:123", + Action: createAddHandler(app, "port"), + }, + { + Name: "service", + Usage: "add new service", + Action: createAddHandler(app, "service"), + }, + { + Name: "user", + Usage: "add new user", + Action: createAddHandler(app, "user"), + }, + { + Name: "group", + Usage: "add new group", + Action: createAddHandler(app, "group"), + }, + { + Name: "command", + Usage: "add new command", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 10 * time.Second, + }, + }, + Action: createAddHandler(app, "command"), + }, + { + Name: "dns", + Usage: "add new dns", + Flags: []cli.Flag{ + cli.DurationFlag{ + Name: "timeout", + Value: 500 * time.Millisecond, + }, + cli.StringFlag{ + Name: "server", + Usage: "The IP address of a DNS server to query", + }, + }, + Action: createAddHandler(app, "dns"), + }, + { + Name: "process", + Usage: "add new process name", + Action: createAddHandler(app, "process"), + }, + { + Name: "http", + Usage: "add new http", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "insecure, k", + }, + cli.BoolFlag{ + Name: "no-follow-redirects, r", + }, + cli.DurationFlag{ + Name: "timeout", + Value: 5 * time.Second, + }, + cli.StringFlag{ + Name: "username, u", + Usage: "Username for basic auth", + }, + cli.StringFlag{ + Name: "password, p", + Usage: "Password for basic auth", + }, + cli.StringFlag{ + Name: "header", + Usage: "Set-Cookie: Value", + }, + }, + Action: createAddHandler(app, "http"), + }, + { + Name: "goss", + Usage: "add new goss file, it will be imported from this one", + Action: createAddHandler(app, "goss"), + }, + { + Name: "kernel-param", + Usage: "add new goss kernel param", + Action: createAddHandler(app, "kernel-param"), + }, + { + Name: "mount", + Usage: "add new mount", + Action: createAddHandler(app, "mount"), + }, + { + Name: "interface", + Usage: "add new interface", + Action: createAddHandler(app, "interface"), + }, + }, + } } func createValidateCommand(app *cli.App) cli.Command { - startTime := time.Now() + startTime := time.Now() - return cli.Command{ - Name: "validate", - Aliases: []string{"v"}, - Usage: "Validate system", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "format, f", - Value: "rspecish", - Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), - EnvVar: "GOSS_FMT", - }, - cli.StringSliceFlag{ - Name: "format-options, o", - Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), - EnvVar: "GOSS_FMT_OPTIONS", - }, - cli.BoolFlag{ - Name: "color", - Usage: "Force color on", - EnvVar: "GOSS_COLOR", - }, - cli.BoolFlag{ - Name: "no-color", - Usage: "Force color off", - EnvVar: "GOSS_NOCOLOR", - }, - cli.DurationFlag{ - Name: "sleep,s", - Usage: "Time to sleep between retries, only active when -r is set", - Value: 1 * time.Second, - EnvVar: "GOSS_SLEEP", - }, - cli.DurationFlag{ - Name: "retry-timeout,r", - Usage: "Retry on failure so long as elapsed + sleep time is less than this", - Value: 0, - EnvVar: "GOSS_RETRY_TIMEOUT", - }, - cli.IntFlag{ - Name: "max-concurrent", - Usage: "Max number of tests to run concurrently", - Value: 50, - EnvVar: "GOSS_MAX_CONCURRENT", - }, - }, - Action: func(c *cli.Context) error { - conf := NewCliConfig(c) + return cli.Command{ + Name: "validate", + Aliases: []string{"v"}, + Usage: "Validate system", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "format, f", + Value: "rspecish", + Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()), + EnvVar: "GOSS_FMT", + }, + cli.StringSliceFlag{ + Name: "format-options, o", + Usage: fmt.Sprintf("Extra options passed to the formatter, valid options: %s", outputs.FormatOptions()), + EnvVar: "GOSS_FMT_OPTIONS", + }, + cli.BoolFlag{ + Name: "color", + Usage: "Force color on", + EnvVar: "GOSS_COLOR", + }, + cli.BoolFlag{ + Name: "no-color", + Usage: "Force color off", + EnvVar: "GOSS_NOCOLOR", + }, + cli.DurationFlag{ + Name: "sleep,s", + Usage: "Time to sleep between retries, only active when -r is set", + Value: 1 * time.Second, + EnvVar: "GOSS_SLEEP", + }, + cli.DurationFlag{ + Name: "retry-timeout,r", + Usage: "Retry on failure so long as elapsed + sleep time is less than this", + Value: 0, + EnvVar: "GOSS_RETRY_TIMEOUT", + }, + cli.IntFlag{ + Name: "max-concurrent", + Usage: "Max number of tests to run concurrently", + Value: 50, + EnvVar: "GOSS_MAX_CONCURRENT", + }, + }, + Action: func(c *cli.Context) error { + conf := NewCliConfig(c) - runtime := getGossRunTime(conf.Gossfile, conf.Vars) + runtime := getGossRunTime(conf.Gossfile, conf.Vars) - v := &goss.Validator{ - OutputWriter: app.Writer, - MaxConcurrent: conf.MaxConcurrent, - Outputer: outputs.GetOutputer(conf.Format), - FormatOptions: conf.FormatOptions, - GossConfig: runtime.GetGossConfig(), - } + v := &goss.Validator{ + OutputWriter: app.Writer, + MaxConcurrent: conf.MaxConcurrent, + Outputer: outputs.GetOutputer(conf.Format), + FormatOptions: conf.FormatOptions, + GossConfig: runtime.GetGossConfig(), + } - //TODO: ugly shit to set the color here, tmp fix for the moment! - if conf.NoColor { - color.NoColor = true - } - if conf.Color { - color.NoColor = false - } + //TODO: ugly shit to set the color here, tmp fix for the moment! + if conf.NoColor { + color.NoColor = true + } + if conf.Color { + color.NoColor = false + } - if conf.Gossfile == "testing" { - v.Validate(startTime) - return nil - } + if conf.Gossfile == "testing" { + v.Validate(startTime) + return nil + } - os.Exit(runtime.Validate(v)) - return nil - }, - } + os.Exit(runtime.Validate(v)) + return nil + }, + } } func getGossRunTime(gossfile string, vars string) goss.GossRunTime { - runtime := goss.GossRunTime{ - Gossfile: gossfile, - Vars: vars, - } - return runtime + runtime := goss.GossRunTime{ + Gossfile: gossfile, + Vars: vars, + } + return runtime } diff --git a/cmd/goss/goss_test.go b/cmd/goss/goss_test.go index 38b1670..2ef63ef 100644 --- a/cmd/goss/goss_test.go +++ b/cmd/goss/goss_test.go @@ -1,46 +1,46 @@ package main import ( - "bytes" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "bytes" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) func TestApp_Validate(t *testing.T) { - b := &bytes.Buffer{} - app := createApp() - app.Writer = b + b := &bytes.Buffer{} + app := createApp() + app.Writer = b - r := app.Run([]string{"", "--gossfile", "testing", "validate"}) + r := app.Run([]string{"", "--gossfile", "testing", "validate"}) - assert.Nil(t, r) - assert.Contains(t, b.String(), "Count: 2, Failed: 0, Skipped: 0") + assert.Nil(t, r) + assert.Contains(t, b.String(), "Count: 2, Failed: 0, Skipped: 0") } func TestApp_Add(t *testing.T) { - b := &bytes.Buffer{} - app := createApp() - app.Writer = b + b := &bytes.Buffer{} + app := createApp() + app.Writer = b - file, err := ioutil.TempFile("/tmp", "testing_goss_*.yaml") - if err != nil { - panic(err.Error()) - } - defer file.Close() + file, err := ioutil.TempFile("/tmp", "testing_goss_*.yaml") + if err != nil { + panic(err.Error()) + } + defer file.Close() - r := app.Run([]string{"", "--gossfile", file.Name(), "add", "http", "http://google.com"}) + r := app.Run([]string{"", "--gossfile", file.Name(), "add", "http", "http://google.com"}) - assert.Nil(t, r) - assert.Contains(t, b.String(), getAddResult()) - assert.Contains(t, b.String(), "Adding HTTP to '/tmp/testing_goss_") + assert.Nil(t, r) + assert.Contains(t, b.String(), getAddResult()) + assert.Contains(t, b.String(), "Adding HTTP to '/tmp/testing_goss_") } func getAddResult() string { - return `http://google.com: + return `http://google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: []` -} \ No newline at end of file +} diff --git a/development/http/http_test_server.go b/development/http/http_test_server.go index c8accb2..0c568da 100644 --- a/development/http/http_test_server.go +++ b/development/http/http_test_server.go @@ -39,4 +39,4 @@ func redirectHandler(w http.ResponseWriter, r *http.Request) { log.Println("/redirect") printHeader(r) http.Redirect(w, r, "/", RedirectStatusCode) -} \ No newline at end of file +} diff --git a/development/ssl/https_server.go b/development/ssl/https_server.go index 02fe619..afaba26 100644 --- a/development/ssl/https_server.go +++ b/development/ssl/https_server.go @@ -1,47 +1,47 @@ package main import ( - "crypto/tls" - "crypto/x509" - "io/ioutil" - "log" - "net/http" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "log" + "net/http" ) // Create a minimal https server with // client cert authentication func main() { - // Add root ca - caCert, _ := ioutil.ReadFile("ca.crt") - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) + // Add root ca + caCert, _ := ioutil.ReadFile("ca.crt") + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) - // Add valid client certificates - clientCert, _ := ioutil.ReadFile("client.crt") - clientCAs := x509.NewCertPool() - clientCAs.AppendCertsFromPEM(clientCert) + // Add valid client certificates + clientCert, _ := ioutil.ReadFile("client.crt") + clientCAs := x509.NewCertPool() + clientCAs.AppendCertsFromPEM(clientCert) - server := &http.Server{ - Addr: ":8081", - TLSConfig: &tls.Config{ - ClientAuth: tls.RequireAndVerifyClientCert, - RootCAs: caCertPool, - ClientCAs: clientCAs, - }, - } - server.Handler = &handler{} + server := &http.Server{ + Addr: ":8081", + TLSConfig: &tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + RootCAs: caCertPool, + ClientCAs: clientCAs, + }, + } + server.Handler = &handler{} - err := server.ListenAndServeTLS("server.crt", "server.key") - if err != nil { - log.Fatal(err.Error()) - } + err := server.ListenAndServeTLS("server.crt", "server.key") + if err != nil { + log.Fatal(err.Error()) + } } type handler struct{} // ServeHTTP serves the handler function func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if _, err := w.Write([]byte("PONG\n")); err != nil { - log.Fatal(err.Error()) - } -} \ No newline at end of file + if _, err := w.Write([]byte("PONG\n")); err != nil { + log.Fatal(err.Error()) + } +} diff --git a/outputs/documentation.go b/outputs/documentation.go index 6fd8c36..cc5af90 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -10,7 +10,7 @@ import ( ) // Documentation represents the documentation output type -type Documentation struct{ +type Documentation struct { //FakeDuration will only be used for testing purposes FakeDuration time.Duration } diff --git a/outputs/documentation_test.go b/outputs/documentation_test.go index 9761874..c5998c7 100644 --- a/outputs/documentation_test.go +++ b/outputs/documentation_test.go @@ -1,45 +1,45 @@ package outputs import ( - "bytes" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/util" - "github.com/stretchr/testify/assert" - "sync" - "testing" - "time" + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" ) func TestDocumentation_Name(t *testing.T) { - j := Documentation{} - assert.Equal(t, "documentation", j.Name()) + j := Documentation{} + assert.Equal(t, "documentation", j.Name()) } func TestDocumentation_Output(t *testing.T) { - var wg sync.WaitGroup - b := &bytes.Buffer{} - d, _ := time.ParseDuration("2s") - j := Documentation{FakeDuration: d} - out := make(chan []resource.TestResult) - r := 1 - - go func() { - defer wg.Done() - wg.Add(1) - r = j.Output(b, out, time.Now(), util.OutputConfig{}) - }() - - out <- GetExampleTestResult() - - close(out) - wg.Wait() - expectedJson := `Title: my title + var wg sync.WaitGroup + b := &bytes.Buffer{} + d, _ := time.ParseDuration("2s") + j := Documentation{FakeDuration: d} + out := make(chan []resource.TestResult) + r := 1 + + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() + + out <- GetExampleTestResult() + + close(out) + wg.Wait() + expectedJson := `Title: my title resource type: my resource id: a property: matches expectation: [expected] Total Duration: 2.000s Count: 1, Failed: 0, Skipped: 0 ` - assert.Equal(t, expectedJson, b.String()) - assert.Equal(t, 0, r) + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) } diff --git a/outputs/json.go b/outputs/json.go index 343c979..c2611bf 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -12,7 +12,7 @@ import ( ) // Json represents the json output type -type Json struct{ +type Json struct { // FakeDuration will only be used for testing purposes FakeDuration time.Duration } diff --git a/outputs/json_test.go b/outputs/json_test.go index 3b1411c..b571dd2 100644 --- a/outputs/json_test.go +++ b/outputs/json_test.go @@ -1,38 +1,38 @@ package outputs import ( - "bytes" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/util" - "github.com/stretchr/testify/assert" - "sync" - "testing" - "time" + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" ) func TestJson_Name(t *testing.T) { - j := Json{} - assert.Equal(t, "json", j.Name()) + j := Json{} + assert.Equal(t, "json", j.Name()) } func TestJson_Output(t *testing.T) { - var wg sync.WaitGroup - b := &bytes.Buffer{} - j := Json{FakeDuration: 1000} - out := make(chan []resource.TestResult) - r := 1 + var wg sync.WaitGroup + b := &bytes.Buffer{} + j := Json{FakeDuration: 1000} + out := make(chan []resource.TestResult) + r := 1 - go func() { - defer wg.Done() - wg.Add(1) - r = j.Output(b, out, time.Now(), util.OutputConfig{}) - }() + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() - out <- GetExampleTestResult() + out <- GetExampleTestResult() - close(out) - wg.Wait() - expectedJson := `{ + close(out) + wg.Wait() + expectedJson := `{ "results": [ { "duration": 500, @@ -61,6 +61,6 @@ func TestJson_Output(t *testing.T) { } } ` - assert.Equal(t, expectedJson, b.String()) - assert.Equal(t, 0, r) + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) } diff --git a/outputs/rspecish.go b/outputs/rspecish.go index fdb16d8..209f4c4 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -10,7 +10,7 @@ import ( ) // Rspecish represents the rspecish output type -type Rspecish struct{ +type Rspecish struct { //FakeDuration will only be needed for testing purposes FakeDuration time.Duration } @@ -50,10 +50,10 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, "\n\n") fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped)) - duration := time.Since(startTime) - if r.FakeDuration != 0 { - duration = r.FakeDuration - } + duration := time.Since(startTime) + if r.FakeDuration != 0 { + duration = r.FakeDuration + } fmt.Fprint(w, summary(duration.Seconds(), testCount, failed, skipped)) if failed > 0 { return 1 diff --git a/outputs/rspecish_test.go b/outputs/rspecish_test.go index 6abd46f..7c3e6b2 100644 --- a/outputs/rspecish_test.go +++ b/outputs/rspecish_test.go @@ -1,43 +1,43 @@ package outputs import ( - "bytes" - "github.com/SimonBaeumer/goss/resource" - "github.com/SimonBaeumer/goss/util" - "github.com/stretchr/testify/assert" - "sync" - "testing" - "time" + "bytes" + "github.com/SimonBaeumer/goss/resource" + "github.com/SimonBaeumer/goss/util" + "github.com/stretchr/testify/assert" + "sync" + "testing" + "time" ) func TestRspecish_Name(t *testing.T) { - j := Rspecish{} - assert.Equal(t, "rspecish", j.Name()) + j := Rspecish{} + assert.Equal(t, "rspecish", j.Name()) } func TestRspecish_Output(t *testing.T) { - var wg sync.WaitGroup - b := &bytes.Buffer{} - d, _ := time.ParseDuration("2s") - j := Rspecish{FakeDuration: d} - out := make(chan []resource.TestResult) - r := 1 + var wg sync.WaitGroup + b := &bytes.Buffer{} + d, _ := time.ParseDuration("2s") + j := Rspecish{FakeDuration: d} + out := make(chan []resource.TestResult) + r := 1 - go func() { - defer wg.Done() - wg.Add(1) - r = j.Output(b, out, time.Now(), util.OutputConfig{}) - }() + go func() { + defer wg.Done() + wg.Add(1) + r = j.Output(b, out, time.Now(), util.OutputConfig{}) + }() - out <- GetExampleTestResult() + out <- GetExampleTestResult() - close(out) - wg.Wait() - expectedJson := `. + close(out) + wg.Wait() + expectedJson := `. Total Duration: 2.000s Count: 1, Failed: 0, Skipped: 0 ` - assert.Equal(t, expectedJson, b.String()) - assert.Equal(t, 0, r) + assert.Equal(t, expectedJson, b.String()) + assert.Equal(t, 0, r) } diff --git a/outputs/test_helper.go b/outputs/test_helper.go index b078c81..3e4ccb2 100644 --- a/outputs/test_helper.go +++ b/outputs/test_helper.go @@ -1,20 +1,20 @@ package outputs import ( - "github.com/SimonBaeumer/goss/resource" - "time" + "github.com/SimonBaeumer/goss/resource" + "time" ) func GetExampleTestResult() []resource.TestResult { - return []resource.TestResult{ - { - Title: "my title", - Duration: time.Duration(500), - Successful: true, - ResourceType: "resource type", - ResourceId: "my resource id", - Property: "a property", - Expected: []string{"expected"}, - }, - } + return []resource.TestResult{ + { + Title: "my title", + Duration: time.Duration(500), + Successful: true, + ResourceType: "resource type", + ResourceId: "my resource id", + Property: "a property", + Expected: []string{"expected"}, + }, + } } diff --git a/resource/addr_test.go b/resource/addr_test.go index e50cd63..9672178 100644 --- a/resource/addr_test.go +++ b/resource/addr_test.go @@ -1,11 +1,11 @@ package resource import ( - "testing" - "github.com/SimonBaeumer/goss/util" "github.com/SimonBaeumer/goss/system/mock_system" + "github.com/SimonBaeumer/goss/util" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "testing" ) func TestAddr_NewAddr(t *testing.T) { diff --git a/resource/dns.go b/resource/dns.go index b9b36c4..2d44391 100644 --- a/resource/dns.go +++ b/resource/dns.go @@ -20,14 +20,16 @@ type DNS struct { } // ID returns the Host as the Identifier -func (d *DNS) ID() string { return d.Host } +func (d *DNS) ID() string { return d.Host } + // SetID sets the ID as Host func (d *DNS) SetID(id string) { d.Host = id } // GetTitle returns the title func (d *DNS) GetTitle() string { return d.Title } + /// GetMeta returns the meta of -func (d *DNS) GetMeta() meta { return d.Meta } +func (d *DNS) GetMeta() meta { return d.Meta } // Validate validates the given resource func (d *DNS) Validate(sys *system.System) []TestResult { diff --git a/resource/dns_test.go b/resource/dns_test.go index fe2c413..65233b4 100644 --- a/resource/dns_test.go +++ b/resource/dns_test.go @@ -1,122 +1,122 @@ package resource import ( - "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" - "github.com/SimonBaeumer/goss/util/goss_testing" - "github.com/stretchr/testify/assert" - "testing" + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "testing" ) var ( - QType = "TXT" - conf = util.Config{Timeout: 50} + QType = "TXT" + conf = util.Config{Timeout: 50} ) func TestNewDNS(t *testing.T) { - mockDns := MockSysDNS{} - dns, err := NewDNS(mockDns, conf) + mockDns := MockSysDNS{} + dns, err := NewDNS(mockDns, conf) - assert.Nil(t, err) - assert.Implements(t, new(Resource), dns) - assert.Equal(t, "TXT:google.com", dns.Host) + assert.Nil(t, err) + assert.Implements(t, new(Resource), dns) + assert.Equal(t, "TXT:google.com", dns.Host) } func TestNewDNS_WithoutQType(t *testing.T) { - QType = "" + QType = "" - mockDns := MockSysDNS{} - dns, err := NewDNS(mockDns, conf) + mockDns := MockSysDNS{} + dns, err := NewDNS(mockDns, conf) - assert.Nil(t, err) - assert.Implements(t, new(Resource), dns) - assert.Equal(t, "google.com", dns.Host) - assert.Equal(t, 50, dns.Timeout) - assert.True(t, dns.Resolvable.(bool)) + assert.Nil(t, err) + assert.Implements(t, new(Resource), dns) + assert.Equal(t, "google.com", dns.Host) + assert.Equal(t, 50, dns.Timeout) + assert.True(t, dns.Resolvable.(bool)) } func TestDNS_Validate(t *testing.T) { - addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) + addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) - mockDns := MockSysDNS{} - dns, _ := NewDNS(mockDns, conf) - dns.Resolvable = true - dns.Host = "localhost" - dns.Addrs = addrs - dns.Timeout = 0 + mockDns := MockSysDNS{} + dns, _ := NewDNS(mockDns, conf) + dns.Resolvable = true + dns.Host = "localhost" + dns.Addrs = addrs + dns.Timeout = 0 - sys := system.System{} - sys.NewDNS = func (host string, sys *system.System, config util.Config) system.DNS { - return &MockSysDNS{Addr: []string{"localhost:53"}} - } + sys := system.System{} + sys.NewDNS = func(host string, sys *system.System, config util.Config) system.DNS { + return &MockSysDNS{Addr: []string{"localhost:53"}} + } - r := dns.Validate(&sys) + r := dns.Validate(&sys) - assert.Equal(t, 500, dns.Timeout, "Could not set default timeout if 0 was given") - assert.Len(t, r, 2) + assert.Equal(t, 500, dns.Timeout, "Could not set default timeout if 0 was given") + assert.Len(t, r, 2) - assert.True(t, r[0].Successful) - assert.Equal(t, "resolvable", r[0].Property) + assert.True(t, r[0].Successful) + assert.Equal(t, "resolvable", r[0].Property) - assert.True(t, r[1].Successful) - assert.Equal(t, "addrs", r[1].Property) + assert.True(t, r[1].Successful) + assert.Equal(t, "addrs", r[1].Property) } func TestDNS_ValidateFail(t *testing.T) { - addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) + addrs := goss_testing.ConvertStringSliceToInterfaceSlice([]string{"localhost:53"}) - mockDns := MockSysDNS{} - dns, _ := NewDNS(mockDns, conf) - dns.Timeout = 50 - dns.Resolvable = true - dns.Host = "localhost" - dns.Addrs = addrs + mockDns := MockSysDNS{} + dns, _ := NewDNS(mockDns, conf) + dns.Timeout = 50 + dns.Resolvable = true + dns.Host = "localhost" + dns.Addrs = addrs - sys := system.System{} - sys.NewDNS = func(host string, sys *system.System, config util.Config) system.DNS { - return &MockSysDNS{Addr: []string{"ns.localhost"}} - } + sys := system.System{} + sys.NewDNS = func(host string, sys *system.System, config util.Config) system.DNS { + return &MockSysDNS{Addr: []string{"ns.localhost"}} + } - r := dns.Validate(&sys) + r := dns.Validate(&sys) - assert.Len(t, r, 2) - assert.True(t, r[0].Successful) - assert.Equal(t, "resolvable", r[0].Property) + assert.Len(t, r, 2) + assert.True(t, r[0].Successful) + assert.Equal(t, "resolvable", r[0].Property) - assert.False(t, r[1].Successful) - assert.Equal(t, "addrs", r[1].Property) - expectedHuman := `Expected + assert.False(t, r[1].Successful) + assert.Equal(t, "addrs", r[1].Property) + expectedHuman := `Expected <[]string | len:1, cap:1>: ["ns.localhost"] to contain element matching : localhost:53` - assert.Equal(t, expectedHuman, r[1].Human) + assert.Equal(t, expectedHuman, r[1].Human) } //MockSysDNS mocks the DNS system interface type MockSysDNS struct { - Addr []string + Addr []string } func (dns MockSysDNS) Addrs() ([]string, error) { - return dns.Addr, nil + return dns.Addr, nil } func (dns MockSysDNS) Resolvable() (bool, error) { - return true, nil + return true, nil } func (dns MockSysDNS) Exists() (bool, error) { - panic("implement me") + panic("implement me") } func (dns MockSysDNS) Server() string { - return "8.8.8.8" + return "8.8.8.8" } func (dns MockSysDNS) Qtype() string { - return QType + return QType } func (dns MockSysDNS) Host() string { - return "google.com" -} \ No newline at end of file + return "google.com" +} diff --git a/resource/http.go b/resource/http.go index 5338d5c..c240dae 100644 --- a/resource/http.go +++ b/resource/http.go @@ -3,30 +3,30 @@ package resource import ( "crypto/tls" "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util" "log" "reflect" - "strings" - "time" + "strings" + "time" ) const TimeoutMS = 5000 type HTTP struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - HTTP string `json:"-" yaml:"-"` - Status matcher `json:"status" yaml:"status"` - AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` - NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` - Timeout int `json:"timeout" yaml:"timeout"` - Body []string `json:"body" yaml:"body"` - Username string `json:"username,omitempty" yaml:"username,omitempty"` - Password string `json:"password,omitempty" yaml:"password,omitempty"` - Headers map[string][]string `json:"headers,omitempty" yaml:"headers,omitempty"` - RequestHeaders map[string][]string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` - Cert string `json:"cert,omitempty" yaml:"cert,omitempty"` - Key string `json:"key,omitempty" yaml:"key,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + HTTP string `json:"-" yaml:"-"` + Status matcher `json:"status" yaml:"status"` + AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` + NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` + Timeout int `json:"timeout" yaml:"timeout"` + Body []string `json:"body" yaml:"body"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` + Headers map[string][]string `json:"headers,omitempty" yaml:"headers,omitempty"` + RequestHeaders map[string][]string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` + Cert string `json:"cert,omitempty" yaml:"cert,omitempty"` + Key string `json:"key,omitempty" yaml:"key,omitempty"` } func (u *HTTP) ID() string { return u.HTTP } @@ -75,9 +75,9 @@ func (u *HTTP) Validate(sys *system.System) []TestResult { } func (u *HTTP) loadClientCertificate() tls.Certificate { - if u.Cert == "" || u.Key == "" { - return tls.Certificate{} - } + if u.Cert == "" || u.Key == "" { + return tls.Certificate{} + } cert, err := tls.LoadX509KeyPair(u.Cert, u.Key) if err != nil { @@ -101,14 +101,13 @@ func NewHTTP(sysHTTP system.HTTP, config util.Config) (*HTTP, error) { AllowInsecure: config.AllowInsecure, NoFollowRedirects: config.NoFollowRedirects, Timeout: config.Timeout, - Username: config.Username, + Username: config.Username, Password: config.Password, - Headers: headers, + Headers: headers, } return u, err } - func validateHeader(res ResourceRead, property string, expectedHeaders map[string][]string, actualHeaders system.Header, skip bool) TestResult { id := res.ID() title := res.GetTitle() diff --git a/resource/http_test.go b/resource/http_test.go index 293abf4..e10acec 100644 --- a/resource/http_test.go +++ b/resource/http_test.go @@ -137,7 +137,7 @@ func Test_isNotInSlice(t *testing.T) { } func Test_ParseYAML(t *testing.T) { - configString := []byte(` + configString := []byte(` status: 200 allow-insecure: true no-follow-redirects: true diff --git a/resource/package.go b/resource/package.go index c10386a..51d5eb7 100644 --- a/resource/package.go +++ b/resource/package.go @@ -6,12 +6,12 @@ import ( ) type Package struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Name string `json:"-" yaml:"-"` - Installed matcher `json:"installed" yaml:"installed"` - Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` - PackageManager string `json:"package-manager,omitempty" yaml:"package-manager,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Name string `json:"-" yaml:"-"` + Installed matcher `json:"installed" yaml:"installed"` + Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` + PackageManager string `json:"package-manager,omitempty" yaml:"package-manager,omitempty"` } func (p *Package) ID() string { return p.Name } diff --git a/resource/package_test.go b/resource/package_test.go index a4fa82d..e303d52 100644 --- a/resource/package_test.go +++ b/resource/package_test.go @@ -1,44 +1,43 @@ package resource import ( - "github.com/SimonBaeumer/goss/system" - "github.com/SimonBaeumer/goss/util" - "github.com/SimonBaeumer/goss/util/goss_testing" - "github.com/stretchr/testify/assert" - "testing" + "github.com/SimonBaeumer/goss/system" + "github.com/SimonBaeumer/goss/util" + "github.com/SimonBaeumer/goss/util/goss_testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestNewPackage(t *testing.T) { - pkg, _ := NewPackage(TestPackage{}, util.Config{}) + pkg, _ := NewPackage(TestPackage{}, util.Config{}) - assert.Equal(t, "test-pkg-manager", pkg.Name) - assert.True(t, pkg.Installed.(bool)) + assert.Equal(t, "test-pkg-manager", pkg.Name) + assert.True(t, pkg.Installed.(bool)) } func TestPackage_Validate(t *testing.T) { - p := Package{ - Title: "vim", - Name: "vim", - Installed: false, - Versions: goss_testing.ConvertStringSliceToInterfaceSlice([]string{"1.0.0"}), - PackageManager: "deb", - } + p := Package{ + Title: "vim", + Name: "vim", + Installed: false, + Versions: goss_testing.ConvertStringSliceToInterfaceSlice([]string{"1.0.0"}), + PackageManager: "deb", + } - sys := &system.System{NewPackage: func(name string, pkg string) system.Package { - return TestPackage{} - }} + sys := &system.System{NewPackage: func(name string, pkg string) system.Package { + return TestPackage{} + }} - r := p.Validate(sys) + r := p.Validate(sys) - assert.False(t, r[0].Successful) - assert.Equal(t, "installed", r[0].Property) + assert.False(t, r[0].Successful) + assert.Equal(t, "installed", r[0].Property) - assert.True(t, r[1].Successful) - assert.Equal(t, "version", r[1].Property) + assert.True(t, r[1].Successful) + assert.Equal(t, "version", r[1].Property) } - -type TestPackage struct {} +type TestPackage struct{} func (p TestPackage) Name() string { return "test-pkg-manager" } @@ -46,4 +45,4 @@ func (p TestPackage) Exists() (bool, error) { return true, nil } func (p TestPackage) Installed() (bool, error) { return true, nil } -func (p TestPackage) Versions() ([]string, error) { return []string{"1.0.0"}, nil } \ No newline at end of file +func (p TestPackage) Versions() ([]string, error) { return []string{"1.0.0"}, nil } diff --git a/resource/resource_list.go b/resource/resource_list.go index aeecd83..6009abd 100644 --- a/resource/resource_list.go +++ b/resource/resource_list.go @@ -18,6 +18,7 @@ import ( //go:generate goimports -w resource_list.go resource_list.go type AddrMap map[string]*Addr + var BlacklistedAutoAddHeaders = [...]string{"Set-Cookie", "set-cookie", "Date", "date"} func (r AddrMap) AppendSysResource(sr string, sys *system.System, config util.Config) (*Addr, error) { diff --git a/resource/resource_list_test.go b/resource/resource_list_test.go index 1b3db4a..e89706c 100644 --- a/resource/resource_list_test.go +++ b/resource/resource_list_test.go @@ -9,7 +9,6 @@ import ( const SuccessStatusCode = 200 - func TestAddrMap_AppendSysResource(t *testing.T) { conf := util.Config{} systemMock := &system.System{ diff --git a/resource/validate_test.go b/resource/validate_test.go index 8ab775d..efa348f 100644 --- a/resource/validate_test.go +++ b/resource/validate_test.go @@ -1,10 +1,10 @@ package resource import ( - "fmt" - "io" - "strings" - "testing" + "fmt" + "io" + "strings" + "testing" ) type FakeResource struct { diff --git a/serve_test.go b/serve_test.go index c191389..0a81247 100644 --- a/serve_test.go +++ b/serve_test.go @@ -1,50 +1,50 @@ package goss import ( - "github.com/SimonBaeumer/goss/outputs" - "github.com/SimonBaeumer/goss/resource" - "github.com/patrickmn/go-cache" - "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" - "sync" - "testing" - "time" + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" ) func TestHealthHandler_Serve(t *testing.T) { - req, err := http.NewRequest("GET", "/healthz", nil) - if err != nil { - t.Fatal(err) - } + req, err := http.NewRequest("GET", "/healthz", nil) + if err != nil { + t.Fatal(err) + } - cmdResource := &resource.Command{ - Command: "echo hello", - Title: "echo hello", - ExitStatus: 0, - } + cmdResource := &resource.Command{ + Command: "echo hello", + Title: "echo hello", + ExitStatus: 0, + } - h := HealthHandler{ - Cache: cache.New(time.Duration(50), time.Duration(50)), - Outputer: outputs.GetOutputer("documentation"), - MaxConcurrent: 1, - ListenAddr: "9999", - ContentType: "application/json", - GossMu: &sync.Mutex{}, - GossConfig: GossConfig{ - Commands: resource.CommandMap{"echo hello": cmdResource}, - }, - } - rr := httptest.NewRecorder() + h := HealthHandler{ + Cache: cache.New(time.Duration(50), time.Duration(50)), + Outputer: outputs.GetOutputer("documentation"), + MaxConcurrent: 1, + ListenAddr: "9999", + ContentType: "application/json", + GossMu: &sync.Mutex{}, + GossConfig: GossConfig{ + Commands: resource.CommandMap{"echo hello": cmdResource}, + }, + } + rr := httptest.NewRecorder() - h.ServeHTTP(rr, req) + h.ServeHTTP(rr, req) - if status := rr.Code; status != http.StatusOK { - t.Errorf("Health check failed!") - } + if status := rr.Code; status != http.StatusOK { + t.Errorf("Health check failed!") + } - assert.Equal(t, http.StatusOK, rr.Code) - assert.Contains(t, rr.Body.String(), "Title: echo hello") - assert.Contains(t, rr.Body.String(), "Command: echo hello: exit-status: matches expectation: [0]") - assert.Contains(t, rr.Body.String(), "Count: 1, Failed: 0, Skipped: 0") + assert.Equal(t, http.StatusOK, rr.Code) + assert.Contains(t, rr.Body.String(), "Title: echo hello") + assert.Contains(t, rr.Body.String(), "Command: echo hello: exit-status: matches expectation: [0]") + assert.Contains(t, rr.Body.String(), "Count: 1, Failed: 0, Skipped: 0") } diff --git a/store_test.go b/store_test.go index d442146..dd9ef4c 100644 --- a/store_test.go +++ b/store_test.go @@ -1,50 +1,50 @@ package goss import ( - "github.com/stretchr/testify/assert" - "io/ioutil" - "os" - "testing" + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" ) const GossTestingEnvOS = "GOSS_TESTING_OS" func Test_RenderJSON(t *testing.T) { - err := os.Setenv(GossTestingEnvOS, "centos") - if err != nil { - panic(err.Error()) - } - defer os.Unsetenv(GossTestingEnvOS) + err := os.Setenv(GossTestingEnvOS, "centos") + if err != nil { + panic(err.Error()) + } + defer os.Unsetenv(GossTestingEnvOS) - tmpVars, err := ioutil.TempFile("", "example_tmp_vars_*.yaml") - if err != nil { - panic(err.Error()) - } - defer os.Remove(tmpVars.Name()) + tmpVars, err := ioutil.TempFile("", "example_tmp_vars_*.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpVars.Name()) - _, err = tmpVars.WriteString(getExampleVars()) - if err != nil { - panic(err.Error()) - } + _, err = tmpVars.WriteString(getExampleVars()) + if err != nil { + panic(err.Error()) + } - tmpGossfile, err := ioutil.TempFile("", "example_tmp_gossfile_*.yaml") - if err != nil { - panic(err.Error()) - } - defer os.Remove(tmpGossfile.Name()) + tmpGossfile, err := ioutil.TempFile("", "example_tmp_gossfile_*.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpGossfile.Name()) - _, err = tmpGossfile.WriteString(getExampleTemplate()) - if err != nil { - panic(err.Error()) - } + _, err = tmpGossfile.WriteString(getExampleTemplate()) + if err != nil { + panic(err.Error()) + } - result := RenderJSON(tmpGossfile, tmpVars) + result := RenderJSON(tmpGossfile, tmpVars) - assert.Equal(t, getExpecetd(), result) + assert.Equal(t, getExpecetd(), result) } func getExampleVars() string { - return ` + return ` centos: packages: kernel: @@ -62,7 +62,7 @@ func getExampleVars() string { } func getExampleTemplate() string { - return ` + return ` package: # Looping over a variables defined in a vars.yaml using $OS environment variable as a lookup key {{range $name, $vers := index .Vars .Env.GOSS_TESTING_OS "packages"}} @@ -102,7 +102,7 @@ func getExampleTemplate() string { } func getExpecetd() string { - expected := `package: + expected := `package: libselinux: installed: true user: @@ -119,5 +119,5 @@ user: home: /home/user2 shell: /bin/bash ` - return expected + return expected } diff --git a/system/dns.go b/system/dns.go index 6649d05..970479f 100644 --- a/system/dns.go +++ b/system/dns.go @@ -64,6 +64,7 @@ func (d *DefDNS) Server() string { func (d *DefDNS) Qtype() string { return d.qtype } + // setup executes the dns lookup func (d *DefDNS) setup() error { if d.loaded { diff --git a/system/dns_test.go b/system/dns_test.go index fdbd77b..8fac45c 100644 --- a/system/dns_test.go +++ b/system/dns_test.go @@ -1,76 +1,76 @@ package system import ( - "github.com/SimonBaeumer/goss/util" - "github.com/stretchr/testify/assert" - "testing" + "github.com/SimonBaeumer/goss/util" + "github.com/stretchr/testify/assert" + "testing" ) func TestNewDefDNS(t *testing.T) { - dns := NewDefDNS("google.com", &System{}, util.Config{Timeout: 50, Server: "8.8.8.8"}) + dns := NewDefDNS("google.com", &System{}, util.Config{Timeout: 50, Server: "8.8.8.8"}) - assert.Implements(t, new(DNS), dns) - assert.Equal(t, "8.8.8.8", dns.Server()) + assert.Implements(t, new(DNS), dns) + assert.Equal(t, "8.8.8.8", dns.Server()) } func TestNewDefDNS_WithQueryType(t *testing.T) { - dns := NewDefDNS("TXT:google.com", &System{}, util.Config{}) + dns := NewDefDNS("TXT:google.com", &System{}, util.Config{}) - assert.Implements(t, new(DNS), dns) - assert.Equal(t, "TXT", dns.Qtype()) - assert.Equal(t, "google.com", dns.Host()) + assert.Implements(t, new(DNS), dns) + assert.Equal(t, "TXT", dns.Qtype()) + assert.Equal(t, "google.com", dns.Host()) } func TestAddr(t *testing.T) { - dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 200}) + dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 200}) - r, err := dns.Resolvable() - assert.Nil(t, err) - assert.True(t, r) + r, err := dns.Resolvable() + assert.Nil(t, err) + assert.True(t, r) } func TestAddr_WithServer(t *testing.T) { - dns := NewDefDNS("google.com", &System{}, util.Config{Timeout: 150, Server: "8.8.8.8"}) + dns := NewDefDNS("google.com", &System{}, util.Config{Timeout: 150, Server: "8.8.8.8"}) - r, err := dns.Resolvable() - assert.Nil(t, err) - assert.True(t, r) + r, err := dns.Resolvable() + assert.Nil(t, err) + assert.True(t, r) } func TestDNSLookup(t *testing.T) { - dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 500}) + dns := NewDefDNS("localhost", &System{}, util.Config{Timeout: 500}) - r, err := dns.Resolvable() - addr, _ := dns.Addrs() + r, err := dns.Resolvable() + addr, _ := dns.Addrs() - assert.Nil(t, err) - assert.True(t, r) - assert.Equal(t, []string{"127.0.0.1"}, addr) + assert.Nil(t, err) + assert.True(t, r) + assert.Equal(t, []string{"127.0.0.1"}, addr) } func TestDefDNS_Exists(t *testing.T) { - dns := NewDefDNS("", &System{}, util.Config{}) - r, _ := dns.Exists() - assert.False(t, r) + dns := NewDefDNS("", &System{}, util.Config{}) + r, _ := dns.Exists() + assert.False(t, r) } func TestDNSLookup_ShouldFail(t *testing.T) { - dns := NewDefDNS("SRV:s-baeumer.de", &System{}, util.Config{Timeout: 500}) + dns := NewDefDNS("SRV:s-baeumer.de", &System{}, util.Config{Timeout: 500}) - r, err := dns.Resolvable() - assert.Nil(t, err) - assert.False(t, r) + r, err := dns.Resolvable() + assert.Nil(t, err) + assert.False(t, r) } func TestDNSLook_WithInvalidAddress(t *testing.T) { - dns := NewDefDNS("thisdomaindoesnotexist123.com", &System{}, util.Config{Timeout: 50}) - r, _ := dns.Resolvable() - assert.False(t, r) + dns := NewDefDNS("thisdomaindoesnotexist123.com", &System{}, util.Config{Timeout: 50}) + r, _ := dns.Resolvable() + assert.False(t, r) } func TestDNSLookupWithTimeout(t *testing.T) { - dns := NewDefDNS("SRV:s-baeumer.de", &System{}, util.Config{Timeout: 0}) - r, err := dns.Resolvable() - assert.Equal(t, "DNS lookup timed out (0s)", err.Error()) - assert.False(t, r) + dns := NewDefDNS("SRV:s-baeumer.de", &System{}, util.Config{Timeout: 0}) + r, err := dns.Resolvable() + assert.Equal(t, "DNS lookup timed out (0s)", err.Error()) + assert.False(t, r) } diff --git a/system/http.go b/system/http.go index 89edd00..3f22d3f 100644 --- a/system/http.go +++ b/system/http.go @@ -44,7 +44,7 @@ func NewDefHTTP(http string, system *System, config util.Config) HTTP { allowInsecure: config.AllowInsecure, noFollowRedirects: config.NoFollowRedirects, Timeout: config.Timeout, - Username: config.Username, + Username: config.Username, Password: config.Password, RequestHeaders: config.RequestHeaders, ClientCertificate: config.Certificate, @@ -58,12 +58,11 @@ func (u *DefHTTP) setup() error { } u.loaded = true - - tr := &http.Transport{ - TLSClientConfig: &tls.Config{ + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ InsecureSkipVerify: u.allowInsecure, - Certificates: []tls.Certificate{u.ClientCertificate}, - }, + Certificates: []tls.Certificate{u.ClientCertificate}, + }, DisableKeepAlives: true, } client := &http.Client{ @@ -154,4 +153,4 @@ func (u *DefHTTP) Headers() (Header, error) { } return headers, nil -} \ No newline at end of file +} diff --git a/system/kernel_param.go b/system/kernel_param.go index 7ff8cc3..dd9b7a5 100644 --- a/system/kernel_param.go +++ b/system/kernel_param.go @@ -1,8 +1,8 @@ package system import ( - "github.com/achanda/go-sysctl" "github.com/SimonBaeumer/goss/util" + "github.com/achanda/go-sysctl" ) type KernelParam interface { diff --git a/system/package_test.go b/system/package_test.go index 2393112..257e6c2 100644 --- a/system/package_test.go +++ b/system/package_test.go @@ -1,25 +1,25 @@ package system import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestNewPackage(t *testing.T) { - deb := NewPackage("package", "deb") - rpm := NewPackage("package", "rpm") - pac := NewPackage("package", "pacman") - apk := NewPackage("package", "apk") + deb := NewPackage("package", "deb") + rpm := NewPackage("package", "rpm") + pac := NewPackage("package", "pacman") + apk := NewPackage("package", "apk") - assert.Implements(t, new(Package), deb) - assert.IsType(t, &DebPackage{}, deb) + assert.Implements(t, new(Package), deb) + assert.IsType(t, &DebPackage{}, deb) - assert.Implements(t, new(Package), rpm) - assert.IsType(t, &RpmPackage{}, rpm) + assert.Implements(t, new(Package), rpm) + assert.IsType(t, &RpmPackage{}, rpm) - assert.Implements(t, new(Package), pac) - assert.IsType(t, &PacmanPackage{}, pac) + assert.Implements(t, new(Package), pac) + assert.IsType(t, &PacmanPackage{}, pac) - assert.Implements(t, new(Package), apk) - assert.IsType(t, &AlpinePackage{}, apk) + assert.Implements(t, new(Package), apk) + assert.IsType(t, &AlpinePackage{}, apk) } diff --git a/system/port.go b/system/port.go index 084d2e3..ce328f1 100644 --- a/system/port.go +++ b/system/port.go @@ -4,8 +4,8 @@ import ( "strconv" "strings" - "github.com/aelsabbahy/GOnetstat" "github.com/SimonBaeumer/goss/util" + "github.com/aelsabbahy/GOnetstat" ) type Port interface { diff --git a/system/process.go b/system/process.go index ac11bb8..b9f51e1 100644 --- a/system/process.go +++ b/system/process.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - "github.com/aelsabbahy/go-ps" "github.com/SimonBaeumer/goss/util" + "github.com/aelsabbahy/go-ps" ) type Process interface { diff --git a/system/system.go b/system/system.go index 2a65c67..517e2ce 100644 --- a/system/system.go +++ b/system/system.go @@ -7,8 +7,8 @@ import ( "os/exec" "sync" - "github.com/aelsabbahy/GOnetstat" util2 "github.com/SimonBaeumer/goss/util" + "github.com/aelsabbahy/GOnetstat" // This needs a better name "github.com/aelsabbahy/go-ps" ) diff --git a/template_test.go b/template_test.go index 358bf4f..9501bba 100644 --- a/template_test.go +++ b/template_test.go @@ -1,86 +1,86 @@ package goss import ( - "github.com/stretchr/testify/assert" - "io/ioutil" - "os" - "testing" + "github.com/stretchr/testify/assert" + "io/ioutil" + "os" + "testing" ) func Test_NewTemplateFilter_Variable(t *testing.T) { - vars, err := ioutil.TempFile("", "*_vars.yaml") - if err != nil { - panic(err.Error()) - } - defer os.Remove(vars.Name()) + vars, err := ioutil.TempFile("", "*_vars.yaml") + if err != nil { + panic(err.Error()) + } + defer os.Remove(vars.Name()) - _, err = vars.WriteString("test: testing") - if err != nil { - panic(err.Error()) - } + _, err = vars.WriteString("test: testing") + if err != nil { + panic(err.Error()) + } - content := []byte(`variable: {{.Vars.test}}`) + content := []byte(`variable: {{.Vars.test}}`) - filter := NewTemplateFilter(vars.Name()) - result := filter(content) + filter := NewTemplateFilter(vars.Name()) + result := filter(content) - assert.Equal(t, "variable: testing", string(result)) + assert.Equal(t, "variable: testing", string(result)) } func Test_NewTemplateFilter_Env(t *testing.T) { - err := os.Setenv("GOSS_TEST_ENV", "env testing") - if err != nil{ - panic(err.Error()) - } - defer os.Unsetenv("template_goss_test_env") + err := os.Setenv("GOSS_TEST_ENV", "env testing") + if err != nil { + panic(err.Error()) + } + defer os.Unsetenv("template_goss_test_env") - content := []byte(`environment: {{.Env.GOSS_TEST_ENV}}`) + content := []byte(`environment: {{.Env.GOSS_TEST_ENV}}`) - filter := NewTemplateFilter("") - result := filter(content) + filter := NewTemplateFilter("") + result := filter(content) - assert.Equal(t, "environment: env testing", string(result)) + assert.Equal(t, "environment: env testing", string(result)) } func Test_NewTemplateFilter_mkSlice(t *testing.T) { - content := []byte(`{{- range mkSlice "test1" "test2" "test3"}}{{.}}{{end}}`) + content := []byte(`{{- range mkSlice "test1" "test2" "test3"}}{{.}}{{end}}`) - filter := NewTemplateFilter("") - result := filter(content) + filter := NewTemplateFilter("") + result := filter(content) - assert.Equal(t, "test1test2test3", string(result)) + assert.Equal(t, "test1test2test3", string(result)) } func Test_NewTemplateFilter_readFile(t *testing.T) { - tmpFile, err := ioutil.TempFile("", "read_file_temp") - if err != nil { - panic(err.Error()) - } - defer os.Remove(tmpFile.Name()) - tmpFile.WriteString("test read file from template") + tmpFile, err := ioutil.TempFile("", "read_file_temp") + if err != nil { + panic(err.Error()) + } + defer os.Remove(tmpFile.Name()) + tmpFile.WriteString("test read file from template") - content := []byte(`{{readFile "` + tmpFile.Name() + `"}}`) + content := []byte(`{{readFile "` + tmpFile.Name() + `"}}`) - filter := NewTemplateFilter("") - result := filter(content) + filter := NewTemplateFilter("") + result := filter(content) - assert.Equal(t, "test read file from template", string(result)) + assert.Equal(t, "test read file from template", string(result)) } func Test_NewTemplateFilter_regexMatch(t *testing.T) { - content := []byte(`{{if "centos" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{end}}`) + content := []byte(`{{if "centos" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{end}}`) - filter := NewTemplateFilter("") - result := filter(content) + filter := NewTemplateFilter("") + result := filter(content) - assert.Equal(t, "detected regex", string(result)) + assert.Equal(t, "detected regex", string(result)) } func Test_NewTemplateFilter_regexMatch_fail(t *testing.T) { - content := []byte(`{{if "ubuntu" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{else}}no match{{end}}`) + content := []byte(`{{if "ubuntu" | regexMatch "[Cc]ent(OS|os)"}}detected regex{{else}}no match{{end}}`) - filter := NewTemplateFilter("") - result := filter(content) + filter := NewTemplateFilter("") + result := filter(content) - assert.Equal(t, "no match", string(result)) -} \ No newline at end of file + assert.Equal(t, "no match", string(result)) +} diff --git a/util/command_test.go b/util/command_test.go index 9c22dcd..238d408 100644 --- a/util/command_test.go +++ b/util/command_test.go @@ -1,22 +1,22 @@ package util import ( - "github.com/stretchr/testify/assert" - "testing" + "github.com/stretchr/testify/assert" + "testing" ) func TestNewCommand(t *testing.T) { - cmd := NewCommand("/bin/sh") - assert.Equal(t, "/bin/sh", cmd.name) - assert.Equal(t, "", cmd.Stdout.String()) + cmd := NewCommand("/bin/sh") + assert.Equal(t, "/bin/sh", cmd.name) + assert.Equal(t, "", cmd.Stdout.String()) } func TestCommand_Run(t *testing.T) { - cmd := NewCommand("/bin/sh", "-c", "echo test") - err := cmd.Run() + cmd := NewCommand("/bin/sh", "-c", "echo test") + err := cmd.Run() - assert.Nil(t, err) - assert.Equal(t, "test\n", cmd.Stdout.String()) - assert.Equal(t, "", cmd.Stderr.String()) - assert.Equal(t, 0, cmd.Status) -} \ No newline at end of file + assert.Nil(t, err) + assert.Equal(t, "test\n", cmd.Stdout.String()) + assert.Equal(t, "", cmd.Stderr.String()) + assert.Equal(t, 0, cmd.Status) +} diff --git a/util/config.go b/util/config.go index 2c7b763..cc8601b 100644 --- a/util/config.go +++ b/util/config.go @@ -24,7 +24,6 @@ type Config struct { } type Request struct { - } type OutputConfig struct { diff --git a/util/goss_testing/helper.go b/util/goss_testing/helper.go index 94f8b0b..20d2285 100644 --- a/util/goss_testing/helper.go +++ b/util/goss_testing/helper.go @@ -3,9 +3,9 @@ package goss_testing //ConvertStringSliceToInterfaceSlice is a helper function to match // system interfaces func ConvertStringSliceToInterfaceSlice(strings []string) []interface{} { - var iStrings = make([]interface{}, len(strings)) - for i, char := range strings { - iStrings[i] = char - } - return iStrings -} \ No newline at end of file + var iStrings = make([]interface{}, len(strings)) + for i, char := range strings { + iStrings[i] = char + } + return iStrings +} diff --git a/validate.go b/validate.go index 29c1d0c..b92d088 100644 --- a/validate.go +++ b/validate.go @@ -2,8 +2,8 @@ package goss import ( "fmt" - "io" - "os" + "io" + "os" "runtime" "sync" "time" @@ -21,15 +21,15 @@ type Validator struct { Sleep time.Duration FormatOptions []string Outputer outputs.Outputer - MaxConcurrent int //Separating concurrency and validation, irritating atm... + MaxConcurrent int //Separating concurrency and validation, irritating atm... OutputWriter io.Writer } // Validate validation runtime func (v *Validator) Validate(startTime time.Time) int { - if v.OutputWriter == nil { - v.OutputWriter = os.Stdout - } + if v.OutputWriter == nil { + v.OutputWriter = os.Stdout + } outputConfig := util.OutputConfig{ FormatOptions: v.FormatOptions, @@ -48,7 +48,7 @@ func (v *Validator) Validate(startTime time.Time) int { } elapsed := time.Since(startTime) - if elapsed + v.Sleep > v.RetryTimeout { + if elapsed+v.Sleep > v.RetryTimeout { color.Red("\nERROR: Timeout of %s reached before tests entered a passing state", v.RetryTimeout) return exitCode } @@ -63,7 +63,7 @@ func (v *Validator) Validate(startTime time.Time) int { } func validate(sys *system.System, gossConfig GossConfig, maxConcurrent int) <-chan []resource.TestResult { - out := make(chan []resource.TestResult) + out := make(chan []resource.TestResult) in := make(chan resource.Resource) // Send resources to input channel diff --git a/validate_test.go b/validate_test.go index 62451db..45137c8 100644 --- a/validate_test.go +++ b/validate_test.go @@ -1,41 +1,41 @@ package goss import ( - "bytes" - "github.com/SimonBaeumer/goss/outputs" - "github.com/SimonBaeumer/goss/resource" - "github.com/stretchr/testify/assert" - "testing" - "time" + "bytes" + "github.com/SimonBaeumer/goss/outputs" + "github.com/SimonBaeumer/goss/resource" + "github.com/stretchr/testify/assert" + "testing" + "time" ) func TestValidator_Validate(t *testing.T) { - cmdRes := &resource.Command{Title: "echo hello", Command: "echo hello", ExitStatus: 0} - fileRes := &resource.File{Title: "/tmp", Path: "/tmp", Filetype: "directory", Exists: true} - addrRes := &resource.Addr{Title: "tcp://google.com:443", Address: "tcp://google.com:443", Reachable: true} - httpRes := &resource.HTTP{Title: "https://google.com", HTTP: "https://google.com", Status: 200} - userRes := &resource.User{Title: "root", Username: "root", Exists: true} - groupRes := &resource.Group{Title: "root", Groupname: "root", Exists: true} - dnsRes := &resource.DNS{Title: "A:google.com", Host: "A:google.com", Resolvable: true} + cmdRes := &resource.Command{Title: "echo hello", Command: "echo hello", ExitStatus: 0} + fileRes := &resource.File{Title: "/tmp", Path: "/tmp", Filetype: "directory", Exists: true} + addrRes := &resource.Addr{Title: "tcp://google.com:443", Address: "tcp://google.com:443", Reachable: true} + httpRes := &resource.HTTP{Title: "https://google.com", HTTP: "https://google.com", Status: 200} + userRes := &resource.User{Title: "root", Username: "root", Exists: true} + groupRes := &resource.Group{Title: "root", Groupname: "root", Exists: true} + dnsRes := &resource.DNS{Title: "A:google.com", Host: "A:google.com", Resolvable: true} - w := &bytes.Buffer{} - v := Validator{ - GossConfig: GossConfig{ - Commands: resource.CommandMap{"echo hello": cmdRes}, - Files: resource.FileMap{"/tmp": fileRes}, - Addrs: resource.AddrMap{"127.0.0.1": addrRes}, - HTTPs: resource.HTTPMap{"https://google.com": httpRes}, - Users: resource.UserMap{"root": userRes}, - Groups: resource.GroupMap{"root": groupRes}, - DNS: resource.DNSMap{"A:https://google.com": dnsRes}, - }, - MaxConcurrent: 1, - Outputer: outputs.GetOutputer("documentation"), - OutputWriter: w, - } + w := &bytes.Buffer{} + v := Validator{ + GossConfig: GossConfig{ + Commands: resource.CommandMap{"echo hello": cmdRes}, + Files: resource.FileMap{"/tmp": fileRes}, + Addrs: resource.AddrMap{"127.0.0.1": addrRes}, + HTTPs: resource.HTTPMap{"https://google.com": httpRes}, + Users: resource.UserMap{"root": userRes}, + Groups: resource.GroupMap{"root": groupRes}, + DNS: resource.DNSMap{"A:https://google.com": dnsRes}, + }, + MaxConcurrent: 1, + Outputer: outputs.GetOutputer("documentation"), + OutputWriter: w, + } - r := v.Validate(time.Now()) + r := v.Validate(time.Now()) - assert.Equal(t, 0, r) - assert.Contains(t, w.String(), "Count: 8, Failed: 0, Skipped: 0") -} \ No newline at end of file + assert.Equal(t, 0, r) + assert.Contains(t, w.String(), "Count: 8, Failed: 0, Skipped: 0") +} From da201429f4afdf74a203b50ed984669828dc8c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4umer?= Date: Mon, 8 Jul 2019 16:51:32 +0200 Subject: [PATCH 19/19] Add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 498057e..8ba5034 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Fixed a bug where DNS record lookups never were fired wihout a nameserver - Removed global `--package` option, added `package-manager` property to `package` resource + - Removed code dependency on `*cli.Context` # v0.5.0